views:

734

answers:

3

I've just upgraded my Mac to Snow Leopard and got my Rails environment up and running. The only difference -- OSX aside -- with my previous install is that I'm now running ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0] (Snow Leopard default) rather than 1.8.6.

I'm now seeing deprecation warnings relating to OpenSSL when I run my code:

warning: argumtents for OpenSSL::Cipher::Cipher#encrypt and OpenSSL::Cipher::Cipher#decrypt were deprecated; use OpenSSL::Cipher::Cipher#pkcs5_keyivgen to derive key and IV

Example of my code which is causing these warnings (it decodes an encrypted string) on line 4:

1. def decrypt(data)
2.  encryptor = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC')
3.  key = "my key"
4.  encryptor.decrypt(key)
5.  text = encryptor.update(data)
6.  text << encryptor.final
7. end

I'm struggling to understand how I can resolve this, and Google isn't really helping. Should I try and downgrade to Ruby 1.8.6 (and if so, what's the best way of doing this?), should I try and just hide the warnings (bury my head in the sand?!) or is there an easy fix I can apply in the code?

+2  A: 

Due to the implicit type conversion in Ruby, older Ruby allows people use PBE (Password-Based Encryption) in a totally wrong way. The newer one fixes that so the warning is a good thing.

Your example shows exactly the problem. Triple-DES requires 24-byte key material (including parity) but you only provided 6 bytes. Your key material will be repeated to make up the deficit, that resulted in a less secure key.

The correct way to do this is to generate key and IV (initial vector) with PKCS5, which use complicated hashing and iteration to make the key much more secure.

Ruby provides following sample code. pass is your key and you can use any hardcoded value for salt.

puts "--Encrypting--"
des = OpenSSL::Cipher::Cipher.new(alg)
des.pkcs5_keyivgen(pass, salt)
des.encrypt
cipher =  des.update(text)
cipher << des.final
puts %(encrypted text: #{cipher.inspect})
puts

puts "--Decrypting--"
des = OpenSSL::Cipher::Cipher.new(alg)
des.pkcs5_keyivgen(pass, salt)
des.decrypt
out =  des.update(cipher)
out << des.final
puts %(decrypted text: "#{out}")
puts
ZZ Coder
A: 

Doesn't the decryption code as it stands above require that we have access to the cleartext password in the DB? That's exactly what we're trying to get away from; storing cleartext passwords in the DB is a Generally Bad Idea.

Are there any other solutions?

The password shouldn't be in the DB. It could be stored in an environment variable (perhaps passed in through the deployment process) or even loaded in from a secure file on disk when the application starts.
Olly
A: 

ZZ Coder was close, but no cigar. In fact, you should never call Cipher#pkcs5_keyivgen before #decrypt or #encrypt. In practice, generally it will encrypt fine, but decrypt will oft times fail. So the code should be:

puts "--Encrypting--"
des = OpenSSL::Cipher::Cipher.new(alg)
des.encrypt
des.pkcs5_keyivgen(pass, salt)
cipher =  des.update(text)
cipher << des.final
puts %(encrypted text: #{cipher.inspect})
puts

and

puts "--Decrypting--"
des = OpenSSL::Cipher::Cipher.new(alg)
des.decrypt
des.pkcs5_keyivgen(pass, salt)  
out =  des.update(cipher)
out << des.final
puts %(decrypted text: "#{out}")
puts