I have the following encryption/decryption using OpenSSL (under Linux in my example):
$ echo test | openssl des -a -K 79616d7379616d73 -iv 1234567890ABCDEF
+ax5NT+Pjh0=
$ echo +ax5NT+Pjh0= | openssl des -a -d -K 79616d7379616d73 -iv 1234567890ABCDEF
test
All good. I need to translate it in Ruby code. As far as I did:
#!/usr/bin/env ruby
require 'openssl'
key = "\x79\x61\x6d\x73\x79\x61\x6d\x73"
iv = "\x12\x34\x56\x78\x90\xAB\xCD\xEF"
todecode = "+ax5NT+Pjh0="
def decode(encryptedString, key, iv)
decrypt = OpenSSL::Cipher::Cipher.new('des-cbc')
decrypt.decrypt
decrypt.key = key
decrypt.iv = iv
decrypt.update(encryptedString) + decrypt.final
end
decoded = decode(todecode, key, iv)
puts decoded
It throws me the following error:
decode.rb:14:in `final': wrong final block length (OpenSSL::Cipher::CipherError)`
What am I doing wrong? Did I select the wrong encryption or wrong use of key/iv?
Seems I forgot to base64_decode the string.
todecode = Base64::decode64("+ax5NT+Pjh0=")
Related
I have encrypted some plaintext using the openssl command line utility using a password:
echo -n "foo" | openssl enc -e -base64 -rc4 -nosalt -pass pass:secretkey
This returns:
ryW2
How can I decrypt this value using Ruby?
Finally with reference of Above in Ruby
Encode
def encrypt(str, key)
cipher = OpenSSL::Cipher.new('RC4')
cipher.key = OpenSSL::Digest.digest('md5', key)
cipher.decrypt
Base64.encode64(cipher.update(str) + cipher.final)
end
encrypt('This is Test', 'Test')
=> "JGmpM+ewGA79qaZH\n"
Decode
def decrypt(encrypted_string, key)
decipher = OpenSSL::Cipher.new('RC4')
decipher.key = OpenSSL::Digest.digest('md5', key)
decipher.decrypt
decipher.update(Base64.decode64(encrypted_string)) + decipher.final
end
decrypt("JGmpM+ewGA79qaZH\n", "Test")
=> "This is Test"
Here is the command you would need to enter to encrypt the string foo into some ciphertext:
echo -n "foo" | openssl enc -e -base64 -rc4 -nosalt -pass pass:secretkey -p
And this returns:
key=610A2EE688CDA9E724885E23CD2CFDEE
ryW2
ryW2 is your base64 encoded ciphertext.
We have added -p so we can see the key that is used for encryption.
-p is required because OpenSSL uses an internal function EVP_BytesToKey to convert the passphrase to a key and there is no equivalent Ruby method to do this. Accordingly, you'll have to use this key value directly in Ruby when encrypting or decrypting instead of the passphrase.
Decryption is just as straight-forward:
echo "ryW2" | openssl enc -d -base64 -rc4 -nosalt -pass pass:secretkey
This returns:
foo
Given all this, here is how you would encrypt this string using Ruby to get the same value:
require 'openssl'
require 'base64'
plaintext = 'foo'
cipher = OpenSSL::Cipher.new('rc4')
cipher.encrypt
# Use the key that was generated by EVP_BytesToKey
key = '610A2EE688CDA9E724885E23CD2CFDEE'
# Convert the key to a byte string
key_bytes = key.scan(/../).map { |x| x.hex.chr }.join
cipher.key = key_bytes
ciphertext = cipher.update(plaintext) + cipher.final
base64 = Base64.strict_encode64(ciphertext)
This returns the same value as OpenSSL on the command line:
ryW2
Decryption is fairly straightforward:
decipher = OpenSSL::Cipher.new('rc4')
decipher.decrypt
decipher.key = key_bytes
decrypted = decipher.update(ciphertext) + decipher.final
This returns the original plaintext:
foo
Ultimately, encryption and decryption is fairly straightforward as long as you know what EVP_BytesToKey will return for a given pass. You can read more about EVP_BytesToKey here:
How to extract the IV vector generated by encrypt method from encrypted_strings
How do I refactor OpenSSL pkcs5_keyivgen in ruby?
https://github.com/crypto-browserify/EVP_BytesToKey
https://www.openssl.org/docs/man1.0.2/man3/EVP_BytesToKey.html
I wasn't happy that there is no built-in way to do this in Ruby. My other answer to this question is a functional workaround but it requires some time-consuming steps that I'm not crazy about. So I wrote a gem called evp_bytes_to_key that'll do it for you!
First install the gem:
gem install evp_bytes_to_key
Then generate your ciphertext:
echo -n "foo" | openssl enc -e -base64 -rc4 -nosalt -pass pass:secretkey
This returns:
ryW2
Then decrypt it in Ruby:
require 'evp_bytes_to_key'
require 'openssl'
require 'base64'
decipher = OpenSSL::Cipher.new('rc4')
decipher.decrypt
decipher.key = EvpBytesToKey::Key.new('secretkey', nil, 128, 0).key
ciphertext = Base64.strict_decode64('ryW2')
plaintext = decipher.update(ciphertext) + decipher.final
This returns the original plaintext:
foo
More examples for using this gem can be found in the README.
I created a file using: echo "test" | openssl enc -aes256 -salt -a -k test
Decryption code:
def decrypt_string(b64_text, decryption_key)
encrypted_text = Base64.decode64(b64_text)
_header = encrypted_text[0, 8]
salt = encrypted_text[8, 8]
payload = encrypted_text[16..-1]
decipher = OpenSSL::Cipher.new('aes-256-cbc').decrypt
d_1 = OpenSSL::Digest::MD5.new(decryption_key + salt).digest
d_2 = OpenSSL::Digest::MD5.new(d_1 + decryption_key + salt).digest
decipher.key = (d_1 + d_2)
decipher.iv = OpenSSL::Digest::MD5.new(d_2 + decryption_key + salt).digest
decipher.update(payload) + decipher.final
end
But when I call: decrypt_string('U2FsdGVkX1+5Sar5DYmbDtze7yvHKdq/ZuZIVnkImDc=', 'test')
I get OpenSSL::Cipher::CipherError: bad decrypt
I can not use external gems and do not want to make an OS call. How do you use the built in openssl library?
If I run your code it does not return an OpenSSL::Cipher::CipherError (bad decrypt) error, so I think the b64_text variable is different to the one you posted.
Can you try running the following in your IRB:
str = `echo "test" | openssl enc -aes256 -salt -a -k test`
decrypt_string(str, 'test')
Does the error still persist?
I'm trying to encrypt data with OpenSSL library in Ruby using passphrase.
Ruby code looks like this:
require('openssl')
require('base64')
cipher = OpenSSL::Cipher.new ('AES-256-CBC')
cipher.encrypt
cipher.iv = iv = cipher.random_iv
pwd = 'topsecret'
salt = OpenSSL::Random.random_bytes 8
iter = 10000
key_len = cipher.key_len
digest = OpenSSL::Digest::SHA256.new
key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key
puts "salt=#{salt.unpack('H*')[0]}"
puts "key=#{key.unpack('H*')[0]}"
puts "iv=#{iv.unpack('H*')[0]}"
encrypted = cipher.update 'my data to encrypt'
encrypted << cipher.final
puts "encrypted=#{Base64.strict_encode64(encrypted)}"
# it returns:
# salt=1332e5603cbc018a
# key=11a168cf01556a5ee3e22e049f0e65d3adcd75f39e32c7d19aec32a0ccb40d93
# iv=35a08f2d3e719abbee78a0f4fe47c938
# encrypted=E3Ag6cRL2R+xytgw01i6tKSFpV7s7bKoiiWvPA1FYxM=
Unfortunately, when I try to decrypt this, I get error bad magic number:
$ echo "E3Ag6cRL2R+xytgw01i6tKSFpV7s7bKoiiWvPA1FYxM=" | openssl enc -aes-256-cbc -base64 -d -p -pass pass:topsecret
bad magic number
However, when I try this in terminal by running openssl enc command, it works:
$ echo 'my data' | openssl enc -aes-256-cbc -base64 -p -pass pass:topsecret
salt=8135837A305553F2
key=8B4373ABD786BAC107F4112640E95E920C77C017FCEC18E1BD919CED42F0298E
iv =910637CE50FADF27D944B7A8DD239E6D
U2FsdGVkX1+BNYN6MFVT8oWa5P/oxZFwzMk1DRCSSGg=
$ echo "U2FsdGVkX1+BNYN6MFVT8oWa5P/oxZFwzMk1DRCSSGg=" | openssl enc - aes-256-cbc -d -p -base64 -pass pass:topsecret
salt=8135837A305553F2
key=8B4373ABD786BAC107F4112640E95E920C77C017FCEC18E1BD919CED42F0298E
iv =910637CE50FADF27D944B7A8DD239E6D
my data
I think I tried every possible combination of generating key/IV from passphrase but I get error every time. Is anyone able to spot where is the problem with this way? I've spent entire day on this.
Given the following script
require "openssl"
require "securerandom"
key = SecureRandom.random_bytes(32)
iv = SecureRandom.random_bytes(16)
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
aes.encrypt
aes.key = key
aes.iv = iv
crypted = aes.update("a"*50)+aes.final
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
aes.decrypt
aes.key = key
aes.iv = iv
puts aes.update(crypted)+aes.final
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
aes.decrypt
aes.key = key
puts aes.update(crypted)+aes.final
I get the following output (e.g.):
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
���y��f{�K~:y��aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Does I need to perform these CBC-style XOR operations, block size caring etc. on my own? I cannot find any example which is not simply calling the update function passing the string which should be decrypted. I already tried to add .dup to all string assignments to prevent inplace edit issues. What is going on here?
Environment: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
The IV's purpose is to prevent discovery of the key by analyzing multiple messages containing the same data. It is not intended to obscure the information if you have the key. Without the proper IV, the first block of the ciphertext will be incorrect, but subsequent blocks will decrypt properly, as you found. The IV protects the key, not the data.
You get the same thing if you perform similar encryption via the openssl command line tools (here I replace the 8-byte IV embedded in the encrypted ciphertext with 8 space character):
echo "Ruby openssl lib - Why does AES-256-CBC decrypting gives readable data without the correct IV?" | \
openssl enc -aes-256-cbc -salt -pass pass:password | \
perl -pi -e "s/^(.{8})(.{8})/\1 /" | \
openssl enc -d -aes-256-cbc -salt -pass pass:password
▒▒▒▒▒▒t▒8q▒g] -▒▒▒▒▒▒7<s AES-256-CBC decrypting gives readable data without the correct IV?
Note: Using OpenSSL for symmetric encryption of text.
I made a Ruby script to test OpenSSL and I found I'm getting different results. The key, iv, and ciphers are identical, so I would expect the results to be identical. But they are not. Here's my script:
require 'openssl'
require 'base64'
key = "00000000000000000000000000000000"
iv = "00000000000000000000000000000000"
### OPENSSL Command Line ###
puts "*** OpenSSL Command Line ***"
print "Encrypted via Command Line: "
string = `printf %s \"Hello\" | openssl enc -aes-128-cbc -K #{key} -iv #{iv} -base64`
puts string
puts string.inspect
print "Decrypted Data is: "
puts `printf %s \"BC2+AQJ6ZQx0al3GXba+EQ==\n\" | openssl enc -d -aes-128-cbc -K #{key} - iv #{iv} -base64`
puts "\n"
### Ruby OpenSSL Library ###
puts "*** OpenSSL Ruby Library ***"
cipher = OpenSSL::Cipher.new('aes-128-cbc').encrypt
cipher.padding = 1
cipher.key = key
cipher.iv = iv
encrypted_data = cipher.update("Hello")
encrypted_data << cipher.final
encrypted_data = Base64.encode64(encrypted_data)
puts "Encrypted via Ruby is: #{encrypted_data}"
puts encrypted_data.inspect
decipher = OpenSSL::Cipher.new('aes-128-cbc').decrypt
decipher.key = key
decipher.iv = iv
data = decipher.update(Base64.decode64(encrypted_data))
data << decipher.final
puts "Decrypted Data: #{data}"
The results are:
*** OpenSSL Command Line ***
Encrypted via Command Line: BC2+AQJ6ZQx0al3GXba+EQ==
"BC2+AQJ6ZQx0al3GXba+EQ==\n"
Decrypted Data is: Hello
*** OpenSSL Ruby Library ***
Encrypted via Ruby is: ZkeNEgsUXi1J7ps6kCQxdQ==
"ZkeNEgsUXi1J7ps6kCQxdQ==\n"
Decrypted Data: Hello
Just a curious result. Any idea what's causing the data to be different?
Just a guess, without knowing Ruby's OpenSSL interface:
You give the keys and initialization vector to the command line OpenSSL in hexadecimal encoding. E.g. your key and initialization vector are 0x000....
I suppose your Ruby library takes the key and initialization vector as binary data, e.g you are actually passing a key and initialization vector consisting of 0x30303030... (assuming ASCII or anything compatible to it) instead of 0x00000....
Pack them to a binary(Hex) sequence will fix it.
Test on my machine(Mac ox 10.11.1 ruby-2.2.3).
cipher.key = ["#{key}"].pack('H*')
cipher.iv = ["#{iv}"].pack('H*')
ruby Packs
decipher.key = ["#{key}"].pack('H*')
decipher.iv = ["#{iv}"].pack('H*')