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?
Make a module in your app and include this where you want to call the module method:
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
Inside ApplicationController
include above file include MyModule
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"
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.
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:
aes-256-cbc
(the aes
gem can actually do this for you, so you could skip this step):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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With