# Exploring Cryptography Fundamentals in Ruby

Share:

#### Free JavaScript Book!

Write powerful, clean and maintainable JavaScript.~~RRP $11.95~~

Cryptography is used all over the world to protect data from prying eyes. If you want to protect your own data you should be familiar with this topic.

To help you get started, I will show you the main concepts & algorithms that you need to know, and I will include some Ruby examples so you can see the concepts in action.

## Primitive Ciphers

Cryptography was already in use way before computers existed. For example you may have heard about the Caesar cipher.

In the Caesar cipher letters are shifted X positions. The number of positions is the key needed to recover the original message.

It’s trivial to break this cipher using a computer, so what we use today are algorithms that take advantage of some mathematical problems (like prime factorization) that are hard to solve, even for the most powerful computers in the world.

Modern cryptography algorithms can be divided into three groups:

- Symmetric ciphers
- Asymmetric ciphers
- Hash functions

## Symmetric Ciphers

A symmetric algorithm uses one secret key to encode & decode data. This is the kind of algorithm that you would use to protect an important file on your computer.

Note that you should not use this for storing passwords because a symmetric algorithm can easily be reversed if you have the key (and the key needs to be on the system if you want to use the encrypted data without human interaction). What you want for storing passwords is called a ‘hashing function’ (covered later in this article).

Here are some popular symmetric algorithms:

- DES
- Triple DES
- AES
- Blowfish

You can use the `OpenSSL`

library to work with many symmetric ciphers in Ruby.

**Example**:

```
require 'openssl'
cipher = OpenSSL::Cipher.new('aes256')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
encrypted = cipher.update('test') + cipher.final
# You need to save both the IV (initialization vector) + the key
```

If you want to use your own key instead of the one generated by `OpenSSL`

, you need to use a password derivation function like `PBKDF#2`

(Password-Based Key Derivation Function 2). Here is how you can do that:

```
require 'openssl'
cipher = OpenSSL::Cipher.new('aes256')
cipher.encrypt
iv = cipher.random_iv
# Password derivation
salt = OpenSSL::Random.random_bytes(16)
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1('password', salt, 20_000, cipher.key_len)
cipher.key = key
encrypted = cipher.update('test') + cipher.final
```

For decryption you only need to change `cipher.encrypt`

to `cipher.decrypt`

& provide the salt and IV generated during encryption.

## Asymmetric Ciphers

Asymmetric algorithms use two keys: a public key & a private key. The private key is used for decoding & signing messages. The public key is used for encoding & verification.

One practical application is the initial exchange in an SSL connection. The Internet is a hostile network and both the client & the server need to agree on a shared secret key to be able to secure the communication between them.

The problem is that this key can’t be sent in the clear because anyone could just intercept it and use it to spy on the encrypted communication.

The solution is to use an asymmetric cipher, like RSA, or a dedicated algorithm, like DH (Diffie-Hellman Key Exchange). After a secret key has been agreed upon, the secure SSL session can start using a regular symmetric cipher like AES.

You can generate a pair of public/private keys using the `openssl`

command. Like this:

```
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
```

You can also generate a key pair from Ruby:

```
key = OpenSSL::PKey::RSA.new(2048)
File.open('private_key.pem', 'w') { |f| f.write(key.to_pem) }
File.open('public_key.pem', 'w') { |f| f.write(key.public_key.to_pem) }
```

## Hash Functions

A hash function takes in an input and returns a fixed-size output. In a cryptographic hash function, the output is typically represented as a hexadecimal string.

It’s important to note that a hash function is not meant to be reversed, it’s a one-way function. This is what makes it great for storing passwords.

Hash functions can also be used to compare if two files are identical. This is done by hashing the contents of the file & comparing the resulting hashes.

Some common hash functions are:

- MD5
- SHA1
- SHA2
- bcrypt

The Ruby standard library includes the `Digest`

module to help you produce the first 3.

**Here is an example**:

```
require 'digest'
Digest::MD5.hexdigest('test')
# "098f6bcd4621d373cade4e832627b4f6"
Digest::SHA1.hexdigest('test')
# "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
```

The same input will always produce the same output, but it’s also possible for two or more inputs to map to the same output, this is called a ‘hash collision’. Lower bit functions, like MD5, are more susceptible to collisions, so that’s one reason why it’s preferable to use a stronger function.

Here is the documentation for `Digest`

: http://ruby-doc.org/stdlib-2.2.0/libdoc/digest/rdoc/Digest.html

Another option is to use `bcrypt`

(which is what Devise uses):

```
require 'bcrypt'
BCrypt::Password.create("test")
# "$2a$10$zccXimeuNdA083RSDFy7VeVgs538d5XRQurRbqjdEd3h0kU7Q0j2e"
```

The advantage of using `bcrypt`

over something like SHA-1 is that `bcrypt`

is slower, which is a good thing because it makes the resulting hashes stronger against brute-force attacks.

Here is the documentation for the `bcrypt`

gem: https://rubygems.org/gems/bcrypt/

## Adding a Little Salt

One problem with hash functions is that it’s possible to pre-calculate the hash values for many common words & passwords, which makes ‘cracking’ a set of hashes much faster than pure brute-force. In fact, you can download sets of pre-computed hashes called ‘Rainbow tables’.

The solution is to make all your hashes unique using what we call a ‘salt’ value. This value is used like this:

```
sha1(salt + password)
```

And the salt is saved with the hashed password, so it can be used for login attempts. In the case of `bcrypt`

, the salt is generated by the algorithm & included in the hash output.

Note: This is already done for you if you use something like Devise, but it’s still good to know :)

One more thing: make sure to use a unique salt for every password, otherwise you won’t get the full benefits of salting.

## Conclusion

Cryptography is a complex world, but you want to be familiar with it if you deal with important data. One word of warning: don’t try to be clever & invent your own ciphers, you are probably going to shoot yourself in the foot.

### Related Articles

### A Comparison of Ruby Version Managers for macOS

By Daniel Kehoe,

If you're a serious Ruby developer, you'll need an up-to-date version, possibly several. We cover the best Ruby version managers for macOS.

### RubyMine: Code Insight for Ruby and Rails

By Artem Sarkisov,

We answer take a look at the Code Insight feature from RubyMine to help answer the question: "what does RubyMine do that a text editor can't?"

### Understanding the Model-View-Controller (MVC) Architecture in Rails

By Glenn Goodrich,

Rails really takes the idea of separating an application's data, user interface, and control logic to a whole new level. In this article we'll see how.

Jesus is a Ruby developer who likes to help other developers improve their Ruby skills & fill-in the gaps in their education. Check out his blog here.

#### New books out now!

Learn the basics of programming with the web's most popular language - JavaScript

A practical guide to leading radical innovation and growth.