Playing with symmetric encryption algorithms in Ruby

In the times where data privacy is a headline every week it is good for software developers to have an understanding of encryption and programming.

For Rubyists the standard library of Ruby offers the OpenSSL::Cipher class to use symmetric encryption in Ruby code. Symmetric encryption means that both users who want to communicate are using the same key. This is different to asymmetric encryption where both users do not share the same key.

Basics of symmetric encryption algorithms

> require 'openssl'
=> true

Lets see what kind of symmetric algorithms the Ruby OpenSSL library offers.

> puts OpenSSL::Cipher.ciphers

As we can see Ruby offers a few algorithms in different varieties. Most ciphers are given using the following format:

<name>-<key length>-<mode>

Lets take a few as examples:

  • Data Encryption Standard (DES) is a widely used encryption algorithm. Today it is not considered as secure and should not be used anymore.
  • Rivest Cipher/Ron’s Code (RC) is a different symmetric algorithm. It is also considered as unsecure and should not be used anymore. (see this)
  • Advanced Encryption Standard (AES) is a symmetric encryption algorithm which is widely used today and counts as secure

Since AES is considered as secure we will use it as our algorithm for now.

AES comes with different key bit lengths: 128, 192, 256. The higher the key length is the harder it is to brute force it (try all possible combinations). Eventhough AES128 counts as secure today computers are getting more and more powerful. To ensure that our code will stay secure for a longer period we will use AES256 for our following examples.

The different modes of encryption algorithms is out of the scope of this blog post so we will not take a deeper look at them. Just note that encryption algorithms can use different modes to encrypt data.

Using OpenSSL::Cipher

> cipher = OpenSSL::Cipher::AES256.new :CBC

After obtaining a cipher instance we have to set it to decryption or encryption mode:

> cipher.encrypt

Normally a cipher needs a key and an initialization vector to encrypt and decrypt a message. The initialization vector should be a random number. After generation it can be transmitted openly and does not need to be kept privately. The algorithm needs an initialization vector (IV) and the key for its encryption process.

OpenSSL::Cipher provides a method for generating an IV and should be used to save some hassle.

> iv = cipher.random_iv

This line does not only generate a random IV and sets it to iv but also sets the initialization vector for this cipher instance.

The key is where things get actually interesting. There are different ways to generate keys. One way of course would be to let the computer generate a random key.

But a user might want to set the key by himself. A problem is that you cannot take a normal word like SecretPassword as your key since AES256 requires your key to be 256 bit long.

> cipher.key = "SecretPassword"
OpenSSL::Cipher::CipherError: key length too short

By executing the following line

> "string".encoding
#<Encoding:UTF-8>

we can see that a string in my current irb instance is using UTF-8 as encoding which means that one character equals 8 bits.

Simple math: 256 / 8 = 32. This means that we need a 32 character string as our key for the AES256 algorithm. Ever had a 32 character long password?

Now lets take a key which is long enough and encrypt some data.

> cipher.key = "ThisPasswordIsReallyHardToGuess!"

Now we have our IV and key set. Now we can encrypt some data.

> cipher_text = cipher.update('This is a secret message') + cipher.final

cipher.update takes a message and encrypts it using the IV and key given before. cipher.final adds the final part of the cipher text to the end of the encrypted message. This is needed if encrypted data is transmitted so the decrypter knows when the end of the message is reached.

Decryption

> decipher = OpenSSL::Cipher::AES256.new :CBC
> decipher.decrypt
> decipher.iv = iv # previously saved
> decipher.key = 'ThisPasswordIsReallyHardToGuess!'
> plain_text = decipher.update(cipher_text) + decipher.final
=> "This is a secret message"

Different Keys

Here we could use a hash. A hash takes something (like a file, a string, …) and turns it into a fixed number of characters. This fixed number is specified by the hash algorithm which is being used.

In our case we want a 256 bit key so we need a hash function which creates 256 bit hashes. For this we can use the SHA-256 hash algorithm.

In Ruby we can use the SHA-256 hash algorithm using the digest library.

> key = Digest::SHA256.digest 'SecretPassword'

This line turns SecretPassword into a 256 bit hash which we can then use as a key for our AES encryption.

I hope this little post could give some insights in the world of symmetric encryption. We could not go deep into many aspects of symmetric encryption since it is out of scope for a simple blog post. But if anybody is interested feel free to leave a comment and I can write about a certain topic more specifically.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store