Reputation: 3062
I'm running into issues with data I'm trying to store encrypted in my Rails 4 app. I've been looking at lots of questions related to this and there are many hints, feels like I'm almost there, but somehow it just won't decrypt the data. These are the two methods involved:
def encrypt( val, pwd_name )
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
cipher.encrypt
iv = cipher.random_iv
pwd = encryptor_pwds[ pwd_name ]
salt = OpenSSL::Random.random_bytes 16
iter = 20000
key_len = cipher.key_len
digest = OpenSSL::Digest::SHA256.new
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key
encrypted = cipher.update val
encrypted << cipher.final
encrypted = Base64.encode64( encrypted ).encode('utf-8')
iv = Base64.encode64( iv ).encode('utf-8')
salt = Base64.encode64( salt ).encode('utf-8')
return { str: encrypted, iv: iv, salt: salt }
end
def decrypt( str, iv, salt, pwd_name )
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
cipher.decrypt
str = Base64.decode64( str )
iv = Base64.decode64( iv )
salt = Base64.decode64( salt )
cipher.iv = iv
pwd = encryptor_pwds[ pwd_name ]
salt = salt
iter = 20000
key_len = cipher.key_len
digest = OpenSSL::Digest::SHA256.new
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key
decrypted = cipher.update str
decrypted << cipher.final
return decrypted
end
And the I modified the read/writes to for example this:
def email=(email)
unless email.nil?
set = encrypt(email, :email)
write_attribute( :email, set[:str] )
write_attribute( :email_iv, set[:iv] )
write_attribute( :email_salt, set[:salt] )
else
write_attribute( :email, nil )
end
end
def email
if read_attribute( :email ).nil? then read_attribute( :email ) else decrypt( read_attribute( :email ), read_attribute( :email_iv ), read_attribute( :email_salt ), :email ) end
end
But when I try to read from it, it throws this OpenSSL::Cipher::CipherError: bad decrypt
that more people seem to run into.
Any help would be much appreciated!
Upvotes: 1
Views: 1308
Reputation: 3062
This was a bit tricky to figure out, but the problem was not with my encryption logic but with a filter in the Devise gem. Devise lowercases email addresses before save by default, but because I'm encrypting and encoding them, I'm saving case sensitive UTF8 strings to the db. Lowercased, decoding the strings back to ASCII resulted in different results than the thing before the save, and that made decryption impossible.
Now if anyone runs into this, look for the case_insensitive_keys
setting in config/initializers/devise.rb
, and make sure it doesn't contain the keys you are saving encrypted. Keep in mind that if you do that you'd better either lowercase the emails yourself or validate and make uppercase characters in emails prohibited, something like that.
Upvotes: 3