25 Sep 2009

Ruby String Encryption

Encrypting a string is fairly easy in Ruby. All you need to do is @require 'openssl'@. For this example I am using the 'des-ede3-cbc' (Triple DES using Cipher Block Chaining) cipher. Most--if not all--of the ciphers listed on OpenSSL.org as supported ciphers should work as well.

Triple DES requires a 24-byte key, with Ruby this means a string with 24 characters. We are also required to provide an initialization vector (IV). The IV is usually the same size as the block size of the cipher you are using (so says Wikipedia, so say we all). In our case this means an IV of 64-bits, or 8-bytes, or an 8 character Ruby String. If we randomize our IV and include it with our encrypted string then we can ensure that our encrypted data never looks the same even when we encrypt the same string multiple times.

  require 'openssl'

  KEY = '0123456789abcdef01234567890' # 24 characters

  string = "encrypt this"

  des = OpenSSL::Cipher::Cipher.new("des-ede3-cbc")
  des.encrypt # this tells OpenSSL what mode to operate in, here we want to encrypt data
  des.iv = iv = '01234567' # 8 characters, hard-coded for now

  data = des.update(string) + des.final  
  data = iv + data # this way when we randomize our IV it will be available when we need to decrypt the data

  puts data

This should output some crazy string. This isn't the friendliest way to send data, so lets do something about that. The easiest thing to do is Base 64 encode the data, and maybe URI escape it for good measure.

  # be sure to put the following at the top of your code:
  # require "base64"
  # require 'uri'
  # after 'the puts data' in the snippet above do the following:

  data = Base64.encode64(data)
  data = URI.escape(data, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))

  puts data

This should give you the much nicer @MDEyMzQ1Njd6jzSlS73fKSAdqYgRiJie%0A@.

Decryption is similarly easy.

  des = OpenSSL::Cipher::Cipher.new("des-ede3-cbc")
  des.decrypt
  des.key = KEY
  encrypted_data = URI.unescape(data)
  encrypted_data = Base64.decode64(data)
  des.iv =  encrypted_data.slice!(0,8) #This gives us our iv back and removes it from the encrypted data
  
  decrypted = des.update(encrypted_data) + des.final  
    
  puts decrypted

If you are using Rails or have access to ActiveSupport ActiveSupport::SecureRandom is a great way to generate your key and IV. Also be sure to check out ActiveSupport::MessageEncryptor and my string_encryption plugin. I'll probably do a writeup on the string_encryption plugin sometime next week.