What is the ruby (cypher / pbkdf2_hmac incantations of the openssl gem) equivalent to encrypting with a command like this:
echo 'Top secret text' | openssl enc -base64 -e -aes-256-cbc -salt -pass pass:'mypassword' -pbkdf2 -p
I'm trying to use the ruby openssl gem to do AES-256-CBC encryption of a plain text string. I found quite a few examples of how to do this, but then I want to be able to give my friend the unix openssl
command necessary to decrypt it at the other end, which means I need to use the same settings and string concatenation approach that the unix command does.
I figured out a pair of unix commands to encrypt and then decrypt. I'm using PBKDF2 key derivation. I have wrapped these commands in ruby code doing system call-outs, so my encrypt_openssl_system_call
is successfully the inverse of decrypt
.
I want my open_ssl_gem_encrypt
method to also be decryptable using decrypt
... and at the moment it nearly works because I've cheated and passed in the @salt_used
, @key_used
, and @iv_used
. Instead I will need to figure out the right way to call OpenSSL::KDF.pbkdf2_hmac
(or similar?) to do key derivation.
But with matching those values I suppose I hoped to see it encrypt to the exact same cypher text, or at least come out with the right plain text. Weirdly it nearly works, producing the plain text but with 14 random characters at the start.
require 'openssl'
require 'base64'
def password
"mypassword"
end
def encrypt_openssl_system_call(plain_text)
command = "echo '#{plain_text}' | openssl enc -base64 -e -aes-256-cbc -salt -pass pass:'#{password}' -pbkdf2 -p"
puts command
output = `#{command}`
puts output
raise(output) unless $?.success?
# Parse the actual used salt key and iv from this output
rows = output.split("\n")
@salt_used = [rows[0].split("salt=").last].pack('H*')
@key_used = [rows[1].split("key=").last].pack('H*')
@iv_used = [rows[2].split("iv =").last].pack('H*')
encrypted = rows.last
encrypted.rstrip!
encrypted
end
def decrypt(encrypted)
command = "echo '#{encrypted}' | openssl enc -base64 -d -aes-256-cbc -salt -pass pass:'#{password}' -pbkdf2"
puts command
output = `#{command}`
raise output unless $?.success?
output.rstrip
rescue RuntimeError => e
puts ">>> ERROR #{e.message}"
puts e.backtrace
e.message
end
def open_ssl_gem_encrypt(plain_text)
# Key derivation (PBKDF2) not working yet
# salt = 'Salted__' + OpenSSL::Random.random_bytes(8)
# key = OpenSSL::KDF.pbkdf2_hmac(password, salt: salt, iterations: 10000, length: 32, hash: "sha1")
salt = 'Salted__' + @salt_used
key = @key_used
cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt
cipher.key = key
iv = @iv_used # cipher.random_iv
cipher.iv = iv
# Concatenate bits in hopefully the right order?!
encrypted = salt
encrypted << iv
encrypted << cipher.update(plain_text)
encrypted << cipher.final
Base64.encode64(encrypted).gsub(/\n/, '')
end
PLAIN_TEXT = "Top secret text"
puts "encrypt_openssl_system_call(#{PLAIN_TEXT.dump})"
encrypted = encrypt_openssl_system_call(PLAIN_TEXT)
puts "openssl command produced '#{encrypted}'"
puts "\n"
puts "decrypt('#{encrypted}')"
decrypted = decrypt(encrypted)
puts "openssl command decryption produced '#{decrypted}'"
puts "So far so good!" if decrypted==PLAIN_TEXT
puts "\n"
puts "open_ssl_gem_encrypt(#{PLAIN_TEXT.dump})"
encrypted_b = open_ssl_gem_encrypt(PLAIN_TEXT)
puts "OpenSSL gem produced '#{encrypted_b}'"
puts "\n"
puts "decrypt(#{encrypted_b.dump})"
decrypted_b = decrypt(encrypted_b)
puts "openssl command decryption produced '#{decrypted_b}'"
Output:
encrypt_openssl_system_call("Top secret text")
echo 'Top secret text' | openssl enc -base64 -e -aes-256-cbc -salt -pass pass:'mypassword' -pbkdf2 -p
salt=27BD7552C3308BBA
key=B99FCDC5F9296AD2B1488E49B8CD29EDF0D15E13C408B1EEB11A2050F6403E94
iv =1294745B0A06C42939283E51EAE29E5E
U2FsdGVkX18nvXVSwzCLuhZeGj9c+fKLG+nDaitgeRahxKftT20Rax2sYFpjiO3h
openssl command produced 'U2FsdGVkX18nvXVSwzCLuhZeGj9c+fKLG+nDaitgeRahxKftT20Rax2sYFpjiO3h'
decrypt('U2FsdGVkX18nvXVSwzCLuhZeGj9c+fKLG+nDaitgeRahxKftT20Rax2sYFpjiO3h')
echo 'U2FsdGVkX18nvXVSwzCLuhZeGj9c+fKLG+nDaitgeRahxKftT20Rax2sYFpjiO3h' | openssl enc -base64 -d -aes-256-cbc -salt -pass pass:'mypassword' -pbkdf2
openssl command decryption produced 'Top secret text'
So far so good!
open_ssl_gem_encrypt("Top secret text")
OpenSSL gem produced 'U2FsdGVkX18nvXVSwzCLuhKUdFsKBsQpOSg+Uerinl4DIK8yljJ2aHCR+8m9Yrq3'
decrypt("U2FsdGVkX18nvXVSwzCLuhKUdFsKBsQpOSg+Uerinl4DIK8yljJ2aHCR+8m9Yrq3")
echo 'U2FsdGVkX18nvXVSwzCLuhKUdFsKBsQpOSg+Uerinl4DIK8yljJ2aHCR+8m9Yrq3' | openssl enc -base64 -d -aes-256-cbc -salt -pass pass:'mypassword' -pbkdf2
openssl command decryption produced ' |�S��p��C��H�PTop secret text'
Partial success shows that I must surely be putting the string together in the right order at least!?
openssl gem version is 2.1.2
openssl version is "LibreSSL 3.3.6"