class MCollective::SSL - mcollective version 2.12.5

# class MCollective::SSL

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

### Attributes

private_key_file[R]
public_key_file[R]
ssl_cipher[R]

### Public Class Methods

base64_decode(string) click to toggle source

        # File lib/mcollective/ssl.rb
    195 def self.base64_decode(string)
    196   # The Base 64 character set is A-Z a-z 0-9 + / =
    197   # Also allow for whitespace, but raise if we get anything else
    198   if string !~ /^[A-Za-z0-9+\/=\s]+$/
    199     raise ArgumentError, 'invalid base64'
    200   end
    201   Base64.decode64(string)
    202 end

base64_encode(string) click to toggle source

        # File lib/mcollective/ssl.rb
    186 def self.base64_encode(string)
    187   Base64.encode64(string)
    188 end

md5(string) click to toggle source

        # File lib/mcollective/ssl.rb
    208 def self.md5(string)
    209   Digest::MD5.hexdigest(string)
    210 end

new(pubkey=nil, privkey=nil, passphrase=nil, cipher=nil) click to toggle source

       # File lib/mcollective/ssl.rb
    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

uuid(string=nil) click to toggle source

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
    218 def self.uuid(string=nil)
    219   string ||= OpenSSL::Random.random_bytes(16).unpack('H*').shift
    220
    221   uuid_name_space_dns = [0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8].map {|b| b.chr}.join
    222
    223   sha1 = Digest::SHA1.new
    224   sha1.update(uuid_name_space_dns)
    225   sha1.update(string)
    226
    227   # first 16 bytes..
    228   bytes = sha1.digest[0, 16].bytes.to_a
    229
    230   # version 5 adjustments
    231   bytes[6] &= 0x0f
    232   bytes[6] |= 0x50
    233
    234   # variant is DCE 1.1
    235   bytes[8] &= 0x3f
    236   bytes[8] |= 0x80
    237
    238   bytes = [4, 2, 2, 2, 6].collect do |i|
    239     bytes.slice!(0, i).pack('C*').unpack('H*')
    240   end
    241
    242   bytes.join('-')
    243 end

### Public Instance Methods

aes_decrypt(key, crypt_string) click to toggle source

decrypts a string given key, iv and data

        # File lib/mcollective/ssl.rb
    158 def aes_decrypt(key, crypt_string)
    159   cipher = OpenSSL::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

aes_encrypt(plain_string) click to toggle source

encrypts a string, returns a hash of key, iv and data

        # File lib/mcollective/ssl.rb
    144 def aes_encrypt(plain_string)
    145   cipher = OpenSSL::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

base64_decode(string) click to toggle source

base 64 decode a string

        # File lib/mcollective/ssl.rb
    191 def base64_decode(string)
    192   SSL.base64_decode(string)
    193 end

base64_encode(string) click to toggle source

base 64 encode a string

        # File lib/mcollective/ssl.rb
    182 def base64_encode(string)
    183   SSL.base64_encode(string)
    184 end

decrypt_with_private(crypted, base64=true) click to toggle source

Decrypts data, expects a hash as create with crypt_with_public

       # File lib/mcollective/ssl.rb
    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

decrypt_with_public(crypted, base64=true) click to toggle source

Decrypts data, expects a hash as create with crypt_with_private

        # File lib/mcollective/ssl.rb
    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

encrypt_with_private(plain_text, base64=true) click to toggle source

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
    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

encrypt_with_public(plain_text, base64=true) click to toggle source

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
    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

md5(string) click to toggle source

        # File lib/mcollective/ssl.rb
    204 def md5(string)
    205   SSL.md5(string)
    206 end

read_key(type, key=nil, passphrase=nil) click to toggle source

Reads either a :public or :private key from disk, uses an optional passphrase to read the private key

        # File lib/mcollective/ssl.rb
    247 def read_key(type, key=nil, passphrase=nil)
    248   return key if key.nil?
    249
    250   raise "Could not find key #{key}" unless File.exist?(key)
    251   raise "#{type} key file '#{key}' is empty" if File.zero?(key)
    252
    253   if type == :public
    254     begin
    255       key = OpenSSL::PKey::RSA.new(File.read(key))
    256     rescue OpenSSL::PKey::RSAError
    257       key = OpenSSL::X509::Certificate.new(File.read(key)).public_key
    258     end
    259
    260     # Ruby < 1.9.3 had a bug where it does not correctly clear the
    261     # queue of errors while reading a key.  It tries various ways
    262     # to read the key and each failing attempt pushes an error onto
    263     # the queue.  With pubkeys only the 3rd attempt pass leaving 2
    264     # stale errors on the error queue.
    265     #
    266     # In 1.9.3 they fixed this by simply discarding the errors after
    267     # every attempt.  So we simulate this fix here for older rubies
    268     # as without it we get SSL_read errors from the Stomp+TLS sessions
    269     #
    270     # We do this only on 1.8 relying on 1.9.3 to do the right thing
    271     # and we do not support 1.9 less than 1.9.3
    272     #
    273     # See  http://bugs.ruby-lang.org/issues/4550
    274     OpenSSL.errors if Util.ruby_version =~ /^1.8/
    275
    276     return key
    277   elsif type == :private
    278     return OpenSSL::PKey::RSA.new(File.read(key), passphrase)
    279   else
    280     raise "Can only load :public or :private keys"
    281   end
    282 end

rsa_decrypt_with_private(crypt_string) click to toggle source

Use the private key to RSA decrypt data

        # File lib/mcollective/ssl.rb
    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

rsa_decrypt_with_public(crypt_string) click to toggle source

Use the public key to RSA decrypt data

        # File lib/mcollective/ssl.rb
    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

rsa_encrypt_with_private(plain_string) click to toggle source

Use the private key to RSA encrypt data

        # File lib/mcollective/ssl.rb
    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

rsa_encrypt_with_public(plain_string) click to toggle source

Use the public key to RSA encrypt data

        # File lib/mcollective/ssl.rb
    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

sign(string, base64=false) click to toggle source

Signs a string using the private key

        # File lib/mcollective/ssl.rb
    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

verify_signature(signature, string, base64=false) click to toggle source

Using the public key verifies that a string was signed using the private key

        # File lib/mcollective/ssl.rb
    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