13

I would like to encrypt and decrypt one attribute string value of a model by using AES algorithm.

I am wondering in Rails, what is the easiest way to have it? Is there any AES gem library which can be used directly? And how to use it?

Basically I need some guideline on how to apply AES encryption/decryption in Rails app.

------- update -------

I just notice that there is AES gem. If I add this gem into my GemFile, how can I use it in my application for encryption & decryption?

5 Answers 5

8

AFAIK, the aes gem wraps the openssl Ruby standard library to provide a much more simplified interface. It supports only aes-256-cbc, which is 256-bit AES with cipher-block chaining. You would probably add encryption/decryption methods to your models in Rails.

The basic order of operation for encryption would be:

  1. compute an AES symmetric encryption key, 256 bits
  2. optionally compute an initialization vector for use with aes-256-cbc (the aes gem can actually do this for you, so you could skip this step)
  3. encrypt your message, optionally indicating output :format (Base64 by default, otherwise plain Ruby byte-strings) and/or initialization vector :iv

That would be:

key = AES.key
=> "6476b3f5ec6dcaddb637e9c9654aa687"    # key ends up as a 32-char long hex string

iv = AES.iv(:base_64)
=> "PPDRCMsZhumCdEO1Zm05uw=="

enc64 = AES.encrypt("hello, secret world", key, {:iv => iv})
=> "PPDRCMsZhumCdEO1Zm05uw==$b3CCy/1dAMJ2JG5T50igEMGtvo9Ppkla1c9vrKbo+zQ="
# note that the encrypted result is the :iv 
# and Base64-transformed encrypted message
# concatenated with $

You would then decrypt enc64 by passing in the entire :iv + $ + encrypted message string, as well as the AES 256-bit key.

AES.decrypt(enc64, key)
=> "hello, secret world"

Having had some experience using the openssl standard library in Ruby, I can tell you that the documentation in English is sparse, while the Japanese documentation is very good. At any rate, using the openssl API is confusing at best, so if you do not mind limiting yourself to aes-256-cbc, then this aes gem looks to be very helpful.

Mind you, the author does have a caveat with regards to speed. If you find that you require a faster solution, you should have a look at FastAES. FastAES is a C-extension, however, and will require a compiler for your target platform.

Sign up to request clarification or add additional context in comments.

Comments

7

Make a module in your app and include this where you want to call the module method:

  1. You can create a file encrytion_algo.rb under lib folder of rails project
  2. Inside config/application.rb add config.autoload_paths += %W(#{config.root}/lib)

encrytion_algo.rb

require 'openssl'
require 'base64'

module EncrytionAlgo
  def self.included(base)
    base.extend self
  end

  def cipher
    OpenSSL::Cipher::Cipher.new('aes-256-cbc')  # ('aes-256-cbc')
  end

  def cipher_key
    'jabcderfghfhfddd!'
  end

  def decrypt(value)
    c = cipher.decrypt
    c.key = Digest::SHA256.digest(cipher_key)
    c.update(Base64.decode64(value.to_s)) + c.final
  end

  def encrypt(value)
    c = cipher.encrypt
    c.key = Digest::SHA256.digest(cipher_key)
    Base64.encode64(c.update(value.to_s) + c.final)
  end
end
  1. Inside ApplicationController include above file include MyModule

  2. Now you can use encrypt and decrypt method from any controller:

    encrypt("This is a text")

    ==> "h0RGuW5m3Wk9AAspik9ZXVysOcy2IeQrhQDn85mdo5I=%0A"

    decrypt("h0RGuW5m3Wk9AAspik9ZXVysOcy2IeQrhQDn85mdo5I=%0A")

    ==> "This is a text"

Comments

3

I stumbled the same problem and created a simple model concern (rails 5) for that:

require 'openssl'
require 'base64'

module EncryptableModelConcern
  extend ActiveSupport::Concern

  included do
    before_save :encrypt_encryptable_attributes
    after_save :decrypt_encryptable_attributes
    after_find :decrypt_encryptable_attributes
  end

  module ClassMethods
    # Sets the model `@encryptable_attributes` class instance variable.
    # Encryptable attributes are encrypted before saving using `before_save` hook and decrypted using `after_save` and `after_find` hooks.
    # Example:
    # ```
    #   class Board < BaseModel
    #     encryptable_attributes :name, :title, :content
    #   end
    # ```
    def encryptable_attributes(*attrs)
      @encryptable_attributes = attrs
    end

  end

  # Returns the model's `@encryptable_attributes` class instance variable.
  #
  def encryptable_attributes
    self.class.instance_variable_get(:@encryptable_attributes) || []
  end


  # Encryptes the model's encryptable attributes before saving using Rails' `before_save` hook.
  #
  # **Note: Be careful in calling this method manually as it can corrupt the data.**
  def encrypt_encryptable_attributes
    encryptable_attributes.each do |k|
      self[k] = encrypt(self[k])
    end
  end

  # Decrypts the model's encryptable attributes using Rails' `after_save` and `after_find` hooks.
  #
  # **Note: Be careful in calling this method manually as it can corrupt the data.**
  def decrypt_encryptable_attributes
    encryptable_attributes.each do |k|
      self[k] = decrypt(self[k])
    end
  end

  private

    def cipher
      OpenSSL::Cipher::Cipher.new('aes-256-cbc')
    end

    def cipher_key
      Rails.configuration.crypto['key'] # <-- your own key generator here
    end

    def encrypt(value)
      c = cipher.encrypt
      c.key = Digest::SHA256.digest(cipher_key)
      c.iv = iv = c.random_iv
      Base64.encode64(iv) + Base64.encode64(c.update(value.to_s) + c.final)
    end

    def decrypt(value)
      c = cipher.decrypt
      c.key = Digest::SHA256.digest(cipher_key)
      c.iv = Base64.decode64 value.slice!(0,25)
      c.update(Base64.decode64(value.to_s)) + c.final
    end

end

Include it in your model you wish to have encryptable attributes

class Post < ApplicationRecord
  include EncryptableModelConcern
  encryptable_attributes :title, :content
end

Now, your model attributes will be encrypted on before_save and will be decrypted on after_save and on after_find hooks.

Comments

2

You can use OpenSSL library. Create the following functions and then you can use the encryption and decryption methods.

def aes(m,k,t)
  (aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k)
  aes.update(t) << aes.final
end

def encrypt(key, text)
  aes(:encrypt, key, text)
end

def decrypt(key, text)
  aes(:decrypt, key, text)
end

Comments

2

https://github.com/shuber/attr_encrypted

http://ezcrypto.rubyforge.org/

attr_encrypted works well for me - although I've also used Ezcrypto. It acts as a wrapper around the OpenSSL library.

1 Comment

Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.