Easiest and fastest way to encrypt/decrypt in Rails? - ruby-on-rails

I'm sending a value to the client that I want encrypted (and decrypted if/when received back).
It is not super sensitive data, I just don't want to send it in plain naked text.
I know of ActiveSupport::MessageEncryptor but it seemingly requires too many steps for my basic need. Perhaps I'm overthinking it and there's no heavy computation involved under the hood but I'm intuitively looking for something more like how setting/reading a signed cookie works.
It would be a great advantage if I wouldn't need to include anything more than what comes out of the box with Rails 4.

Why not put it in the session? Then it will be part of the encrypted cookie / in your session store.
You can set the value with session[:message] = "my message" and get it with session[:message].

Using Cipher which comes included in ruby # OpenSSL seems simple.
Try this on irb:
require "openssl"
data = "Very, very confidential data"
cipher = OpenSSL::Cipher::AES.new(128, :CBC)
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
encrypted = cipher.update(data) + cipher.final
decipher = OpenSSL::Cipher::AES.new(128, :CBC)
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(encrypted) + decipher.final
puts data == plain

Related

Rails 4: How to decrypt rails 4 session cookie (Given the session key and secret)

In Rails 3 session cookie can easily decoded with base64 decoding but in Rails 4 cookies are encoded as well as encrypted.
I want to know how to read rails 4 cookie which is encoded as well as encrypted(assuming we know the secret key base).
Thanks,
Rails 4 uses AES-256 to encrypt cookies with the key based on your app's secret_token_base.
Here's the general scheme of decrypting a session cookie:
calc your secret key
Base 64 decode the cookie value
split the decoded cookie value by '--', this will result in two parts, the first part is the encrypted data and the second is the initialization vector used by the encryption scheme. Base 64 decode each part independently.
decrypt the encrypted data by applying AES decryption with the secret key and the initialization vector.
I couldn't find a website to easily decrypt the messages (advice is welcome), programmatically it can be done like this:
secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(app_secret_token, 'encrypted cookie', 1000, 64)
encrypted_message = Base64.decode64(cookie_str)
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
cipher.decrypt
cipher.key = secret
cipher.iv = iv
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
Marshal.load(decrypted_data)
Couple of notes:
This code snippet is almost identical to the actual _decript method implementation in ActiveSupport::MessageEncryptor which is used by the ActionDispatch::Cookies middelware.
This is all very much Rails 4 specific, from the ActionDispatch::Session::CookieJar:
If you only have secret_token set, your cookies will be signed, but not encrypted. This means a user cannot alter their +user_id+ without knowing your app's secret key, but can easily read their +user_id+. This was the default for Rails 3 apps.
If you have secret_key_base set, your cookies will be encrypted. This
goes a step further than signed cookies in that encrypted cookies cannot
be altered or read by users. This is the default starting in Rails 4.

ruby OpenSSL RSA character encoding

I'm building an API in Rails which decrypts one of the POST parameters using RSA. When testing encryption and decryption internally in ruby as seen below, the string literal encrypted (which seems to be UTF8 encoded) does not come out as UTF-8 after decryption. Why is it ASCII-8BIT? What's the recommended way to handle this? Should I call force_encoding("UTF-8") on the decrypted string and say in the API specification that all strings should be UTF8 before encrypting?
#rsa = OpenSSL::PKey::RSA.new(1024)
original = "hej på dig\n"
puts original.encoding.name # => "UTF-8"
ciphertext = #rsa.public_encrypt(original)
decrypted = #rsa.private_decrypt(ciphertext)
puts decrypted.encoding.name # => "ASCII-8BIT"
assert_equal original, decrypted
#expected: hej på dig
#actual: hej p\xC3\xA5 dig
I'm using Ruby 2.1.3 on OSX Yosemite with Rails 4.1.5.
Yep, you got it. You should explicitly turn the UTF-8 to bytes before encryption and back again after decryption. The underlying library simply handles unsigned chars, which are the byte equivalent for C.
If I read the Ruby API's correctly you should be able to do this by using:
string.force_encoding(Encoding::UTF_8)

Encrypt, decrypt using Rails

I saw a while ago the possibility to decrypt and encrypt strings in rails without including any library, but I can't find the blog post.
I want to be able to encrypt and decrypt strings without including anything.
Using the same key has for the everything else in rails, signed cookies for example.
Any ideas?
You mean this one?: ActiveSupport::MessageEncryptor. Here is the way to reuse Rails 5+ on Ruby 2.4+ application's secret:
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31])
encrypted_data = crypt.encrypt_and_sign('my confidental data')
And encrypted data can be decrypted with:
decrypted_back = crypt.decrypt_and_verify(encrypted_data)
The above example uses first 32 characters of Rails app secret as an encryption and signing key, because the default MessageEncryptor cipher aes-256-gcm requires exactly 256 bit key. By convention, during the app creation, Rails generates a secret as a string of 128 hex digits.
Important! Ruby 2.4 upgrade note
Before Ruby 2.4 and Rails 5 there was no key size restriction and it was popular to just past full secret into the encryptor initializer:
# pre-2.4
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base)
Internally the encryption algorithm (AES256GCM provided by OpenSSL) was using only 32 characters from the key, however the signing algorithm (SHA1) was consuming all 128 characters.
Therefore, while upgrading an app from pre-2.4 Ruby, and where the app previously encrypted the data with an unrestricted key size, the MessageEncryptor must get a full secret in the second parameter to avoid ActiveSupport::MessageVerifier::InvalidSignature on the legacy data decryption:
# post-2.4 upgrade
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31], Rails.application.secrets.secret_key_base)
Rails 5 requires that the key be 32 bytes.
Edit to Rails 4 answer that works for Rails 5:
key = SecureRandom.random_bytes(32)
crypt = ActiveSupport::MessageEncryptor.new(key)
encrypted_data = crypt.encrypt_and_sign('my confidental data')
Decrypt:
decrypted_back = crypt.decrypt_and_verify(encrypted_data)
Rails 5 update:
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31])
encrypted_data = crypt.encrypt_and_sign('my confidental data')
Rails 5.x Needs a key of exactly 32 bytes.
To verify a previously signed message with a longer key:
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31], Rails.application.secrets.secret_key_base)
encrypted_data = crypt.encrypt_and_sign('my confidental data')
as described in the documentation and the discussion on this change

blowfish encrypted url in ruby

How to encrypt and decrypt the url using blowfish in ruby?
ex: url= http://localhost:3000?username=vam&paswd=1234&street=hyd&contact=999999999&company=raymarine&city=hyd&state=UP&country=ZP&zip_code=543211
Shamelessly stolen and adapted, this appears to be what you want.
require 'rubygems'
require 'crypt/blowfish'
require 'base64'
plain = "http://localhost:3000?username=vam&paswd=1234&street=hyd&contact=999999999&company=raymarine&city=hyd&state=UP&country=ZP&zip_code=543211"
puts plain
blowfish = Crypt::Blowfish.new("A key up to 56 bytes long")
enc = blowfish.encrypt_string(plain)
mimed = Base64.encode64(enc)
puts mimed
$ ruby blowfish.rb
http://localhost:3000?username=vam&paswd=1234&street=hyd&contact=999999999&company=raymarine&city=hyd&state=UP&country=ZP&zip_code=543211
K9XLp7LmidHZnhQi1i93Lfi1qV4pWFzksnOkNDt/VqyWdZ0OA+K+0soWl7OZ
bNOi17OLIkjhMzHx4Av+h1SL7GP9aletclQGO6XoW2Cge0JweChlj3HXjZT1
fQ6WIqw0zVRaWmqvk1sTqKgvNhy7XPS99RPuX8JdVP87rreklam2LJC97sPh
pu5W9U/lhW7VeRm1HgbI+M0=
Of course, if you need the encrypted contents to serve as an URL, then prepend http://localhost:3000/foo?q= to the encrypted contents, and provide a /foo/q GET handler that can decrypt the string and do whatever it is you need to do with it.
The Crypt library does not work for Ruby 1.9 and later. You can use this gist instead. It requires no gems:
https://gist.github.com/kajic/5686064
url = 'http://localhost:3000?username=vam&paswd=1234&street=hyd&contact=999999999&company=raymarine&city=hyd&state=UP&country=ZP&zip_code=543211'
encrypted_url = Cipher.encrypt_base64('your secret key', url)

DES3 decryption in Ruby on Rails

My RoR server receives a string, that was encrypted in C++ application using des3 with base64 encoding
The cipher object is created so:
cipher = OpenSSL::Cipher::Cipher::new("des3")
cipher.key = key_str
cipher.iv = iv_str
key_str and iv_str: are string representations of key and initialization vector for encryption algorithm. They are the same for RoR and C++ application.
The code on the RoR side is following:
result = ""
result << cipher.update( Base64.decode64(message) )
result << cipher.final
After executing the last line of code, i get an exception
OpenSSL::CipherError (bad decrypt)
What is wrong here ? Any ideas ?
The documentation for OpenSSL::Cipher states:
Make sure to call .encrypt or .decrypt before using any of the following
methods:
[key=, iv=, random_key, random_iv, pkcs5_keyivgen]
In your specific case, omitting the call to cipher.decrypt causes a bad decrypt error, as you've seen.
The following example corrects that problem and exhibits the expected behavior:
require 'openssl'
require 'Base64'
# For testing purposes only!
message = 'MyTestString'
key = 'PasswordPasswordPassword'
iv = '12345678'
# Encrypt plaintext using Triple DES
cipher = OpenSSL::Cipher::Cipher.new("des3")
cipher.encrypt # Call this before setting key or iv
cipher.key = key
cipher.iv = iv
ciphertext = cipher.update(message)
ciphertext << cipher.final
puts "Encrypted \"#{message}\" with \"#{key}\" to:\n\"#{ciphertext}\"\n"
# Base64-encode the ciphertext
encodedCipherText = Base64.encode64(ciphertext)
# Base64-decode the ciphertext and decrypt it
cipher.decrypt
plaintext = cipher.update(Base64.decode64(encodedCipherText))
plaintext << cipher.final
# Print decrypted plaintext; should match original message
puts "Decrypted \"#{ciphertext}\" with \"#{key}\" to:\n\"#{plaintext}\"\n\n"
gem install encryptor
It wraps the standard Ruby OpenSSL library and allows you to use any of its algorithms.
require 'encryptor'
Base64.decode64(message).decrypt(:algorithm => 'des', :key => key, :iv => iv)

Resources