opensecret 0.0.9925 → 0.0.9949
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +656 -40
- data/lib/configs/README.md +58 -0
- data/lib/extension/file.rb +67 -0
- data/lib/extension/string.rb +10 -0
- data/lib/factbase/facts.opensecret.io.ini +1 -0
- data/lib/interprete.rb +334 -61
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +9 -15
- data/lib/keytools/kdf.bcrypt.rb +69 -19
- data/lib/keytools/kdf.pbkdf2.rb +112 -23
- data/lib/keytools/key.api.rb +146 -36
- data/lib/keytools/key.db.rb +94 -29
- data/lib/keytools/key.id.rb +1 -1
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.local.rb +62 -68
- data/lib/keytools/key.pass.rb +2 -2
- data/lib/keytools/key.rb +2 -28
- data/lib/modules/{cryptology.md → README.md} +0 -0
- data/lib/session/fact.finder.rb +65 -428
- data/lib/session/time.stamp.rb +1 -28
- data/lib/usecase/cmd.rb +127 -54
- data/lib/usecase/config/README.md +57 -0
- data/lib/usecase/docker/README.md +146 -0
- data/lib/usecase/docker/docker.rb +49 -0
- data/lib/usecase/edit/README.md +43 -0
- data/lib/usecase/edit/delete.rb +46 -0
- data/lib/usecase/export.rb +40 -0
- data/lib/usecase/files/README.md +37 -0
- data/lib/usecase/files/eject.rb +56 -0
- data/lib/usecase/files/file_me.rb +78 -0
- data/lib/usecase/files/read.rb +169 -0
- data/lib/usecase/files/write.rb +89 -0
- data/lib/usecase/goto.rb +57 -0
- data/lib/usecase/id.rb +1 -1
- data/lib/usecase/import.rb +13 -30
- data/lib/usecase/init.rb +2 -17
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/crazy_ruby_post_attempt.OLD +234 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +6 -5
- data/lib/usecase/logout.rb +1 -3
- data/lib/usecase/open.rb +11 -66
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +34 -156
- data/lib/usecase/set.rb +2 -4
- data/lib/usecase/show.rb +138 -0
- data/lib/usecase/terraform/README.md +91 -0
- data/lib/usecase/terraform/terraform.rb +121 -0
- data/lib/usecase/token.rb +4 -80
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +1 -3
- data/lib/usecase/verse.rb +20 -0
- data/lib/usecase/view.rb +71 -0
- data/lib/usecase/vpn/README.md +150 -0
- data/lib/usecase/vpn/vpn.ini +31 -0
- data/lib/usecase/vpn/vpn.rb +54 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +3 -4
- metadata +34 -35
- data/.travis.yml +0 -5
- data/CODE_OF_CONDUCT.md +0 -74
- data/LICENSE.txt +0 -21
- data/bin/ops +0 -20
- data/lib/keytools/binary.map.rb +0 -294
- data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +0 -179
- data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +0 -190
- data/lib/keytools/doc.star.schema.strategy.txt +0 -77
- data/lib/keytools/doc.using.pbkdf2.kdf.ruby +0 -95
- data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +0 -266
- data/lib/keytools/key.mach.rb +0 -248
- data/lib/keytools/keydebug.txt +0 -295
- data/lib/modules/cryptology/open.bcrypt.rb +0 -170
- data/lib/usecase/read.rb +0 -89
- data/lib/usecase/safe.rb +0 -92
@@ -1,190 +0,0 @@
|
|
1
|
-
|
2
|
-
For Starters....
|
3
|
-
I am often torn when programming between using libraries and following the DIY approach. On one hand you can accomplish a lot in a short time by including libraries that have the functionality you need and just bridging the gaps, but I am often left with the feeling of some loss-of-control and bloat. Obviously some library usage is inevitable, but I do like to take a minimalist approach to including external libraries because the management burden of updating libraries, the effort to make various libraries play together and because moving library-heavy applications between platforms can be... interesting. Most of all I like the learning that takes place when building it yourself. It was for this reason that I decided to see if I could effectively utilize the OpenSSL library that is typically part of a Ruby installation to create my own higher-level encryption/decryption class. Before we dive in I would like to layout the details of the application.
|
4
|
-
|
5
|
-
Password Management App
|
6
|
-
|
7
|
-
I have for years maintained a PGP encrypted file for my account passwords. I had the desire to centralize this and make it more web accessible. In order to do this I wanted assurance that the data was not only encrypted on the wire via SSL, but also at rest. I looked at various Ruby PGP/GPG libraries, but all of the ones I surveyed were wrappers around the GPG binary itself which didn't make it very web host friendly ( I planned on hosting it on Heroku with a Couchdb backend on Cloudant). That was when I decided to look at OpenSSL for file encryption since it is typically part of a Ruby installation and was available on Heroku. I also had the need for public-key encryption so a password store could be shared between two or more people (I share some accounts with my partner).
|
8
|
-
|
9
|
-
Take One... RSA
|
10
|
-
I thought I could simply use RSA encryption to meet all my needs and at first it worked quite well. Creating keys was straight forward and encryption/decryption worked well. One caveat is that the string passed to the FileEncryptor#encrypt_string method cannot be larger than the key size + PKCS padding size (11 bytes). So for a 128 byte (1024 bit) key the string should only be 117 bytes (see String#size).
|
11
|
-
|
12
|
-
|
13
|
-
require 'openssl'
|
14
|
-
|
15
|
-
class Encryptor
|
16
|
-
|
17
|
-
def initialize(key_pass, key_size = 1024, key_name = 'rsakey.sec', cipher = OpenSSL::Cipher.new('aes-256-cbc'))
|
18
|
-
@key_size = key_size
|
19
|
-
@cipher = cipher
|
20
|
-
@key_name = key_name
|
21
|
-
|
22
|
-
if( File.exists?(@key_name) )
|
23
|
-
@rsakey = OpenSSL::PKey::RSA.new(File.read(@key_name), key_pass)
|
24
|
-
else
|
25
|
-
@rsakey = OpenSSL::PKey::RSA.generate(@key_size)
|
26
|
-
File.open(@key_name,'w+') do |priv|
|
27
|
-
priv.write(@rsakey.to_pem(@cipher, key_pass))
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def encrypt_string(txt)
|
33
|
-
begin
|
34
|
-
@rsakey.public_encrypt(txt)
|
35
|
-
rescue OpenSSL::PKey::RSAError => e
|
36
|
-
if e.message == 'data too large for key size'
|
37
|
-
STDERR.puts 'Your string is too large to encrypt'
|
38
|
-
else
|
39
|
-
raise
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def decrypt_string(etxt)
|
45
|
-
@rsakey.private_decrypt(etxt)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
## -- See YAML storage of key (excellent).
|
53
|
-
## -- See YAML storage of key (excellent).
|
54
|
-
## -- See YAML storage of key (excellent).
|
55
|
-
## -- See YAML storage of key (excellent).
|
56
|
-
## -- See YAML storage of key (excellent).
|
57
|
-
## -- See YAML storage of key (excellent).
|
58
|
-
## -- See YAML storage of key (excellent).
|
59
|
-
## -- See YAML storage of key (excellent).
|
60
|
-
## -- See YAML storage of key (excellent).
|
61
|
-
## -- See YAML storage of key (excellent).
|
62
|
-
## -- See YAML storage of key (excellent).
|
63
|
-
## -- See YAML storage of key (excellent).
|
64
|
-
## -- See YAML storage of key (excellent).
|
65
|
-
## -- See YAML storage of key (excellent).
|
66
|
-
## -- See YAML storage of key (excellent).
|
67
|
-
## -- See YAML storage of key (excellent).
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
This seemed to work quite well until I remembered that I needed to have the encrypted data be readable by multiple parties. I then had to change my approach and decided to use a symmetric AES key that is then shared between the multiple parties and each person encrypts the key with their RSA public key.
|
72
|
-
|
73
|
-
Take Two... RSA + AES
|
74
|
-
In order to accomplish secure use of the AES key we start out the same way as the previous solution by creating a RSA key. It will only be used to secure AES keys that we use to protect certain data stores. So if I have a data store I want to share with my brother I encrypt the data store with an AES key then I encrypt that key with my public RSA key and my brother's public RSA key. Now both he and I can access anything in that store and the AES key is still secure from prying eyes. Here is an overview of the steps taken before I post the code.
|
75
|
-
Create a RSA key-pair
|
76
|
-
Create an AES key to encrypt the data store
|
77
|
-
Encrypt the AES key with your RSA public key
|
78
|
-
To give access to the data store encrypt the AES key with the person's public RSA key send them back the cipher-text.
|
79
|
-
|
80
|
-
And here is the class I wrote to make this work:
|
81
|
-
|
82
|
-
|
83
|
-
require 'openssl'
|
84
|
-
require 'yaml'
|
85
|
-
|
86
|
-
class Encryptor
|
87
|
-
|
88
|
-
def initialize(key_pass, key_size = 2048, key_name = 'rsakey.sec', cipher = OpenSSL::Cipher.new('aes-256-cbc'))
|
89
|
-
@key_size = key_size
|
90
|
-
@cipher = cipher
|
91
|
-
@key_name = key_name
|
92
|
-
|
93
|
-
if( File.exists?(@key_name) )
|
94
|
-
@rsakey = OpenSSL::PKey::RSA.new(File.read(@key_name), key_pass)
|
95
|
-
else
|
96
|
-
@rsakey = OpenSSL::PKey::RSA.generate(@key_size)
|
97
|
-
File.open(@key_name,'w+') do |priv|
|
98
|
-
priv.write(@rsakey.to_pem(@cipher, key_pass))
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def rsa_encrypt(txt, pub_key=nil)
|
104
|
-
begin
|
105
|
-
pub_key.nil? ? @rsakey.public_encrypt(txt) : OpenSSL::PKey::RSA.new(pub_key).public_encrypt(txt)
|
106
|
-
rescue OpenSSL::PKey::RSAError => e
|
107
|
-
if e.message == 'data too large for key size'
|
108
|
-
STDERR.puts 'Your string is too large to encrypt'
|
109
|
-
else
|
110
|
-
raise
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def rsa_decrypt(etxt)
|
116
|
-
@rsakey.private_decrypt(etxt)
|
117
|
-
end
|
118
|
-
|
119
|
-
def aes_encrypt(txt, aes_file)
|
120
|
-
key = get_aes_key(aes_file)
|
121
|
-
cif = OpenSSL::Cipher.new('AES-256-CBC')
|
122
|
-
cif.encrypt
|
123
|
-
cif.key = key[:key]
|
124
|
-
cif.iv = key[:iv] = cif.random_iv
|
125
|
-
save_aes_key(aes_file, key)
|
126
|
-
etxt = ''
|
127
|
-
etxt << cif.update(txt)
|
128
|
-
etxt << cif.final
|
129
|
-
etxt
|
130
|
-
end
|
131
|
-
|
132
|
-
def aes_decrypt(etxt, aes_file)
|
133
|
-
key = get_aes_key(aes_file)
|
134
|
-
cif = OpenSSL::Cipher.new('AES-256-CBC')
|
135
|
-
cif.decrypt
|
136
|
-
cif.key = key[:key]
|
137
|
-
cif.iv = key[:iv]
|
138
|
-
txt = ''
|
139
|
-
txt << cif.update(etxt)
|
140
|
-
txt << cif.final
|
141
|
-
txt
|
142
|
-
end
|
143
|
-
|
144
|
-
def gen_aes_key(file)
|
145
|
-
cif = OpenSSL::Cipher.new('AES-256-CBC')
|
146
|
-
key = {key: cif.random_key}
|
147
|
-
save_aes_key(file, key)
|
148
|
-
key
|
149
|
-
end
|
150
|
-
|
151
|
-
def save_aes_key(file,key_iv)
|
152
|
-
File.open(file,'w+') do |f|
|
153
|
-
f.write(rsa_encrypt(YAML.dump(key_iv)))
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def get_aes_key(file)
|
158
|
-
YAML.load(rsa_decrypt(File.read(file)))
|
159
|
-
end
|
160
|
-
|
161
|
-
def give_aes_key(aes_file, rsa_pub)
|
162
|
-
aes = get_aes_key(aes_file)
|
163
|
-
rsa_encrypt(YAML.dump(aes), rsa_pub)
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
Below is a demonstration of the use of this class to accomplish the needs I had for my password protector application. I am aware that this class needs some refactoring and it's a bit annoying to have to pass the AES key file to the methods, but logically it is working the way I had hoped.
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
fe = Encryptor.new('mysecret')
|
175
|
-
|
176
|
-
aesfile = 'aeskey.sec'
|
177
|
-
|
178
|
-
fe.gen_aes_key(aesfile)
|
179
|
-
|
180
|
-
etxt = fe.aes_encrypt('this is a test', aesfile)
|
181
|
-
|
182
|
-
txt = fe.aes_decrypt(etxt, aesfile)
|
183
|
-
|
184
|
-
pub_key = < Assume I got someone's public RSA Key somehow >
|
185
|
-
|
186
|
-
fe.give_aes_key(aesfile, pub_key)
|
187
|
-
|
188
|
-
Like I mentioned, this code needs to be cleaned up a bit, but it gives me all of the functionality I need to build my application and it doesn't require any external libraries except OpenSSL and YAML which are typically standard in any Ruby installation.
|
189
|
-
|
190
|
-
Hopefully someone finds this post useful and if you have any additions, corrections, criticisms please post them below. Feedback is always welcome.
|
@@ -1,77 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
4
|
-
### Star Schema Strategy
|
5
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
6
|
-
|
7
|
-
- We move away from a single private key to one per sealed envelope.
|
8
|
-
|
9
|
-
There are 2 huge 72 character keys ( NOT base64 characters but UTF-7 (or 8) possible characters )
|
10
|
-
|
11
|
-
(note - in common mistakes article put the fact that random keys are generated
|
12
|
-
from tools like securerandom that do not cover the full range f possible
|
13
|
-
chars hence sharpely narrowing the search space for an attacker.
|
14
|
-
)
|
15
|
-
|
16
|
-
Write test program to prove that EVERY ONE of THE 128 CHARACTERS COUNT.
|
17
|
-
How ----> Create 71 characters that are all AAAAAAA...
|
18
|
-
====> Rotate round each of the 128 chars as the 72nd character
|
19
|
-
----> Check that bcrypt generates 128 different outcomes for each of the 128 chars.
|
20
|
-
|
21
|
-
|
22
|
-
==> Use get random bytes to get 72 random bytes (not secure random which halves (if not worse) the search space).
|
23
|
-
|
24
|
-
|
25
|
-
When password entered we read the salt and retrieve the key and unlock the STRONG 256 BIT key (found by getting 32 random bytes then mapping to 32 bytes with say SHA512 digest) crypt for index file.
|
26
|
-
|
27
|
-
We then grab this crypt key (32 bytes long or 64 bytes - the longest a symmetric crypt key can be) - test this out with AES256.
|
28
|
-
|
29
|
-
The crypt key unlocks the index file.
|
30
|
-
|
31
|
-
We then grab the session ID - then hash that say through Digest.SHA512 - and use the resulting key to decrypt the ENVironment VARIABLE.
|
32
|
-
This reveals a 512 (or 256) bit SESSION crypt key which creates a copy of the (in-memory) index file and then writes it with a new name
|
33
|
-
that is the alpha-number session ID hash. We encrypt the index file with the new key.
|
34
|
-
|
35
|
-
Still in SAME CALL we prepare the NEXT (future) SEAL password. We create a 256 bit key and then derive a new bcrypt salt and out comes the 186bit key (which we hash and use to lock the 256 bit key just created and we save the salt in the configs and save the future SEAL password crypt in the configs.
|
36
|
-
The crux move is we use the session key to also lock the seal password for when session ends.
|
37
|
-
|
38
|
-
NOW WE THROW AWAY THE PASSWORD THE BEAUTY OF THIS METHOD IS THAT WE NEVER KEEP THE PASSWORD ANYWHERE ENCRYPTED OR OTHERWISE - WE TRHOW IT AWAY WE WONT NEED IT AGAIN UNTIL THE NEXT SESSION.
|
39
|
-
|
40
|
-
|
41
|
-
Now we continue with the other open, put, add type calls. They all now accesses the copied session index file and it is changed as appropriate.
|
42
|
-
|
43
|
-
|
44
|
-
Finally when the SEAL time comes we use session key to unlock and retrieve the NEXT time (seal) key. We lock index file with this key.
|
45
|
-
|
46
|
-
In clean up we throw away all our session bits and bobs.
|
47
|
-
|
48
|
-
|
49
|
-
We are ready for "next time" - when the password is entered we derive the key again and can unlock the last saved index file!! Cool plan or what??
|
50
|
-
|
51
|
-
|
52
|
-
REDIS Store + more
|
53
|
-
|
54
|
-
This plan allows a seamless migration to keeping the index not in a file (risks failure)
|
55
|
-
but in a REDIS or etcd or PostgreSQL or other key-value stores.
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
60
|
-
### Star Schema for Each Envelope Details
|
61
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
62
|
-
|
63
|
-
- path off session base
|
64
|
-
- path to private key
|
65
|
-
- (inner encrypted private key lock)
|
66
|
-
- path to keyfile
|
67
|
-
- path to crypt file
|
68
|
-
- outer path
|
69
|
-
- inner path
|
70
|
-
- sealed already?
|
71
|
-
- is reopen or no?
|
72
|
-
- last accessed time
|
73
|
-
- who by user@hostname
|
74
|
-
- on what ppi (shell) id@mac address
|
75
|
-
- top level count
|
76
|
-
|
77
|
-
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
# OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac.
|
4
|
-
# This method is provided for backwards compatibility.
|
5
|
-
|
6
|
-
def pbkdf2_hmac(pass, salt, iter, keylen, digest)
|
7
|
-
|
8
|
-
OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
|
9
|
-
length: keylen, hash: digest)
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
##########################################################################################
|
15
|
-
##########################################################################################
|
16
|
-
##########################################################################################
|
17
|
-
##########################################################################################
|
18
|
-
##########################################################################################
|
19
|
-
|
20
|
-
|
21
|
-
OpenSSL::KDF
|
22
|
-
Provides functionality of various KDFs (key derivation function).
|
23
|
-
|
24
|
-
KDF is typically used for securely deriving arbitrary length symmetric keys to be used with an OpenSSL::Cipher from passwords. Another use case is for storing passwords: Due to the ability to tweak the effort of computation by increasing the iteration count, computation can be slowed down artificially in order to render possible attacks infeasible.
|
25
|
-
|
26
|
-
Currently, OpenSSL::KDF provides implementations for the following KDF:
|
27
|
-
|
28
|
-
PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in combination with HMAC
|
29
|
-
|
30
|
-
scrypt
|
31
|
-
|
32
|
-
HKDF
|
33
|
-
|
34
|
-
############################
|
35
|
-
#### Parameters
|
36
|
-
############################
|
37
|
-
|
38
|
-
pass = the passphrase
|
39
|
-
|
40
|
-
salt = The salt. Salts prevent attacks based on dictionaries of common passwords and attacks based on rainbow tables. It is a public value that can be safely stored along with the password (e.g. if the derived value is used for password storage).
|
41
|
-
|
42
|
-
iterations = The iteration count. This provides the ability to tune the algorithm. It is better to use the highest count possible for the maximum resistance to brute-force attacks.
|
43
|
-
|
44
|
-
length = The desired length of the derived key in octets.
|
45
|
-
|
46
|
-
hash = The hash algorithm used with HMAC for the PRF. May be a String representing the algorithm name, or an instance of OpenSSL::Digest.
|
47
|
-
|
48
|
-
|
49
|
-
############################
|
50
|
-
#### Examples
|
51
|
-
############################
|
52
|
-
|
53
|
-
Generating a 128 bit key for a Cipher (e.g. AES)¶ ↑
|
54
|
-
pass = "secret"
|
55
|
-
salt = OpenSSL::Random.random_bytes(16)
|
56
|
-
iter = 20_000
|
57
|
-
key_len = 16
|
58
|
-
key = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
|
59
|
-
length: key_len, hash: "sha1")
|
60
|
-
|
61
|
-
############################
|
62
|
-
#### Storing Passwords
|
63
|
-
############################
|
64
|
-
|
65
|
-
pass = "secret"
|
66
|
-
# store this with the generated value
|
67
|
-
salt = OpenSSL::Random.random_bytes(16)
|
68
|
-
iter = 20_000
|
69
|
-
hash = OpenSSL::Digest::SHA256.new
|
70
|
-
len = hash.digest_length
|
71
|
-
# the final value to be stored
|
72
|
-
value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
|
73
|
-
length: len, hash: hash)
|
74
|
-
|
75
|
-
############################
|
76
|
-
#### Important Note on Checking Passwords
|
77
|
-
#### Thwart Timing Attacks by taking the Same Amount of Time to Check Failures and Successes
|
78
|
-
############################
|
79
|
-
|
80
|
-
When comparing passwords provided by the user with previously stored values, a common mistake made is comparing the two values using "==". Typically, "==" short-circuits on evaluation, and is therefore vulnerable to timing attacks. The proper way is to use a method that always takes the same amount of time when comparing two values, thus not leaking any information to potential attackers. To compare two values, the following could be used:
|
81
|
-
|
82
|
-
def eql_time_cmp(a, b)
|
83
|
-
unless a.length == b.length
|
84
|
-
return false
|
85
|
-
end
|
86
|
-
cmp = b.bytes
|
87
|
-
result = 0
|
88
|
-
a.bytes.each_with_index {|c,i|
|
89
|
-
result |= c ^ cmp[i]
|
90
|
-
}
|
91
|
-
result == 0
|
92
|
-
end
|
93
|
-
Please note that the premature return in case of differing lengths typically does not leak valuable information - when using PBKDF2, the length of the values to be compared is of fixed size.
|
94
|
-
|
95
|
-
|
@@ -1,266 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
4
|
-
### Creating a Key
|
5
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
6
|
-
|
7
|
-
This example creates a 2048 bit RSA keypair and writes it to the current directory.
|
8
|
-
|
9
|
-
key = OpenSSL::PKey::RSA.new 2048
|
10
|
-
|
11
|
-
open 'private_key.pem', 'w' do |io| io.write key.to_pem end
|
12
|
-
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end
|
13
|
-
|
14
|
-
|
15
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
16
|
-
### Exporting a Key
|
17
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
18
|
-
|
19
|
-
Keys saved to disk without encryption are not secure as anyone who gets ahold of the key may use it unless it is encrypted. In order to securely export a key you may export it with a pass phrase.
|
20
|
-
|
21
|
-
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
|
22
|
-
pass_phrase = 'my secure pass phrase goes here'
|
23
|
-
|
24
|
-
key_secure = key.export cipher, pass_phrase
|
25
|
-
|
26
|
-
open 'private.secure.pem', 'w' do |io|
|
27
|
-
io.write key_secure
|
28
|
-
end
|
29
|
-
OpenSSL::Cipher.ciphers returns a list of available ciphers.
|
30
|
-
|
31
|
-
|
32
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
33
|
-
### Loading a Key
|
34
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
35
|
-
|
36
|
-
A key can also be loaded from a file.
|
37
|
-
|
38
|
-
key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
|
39
|
-
key2.public? # => true
|
40
|
-
key2.private? # => true
|
41
|
-
or
|
42
|
-
|
43
|
-
key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
|
44
|
-
key3.public? # => true
|
45
|
-
key3.private? # => false
|
46
|
-
|
47
|
-
|
48
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
49
|
-
### Loading an Encrypted Key
|
50
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
51
|
-
|
52
|
-
OpenSSL will prompt you for your pass phrase when loading an encrypted key. If you will not be able to type in the pass phrase you may provide it when loading the key:
|
53
|
-
|
54
|
-
key4_pem = File.read 'private.secure.pem'
|
55
|
-
pass_phrase = 'my secure pass phrase goes here'
|
56
|
-
key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase
|
57
|
-
|
58
|
-
|
59
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
60
|
-
### RSA Encryption
|
61
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
62
|
-
|
63
|
-
RSA provides encryption and decryption using the public and private keys. You can use a variety of padding methods depending upon the intended use of encrypted data.
|
64
|
-
|
65
|
-
|
66
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
67
|
-
### Encryption & Decryption
|
68
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
69
|
-
|
70
|
-
Asymmetric public/private key encryption is slow and victim to attack in cases where it is used without padding or directly to encrypt larger chunks of data. Typical use cases for RSA encryption involve wrapping a symmetric key with the public key of the recipient who would unwrap that symmetric key again using their private key. The following illustrates a simplified example of such a key transport scheme. It shouldnt be used in practice, though, standardized protocols should always be preferred.
|
71
|
-
|
72
|
-
wrapped_key = key.public_encrypt key
|
73
|
-
A symmetric key encrypted with the public key can only be decrypted with the corresponding private key of the recipient.
|
74
|
-
|
75
|
-
original_key = key.private_decrypt wrapped_key
|
76
|
-
By default PKCS#1 padding will be used, but it is also possible to use other forms of padding, see PKey::RSA for further details.
|
77
|
-
|
78
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
79
|
-
### Signatures
|
80
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
81
|
-
|
82
|
-
|
83
|
-
Using “private_encrypt” to encrypt some data with the private key is equivalent to applying a digital signature to the data. A verifying party may validate the signature by comparing the result of decrypting the signature with “public_decrypt” to the original data. However, OpenSSL::PKey already has methods “sign” and “verify” that handle digital signatures in a standardized way - “private_encrypt” and “public_decrypt” shouldnt be used in practice.
|
84
|
-
|
85
|
-
To sign a document, a cryptographically secure hash of the document is computed first, which is then signed using the private key.
|
86
|
-
|
87
|
-
digest = OpenSSL::Digest::SHA256.new
|
88
|
-
signature = key.sign digest, document
|
89
|
-
To validate the signature, again a hash of the document is computed and the signature is decrypted using the public key. The result is then compared to the hash just computed, if they are equal the signature was valid.
|
90
|
-
|
91
|
-
digest = OpenSSL::Digest::SHA256.new
|
92
|
-
if key.verify digest, signature, document
|
93
|
-
puts 'Valid'
|
94
|
-
else
|
95
|
-
puts 'Invalid'
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
100
|
-
### PBKDF2 Password-based Encryption
|
101
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
102
|
-
|
103
|
-
If supported by the underlying OpenSSL version used, Password-based Encryption should use the features of PKCS5. If not supported or if required by legacy applications, the older, less secure methods specified in RFC 2898 are also supported (see below).
|
104
|
-
|
105
|
-
PKCS5 supports PBKDF2 as it was specified in PKCS#5 v2.0. It still uses a password, a salt, and additionally a number of iterations that will slow the key derivation process down. The slower this is, the more work it requires being able to brute-force the resulting key.
|
106
|
-
|
107
|
-
|
108
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
109
|
-
### Encryption
|
110
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
111
|
-
|
112
|
-
The strategy is to first instantiate a Cipher for encryption, and then to generate a random IV plus a key derived from the password using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt, the number of iterations largely depends on the hardware being used.
|
113
|
-
|
114
|
-
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
|
115
|
-
cipher.encrypt
|
116
|
-
iv = cipher.random_iv
|
117
|
-
|
118
|
-
pwd = 'some hopefully not to easily guessable password'
|
119
|
-
salt = OpenSSL::Random.random_bytes 16
|
120
|
-
iter = 20000
|
121
|
-
key_len = cipher.key_len
|
122
|
-
digest = OpenSSL::Digest::SHA256.new
|
123
|
-
|
124
|
-
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
|
125
|
-
cipher.key = key
|
126
|
-
|
127
|
-
Now encrypt the data:
|
128
|
-
|
129
|
-
encrypted = cipher.update document
|
130
|
-
encrypted << cipher.final
|
131
|
-
|
132
|
-
|
133
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
134
|
-
### Decryption
|
135
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
136
|
-
|
137
|
-
Use the same steps as before to derive the symmetric AES key, this time setting the Cipher up for decryption.
|
138
|
-
|
139
|
-
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
|
140
|
-
cipher.decrypt
|
141
|
-
cipher.iv = iv # the one generated with #random_iv
|
142
|
-
|
143
|
-
pwd = 'some hopefully not to easily guessable password'
|
144
|
-
salt = ... # the one generated above
|
145
|
-
iter = 20000
|
146
|
-
key_len = cipher.key_len
|
147
|
-
digest = OpenSSL::Digest::SHA256.new
|
148
|
-
|
149
|
-
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
|
150
|
-
cipher.key = key
|
151
|
-
|
152
|
-
Now decrypt the data:
|
153
|
-
|
154
|
-
decrypted = cipher.update encrypted
|
155
|
-
decrypted << cipher.final
|
156
|
-
|
157
|
-
|
158
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
159
|
-
### PKCS #5 Password-based Encryption
|
160
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
161
|
-
|
162
|
-
PKCS #5 is a password-based encryption standard documented at RFC2898. It allows a short password or passphrase to be used to create a secure encryption key. If possible, PBKDF2 as described above should be used if the circumstances allow it.
|
163
|
-
|
164
|
-
PKCS #5 uses a Cipher, a pass phrase and a salt to generate an encryption key.
|
165
|
-
|
166
|
-
pass_phrase = 'my secure pass phrase goes here'
|
167
|
-
salt = '8 octets'
|
168
|
-
|
169
|
-
|
170
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
171
|
-
### Encryption
|
172
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
173
|
-
|
174
|
-
First set up the cipher for encryption
|
175
|
-
|
176
|
-
encryptor = OpenSSL::Cipher.new 'AES-128-CBC'
|
177
|
-
encryptor.encrypt
|
178
|
-
encryptor.pkcs5_keyivgen pass_phrase, salt
|
179
|
-
Then pass the data you want to encrypt through
|
180
|
-
|
181
|
-
encrypted = encryptor.update 'top secret document'
|
182
|
-
encrypted << encryptor.final
|
183
|
-
|
184
|
-
|
185
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
186
|
-
### Decryption
|
187
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
188
|
-
|
189
|
-
Use a new Cipher instance set up for decryption
|
190
|
-
|
191
|
-
decryptor = OpenSSL::Cipher.new 'AES-128-CBC'
|
192
|
-
decryptor.decrypt
|
193
|
-
decryptor.pkcs5_keyivgen pass_phrase, salt
|
194
|
-
Then pass the data you want to decrypt through
|
195
|
-
|
196
|
-
plain = decryptor.update encrypted
|
197
|
-
plain << decryptor.final
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
202
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
203
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
204
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
205
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@ ### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
206
|
-
|
207
|
-
|
208
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
209
|
-
### OpenSSL::PKCS5
|
210
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
211
|
-
|
212
|
-
Provides password-based encryption functionality based on PKCS#5. Typically used for securely deriving arbitrary length symmetric keys to be used with an OpenSSL::Cipher from passwords. Another use case is for storing passwords: Due to the ability to tweak the effort of computation by increasing the iteration count, computation can be slowed down artificially in order to render possible attacks infeasible.
|
213
|
-
|
214
|
-
PKCS5 offers support for PBKDF2 with an OpenSSL::Digest::SHA1-based HMAC, or an arbitrary Digest if the underlying version of OpenSSL already supports it (>= 0.9.4).
|
215
|
-
|
216
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
217
|
-
### Parameters
|
218
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
219
|
-
|
220
|
-
|
221
|
-
1 - Password - Typically an arbitrary String that represents the password to be used for deriving a key.
|
222
|
-
|
223
|
-
2 - Salt - Prevents attacks based on dictionaries of common passwords. It is a public value that can be safely stored along with the password (e.g. if PBKDF2 is used for password storage). For maximum security, a fresh, random salt should be generated for each stored password. According to PKCS#5, a salt should be at least 8 bytes long.
|
224
|
-
|
225
|
-
3 - Iteration Count - Allows to tweak the length that the actual computation will take. The larger the iteration count, the longer it will take.
|
226
|
-
|
227
|
-
4 - Key Length - Specifies the length in bytes of the output that will be generated. Typically, the key length should be larger than or equal to the output length of the underlying digest function, otherwise an attacker could simply try to brute-force the key. According to PKCS#5, security is limited by the output length of the underlying digest function, i.e. security is not improved if a key length strictly larger than the digest output length is chosen. Therefore, when using PKCS5 for password storage, it suffices to store values equal to the digest output length, nothing is gained by storing larger values.
|
228
|
-
|
229
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
230
|
-
### Generating a 128 bit key for a Cipher (e.g. AES)
|
231
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
232
|
-
|
233
|
-
pass = "secret"
|
234
|
-
salt = OpenSSL::Random.random_bytes(16)
|
235
|
-
iter = 20000
|
236
|
-
key_len = 16
|
237
|
-
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len)
|
238
|
-
|
239
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
240
|
-
### Storing Passwords
|
241
|
-
### @@@@@@@@@@@@@@@@@@@@@@@@@@
|
242
|
-
|
243
|
-
pass = "secret"
|
244
|
-
salt = OpenSSL::Random.random_bytes(16) #store this with the generated value
|
245
|
-
iter = 20000
|
246
|
-
digest = OpenSSL::Digest::SHA256.new
|
247
|
-
len = digest.digest_length
|
248
|
-
#the final value to be stored
|
249
|
-
value = OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter, len, digest)
|
250
|
-
Important Note on Checking Passwords¶ ↑
|
251
|
-
When comparing passwords provided by the user with previously stored values, a common mistake made is comparing the two values using "==". Typically, "==" short-circuits on evaluation, and is therefore vulnerable to timing attacks. The proper way is to use a method that always takes the same amount of time when comparing two values, thus not leaking any information to potential attackers. To compare two values, the following could be used:
|
252
|
-
|
253
|
-
def eql_time_cmp(a, b)
|
254
|
-
unless a.length == b.length
|
255
|
-
return false
|
256
|
-
end
|
257
|
-
cmp = b.bytes.to_a
|
258
|
-
result = 0
|
259
|
-
a.bytes.each_with_index {|c,i|
|
260
|
-
result |= c ^ cmp[i]
|
261
|
-
}
|
262
|
-
result == 0
|
263
|
-
end
|
264
|
-
|
265
|
-
|
266
|
-
Please note that the premature return in case of differing lengths typically does not leak valuable information - when using PKCS#5, the length of the values to be compared is of fixed size.
|