Thursday, December 20, 2007

AES encryption and decryption in Ruby

AES encryption and decryption in Ruby is very simple, although I had a hard time finding documentation on how to do it. Ruby has a wrapper over OpenSSL, but I had trouble finding documentation on how to use OpenSSL as well. I found a pretty good piece of code at http://snippets.dzone.com/posts/show/576.

Using this and other information that I found, I created a module for encrypting and decrypting blocks of data. It isn't just limited to AES encryption and decryption, since OpenSSL supports many other types of encryption, but I only use it for AES 256. To get a list of the types of encryption that OpenSSL uses (and what the string would be that you pass in to my functions as cipher_type), open a command prompt, type openssl, then help, and the cipher types are listed under cipher-commands. The two types for AES 256 encryption are "AES-256-CBC" and "AES-256-ECB". Here is my module:


require 'openssl'

module AESCrypt
# Decrypts a block of data (encrypted_data) given an encryption key
# and an initialization vector (iv). Keys, iv's, and the data
# returned are all binary strings. Cipher_type should be
# "AES-256-CBC", "AES-256-ECB", or any of the cipher types
# supported by OpenSSL. Pass nil for the iv if the encryption type
# doesn't use iv's (like ECB).
#:return: => String
#:arg: encrypted_data => String
#:arg: key => String
#:arg: iv => String
#:arg: cipher_type => String
def AESCrypt.decrypt(encrypted_data, key, iv, cipher_type)
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(encrypted_data) + aes.final
end

# Encrypts a block of data given an encryption key and an
# initialization vector (iv). Keys, iv's, and the data returned
# are all binary strings. Cipher_type should be "AES-256-CBC",
# "AES-256-ECB", or any of the cipher types supported by OpenSSL.
# Pass nil for the iv if the encryption type doesn't use iv's (like
# ECB).
#:return: => String
#:arg: data => String
#:arg: key => String
#:arg: iv => String
#:arg: cipher_type => String
def AESCrypt.encrypt(data, key, iv, cipher_type)
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(data) + aes.final
end
end

11 comments:

ryan said...

great post, I am trying to get rails to do the encryption/decryption for me so i can just use the variable in the controller. It is easy to encrypt with a before_save filter, but how can we decrypt it after a select statement?

Anonymous said...

Well - if you are worried about decrypting just a single field, you can override the accessor methods.

def value=(val)
write_attribute(:value,encrypt(val))
end
def value
decrypt(read_attribute(:value))
end

danimata said...

Thanks a lot for this code. Really useful

wkapastin said...

When I try using this module, I always get the error "OpenSSL::CipherError: wrong final block length" when trying to decrypt. Example below:

AESCrypt.encrypt("tester","blahblahasdfasdfasdfasdfasdfasdf",nil,"AES-256-CBC")

AESCrypt.decrypt("}\206\213\237x?Kl\002x9?","blahblahasdfasdfasdfasdfasdfasdf",nil,"AES-256-CBC")
OpenSSL::CipherError: wrong final block length

I am pretty uninformed on this topic, so any help is greatly appreciated...

Anonymous said...

That's a nice script and it works for me as long as i run it in the terminal via irb on my mac. But when I try to implement this in my Rails App in /lib as a module and than call it from a Controller it returns things like "PG)??_Q?Ф???"

My code to run it looks like that:

class XY < ApplicationController
include Aescrypt
def index
puts Aescrypt.encrypt("Hehe", "Blalalalalalalalalalalalalalalalalalalalalalalalalal" , "jahdsfjhgdhjgdsjkhgjdfgajdhfhdagf", "AES-256-CBC")
end
end

in the terminal it returns

F???EMq??'?l%

do you have any idea?
Thx a lot.

Brent said...

The difference might be because your key and IV lengths are not correct. The second parameter is the key, which should be 32 bytes/characters long for AES-256. The text string that you have for that is longer than 32 characters. The third parameter is the IV (initialization vector) which should be 16 bytes/characters long, and what you are passing in is longer than 16 characters. Try to cut those two parameters down to 32 and 16 characters and see if you get the same results.

Anonymous said...

Hmm, it doesn't look like that this is the problem because as long as I run it outside the Rails env. it works fine and returns:
"\354\025#\300!\350'ZE\310\245I\2738\n-"

Inside Rails it doesn't work.

0a said...

Thank you for the code...
Did you find a way to make it work inside rails?

Alexey said...

This comment has been removed by the author.

Alexey said...

great article!

how do i store encrypted string into database ?
I have:
Mysql::Error: Incorrect string value: '\xDF\xF0\xCF\xE7\x1A\xDB...' for column 'email' at row 1:
UPDATE `orders` SET `email` = '▀Ё╧ч\Z█n~╒q+eХ┤a╡' .....

What type of column it has to be ?

Alexey said...

ok i found ... it has to be blob (:binary)

for mysql