| Class | MCollective::SSL |
| In: |
lib/mcollective/ssl.rb
|
| Parent: | Object |
A class that assists in encrypting and decrypting data using a combination of RSA and AES
Data will be AES encrypted for speed, the Key used in # the AES stage will be encrypted using RSA
ssl = SSL.new(public_key, private_key, passphrase)
data = File.read("largefile.dat")
crypted_data = ssl.encrypt_with_private(data)
pp crypted_data
This will result in a hash of data like:
crypted = {:key => "crd4NHvG....=",
:data => "XWXlqN+i...=="}
The key and data will all be base 64 encoded already by default you can pass a 2nd parameter as false to encrypt_with_private and counterparts that will prevent the base 64 encoding
You can pass the data hash into ssl.decrypt_with_public which should return your original data
There are matching methods for using a public key to encrypt data to be decrypted using a private key
| private_key_file | [R] | |
| public_key_file | [R] | |
| ssl_cipher | [R] |
# File lib/mcollective/ssl.rb, line 195
195: def self.base64_decode(string)
196: Base64.decode64(string)
197: end
# File lib/mcollective/ssl.rb, line 186
186: def self.base64_encode(string)
187: Base64.encode64(string)
188: end
# File lib/mcollective/ssl.rb, line 203
203: def self.md5(string)
204: Digest::MD5.hexdigest(string)
205: end
# File lib/mcollective/ssl.rb, line 37
37: def initialize(pubkey=nil, privkey=nil, passphrase=nil, cipher=nil)
38: @public_key_file = pubkey
39: @private_key_file = privkey
40:
41: @public_key = read_key(:public, pubkey)
42: @private_key = read_key(:private, privkey, passphrase)
43:
44: @ssl_cipher = "aes-256-cbc"
45: @ssl_cipher = Config.instance.ssl_cipher if Config.instance.ssl_cipher
46: @ssl_cipher = cipher if cipher
47:
48: raise "The supplied cipher '#{@ssl_cipher}' is not supported" unless OpenSSL::Cipher.ciphers.include?(@ssl_cipher)
49: end
Creates a RFC 4122 version 5 UUID. If string is supplied it will produce repeatable UUIDs for that string else a random 128bit string will be used from OpenSSL::BN
Code used with permission from:
https://github.com/kwilczynski/puppet-functions/blob/master/lib/puppet/parser/functions/uuid.rb
# File lib/mcollective/ssl.rb, line 213
213: def self.uuid(string=nil)
214: string ||= OpenSSL::Random.random_bytes(16).unpack('H*').shift
215:
216: uuid_name_space_dns = "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"
217:
218: sha1 = Digest::SHA1.new
219: sha1.update(uuid_name_space_dns)
220: sha1.update(string)
221:
222: # first 16 bytes..
223: bytes = sha1.digest[0, 16].bytes.to_a
224:
225: # version 5 adjustments
226: bytes[6] &= 0x0f
227: bytes[6] |= 0x50
228:
229: # variant is DCE 1.1
230: bytes[8] &= 0x3f
231: bytes[8] |= 0x80
232:
233: bytes = [4, 2, 2, 2, 6].collect do |i|
234: bytes.slice!(0, i).pack('C*').unpack('H*')
235: end
236:
237: bytes.join('-')
238: end
decrypts a string given key, iv and data
# File lib/mcollective/ssl.rb, line 158
158: def aes_decrypt(key, crypt_string)
159: cipher = OpenSSL::Cipher::Cipher.new(ssl_cipher)
160:
161: cipher.decrypt
162: cipher.key = key
163: cipher.pkcs5_keyivgen(key)
164: decrypted_data = cipher.update(crypt_string) + cipher.final
165: end
encrypts a string, returns a hash of key, iv and data
# File lib/mcollective/ssl.rb, line 144
144: def aes_encrypt(plain_string)
145: cipher = OpenSSL::Cipher::Cipher.new(ssl_cipher)
146: cipher.encrypt
147:
148: key = cipher.random_key
149:
150: cipher.key = key
151: cipher.pkcs5_keyivgen(key)
152: encrypted_data = cipher.update(plain_string) + cipher.final
153:
154: {:key => key, :data => encrypted_data}
155: end
base 64 decode a string
# File lib/mcollective/ssl.rb, line 191
191: def base64_decode(string)
192: SSL.base64_decode(string)
193: end
base 64 encode a string
# File lib/mcollective/ssl.rb, line 182
182: def base64_encode(string)
183: SSL.base64_encode(string)
184: end
Decrypts data, expects a hash as create with crypt_with_public
# File lib/mcollective/ssl.rb, line 88
88: def decrypt_with_private(crypted, base64=true)
89: raise "Crypted data should include a key" unless crypted.include?(:key)
90: raise "Crypted data should include data" unless crypted.include?(:data)
91:
92: if base64
93: key = rsa_decrypt_with_private(base64_decode(crypted[:key]))
94: aes_decrypt(key, base64_decode(crypted[:data]))
95: else
96: key = rsa_decrypt_with_private(crypted[:key])
97: aes_decrypt(key, crypted[:data])
98: end
99: end
Decrypts data, expects a hash as create with crypt_with_private
# File lib/mcollective/ssl.rb, line 102
102: def decrypt_with_public(crypted, base64=true)
103: raise "Crypted data should include a key" unless crypted.include?(:key)
104: raise "Crypted data should include data" unless crypted.include?(:data)
105:
106: if base64
107: key = rsa_decrypt_with_public(base64_decode(crypted[:key]))
108: aes_decrypt(key, base64_decode(crypted[:data]))
109: else
110: key = rsa_decrypt_with_public(crypted[:key])
111: aes_decrypt(key, crypted[:data])
112: end
113: end
Encrypts supplied data using AES and then encrypts using RSA the key and IV
Return a hash with everything optionally base 64 encoded
# File lib/mcollective/ssl.rb, line 73
73: def encrypt_with_private(plain_text, base64=true)
74: crypted = aes_encrypt(plain_text)
75:
76: if base64
77: key = base64_encode(rsa_encrypt_with_private(crypted[:key]))
78: data = base64_encode(crypted[:data])
79: else
80: key = rsa_encrypt_with_private(crypted[:key])
81: data = crypted[:data]
82: end
83:
84: {:key => key, :data => data}
85: end
Encrypts supplied data using AES and then encrypts using RSA the key and IV
Return a hash with everything optionally base 64 encoded
# File lib/mcollective/ssl.rb, line 55
55: def encrypt_with_public(plain_text, base64=true)
56: crypted = aes_encrypt(plain_text)
57:
58: if base64
59: key = base64_encode(rsa_encrypt_with_public(crypted[:key]))
60: data = base64_encode(crypted[:data])
61: else
62: key = rsa_encrypt_with_public(crypted[:key])
63: data = crypted[:data]
64: end
65:
66: {:key => key, :data => data}
67: end
Reads either a :public or :private key from disk, uses an optional passphrase to read the private key
# File lib/mcollective/ssl.rb, line 242
242: def read_key(type, key=nil, passphrase=nil)
243: return key if key.nil?
244:
245: raise "Could not find key #{key}" unless File.exist?(key)
246: raise "#{type} key file '#{key}' is empty" if File.zero?(key)
247:
248: if type == :public
249: begin
250: key = OpenSSL::PKey::RSA.new(File.read(key))
251: rescue OpenSSL::PKey::RSAError
252: key = OpenSSL::X509::Certificate.new(File.read(key)).public_key
253: end
254:
255: # Ruby < 1.9.3 had a bug where it does not correctly clear the
256: # queue of errors while reading a key. It tries various ways
257: # to read the key and each failing attempt pushes an error onto
258: # the queue. With pubkeys only the 3rd attempt pass leaving 2
259: # stale errors on the error queue.
260: #
261: # In 1.9.3 they fixed this by simply discarding the errors after
262: # every attempt. So we simulate this fix here for older rubies
263: # as without it we get SSL_read errors from the Stomp+TLS sessions
264: #
265: # We do this only on 1.8 relying on 1.9.3 to do the right thing
266: # and we do not support 1.9 less than 1.9.3
267: #
268: # See http://bugs.ruby-lang.org/issues/4550
269: OpenSSL.errors if Util.ruby_version =~ /^1.8/
270:
271: return key
272: elsif type == :private
273: return OpenSSL::PKey::RSA.new(File.read(key), passphrase)
274: else
275: raise "Can only load :public or :private keys"
276: end
277: end
Use the private key to RSA decrypt data
# File lib/mcollective/ssl.rb, line 123
123: def rsa_decrypt_with_private(crypt_string)
124: raise "No private key set" unless @private_key
125:
126: @private_key.private_decrypt(crypt_string)
127: end
Use the public key to RSA decrypt data
# File lib/mcollective/ssl.rb, line 137
137: def rsa_decrypt_with_public(crypt_string)
138: raise "No public key set" unless @public_key
139:
140: @public_key.public_decrypt(crypt_string)
141: end
Use the private key to RSA encrypt data
# File lib/mcollective/ssl.rb, line 130
130: def rsa_encrypt_with_private(plain_string)
131: raise "No private key set" unless @private_key
132:
133: @private_key.private_encrypt(plain_string)
134: end
Use the public key to RSA encrypt data
# File lib/mcollective/ssl.rb, line 116
116: def rsa_encrypt_with_public(plain_string)
117: raise "No public key set" unless @public_key
118:
119: @public_key.public_encrypt(plain_string)
120: end
Signs a string using the private key
# File lib/mcollective/ssl.rb, line 168
168: def sign(string, base64=false)
169: sig = @private_key.sign(OpenSSL::Digest::SHA1.new, string)
170:
171: base64 ? base64_encode(sig) : sig
172: end
Using the public key verifies that a string was signed using the private key
# File lib/mcollective/ssl.rb, line 175
175: def verify_signature(signature, string, base64=false)
176: signature = base64_decode(signature) if base64
177:
178: @public_key.verify(OpenSSL::Digest::SHA1.new, signature, string)
179: end