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
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 209 209: def read_key(type, key=nil, passphrase=nil) 210: return key if key.nil? 211: 212: raise "Could not find key #{key}" unless File.exist?(key) 213: 214: if type == :public 215: begin 216: key = OpenSSL::PKey::RSA.new(File.read(key)) 217: rescue OpenSSL::PKey::RSAError 218: key = OpenSSL::X509::Certificate.new(File.read(key)).public_key 219: end 220: 221: # Ruby < 1.9.3 had a bug where it does not correctly clear the 222: # queue of errors while reading a key. It tries various ways 223: # to read the key and each failing attempt pushes an error onto 224: # the queue. With pubkeys only the 3rd attempt pass leaving 2 225: # stale errors on the error queue. 226: # 227: # In 1.9.3 they fixed this by simply discarding the errors after 228: # every attempt. So we simulate this fix here for older rubies 229: # as without it we get SSL_read errors from the Stomp+TLS sessions 230: # 231: # We do this only on 1.8 relying on 1.9.3 to do the right thing 232: # and we do not support 1.9 less than 1.9.3 233: # 234: # See http://bugs.ruby-lang.org/issues/4550 235: OpenSSL.errors if Util.ruby_version =~ /^1.8/ 236: 237: return key 238: elsif type == :private 239: return OpenSSL::PKey::RSA.new(File.read(key), passphrase) 240: else 241: raise "Can only load :public or :private keys" 242: end 243: 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