Perl AES vs Ruby AES - ruby

I cannot get Perl and Ruby to agree on CBC AES:
Perl
use Crypt::CBC;
use MIME::Base64::Perl;
my $cipher = Crypt::CBC->new(
-key => 'd2cb415e067c7b13',
-iv => 'e36dc751d0433f05', #random 16chars!!!!!! shold NOT repeat between requests
-cipher => 'OpenSSL::AES', #this is same as Rijndael
-literal_key => 1,
-header => "none",
-keysize => 16
);
$encypted = $cipher->encrypt("a really really long long text has differrent results???");
$base64 = encode_base64($encypted);
print("Ciphertext(b64): $base64");
$de_base64 = decode_base64($base64);
$decrypted = $cipher->decrypt($de_base64);
$c = $cipher->finish;
Ciphertext(b64): qz4eSQaFkQUkDOyJSbZf5W03HoldwtgvTLq0yJFRViKJnytf3PVSCGW2CYDjO+tRqV20oxeB2VPa
7NqN1TDSNQ==
there's a newline after the 2VPa section and another at the end
Ruby
require 'openssl'
require 'digest/sha2'
require 'base64'
message = "a really really long long text has differrent results???"
cipher = OpenSSL::Cipher.new('aes-128-cbc')
# digest the key, iv and hmac_key so we have 16-byte length
# also, it looks more of a funky password
# prepare cipher
cipher.encrypt
cipher.key = aes_key = "d2cb415e067c7b13"
cipher.iv = aes_iv = "e36dc751d0433f05"
encrypted = cipher.update(message)
encrypted << cipher.final()
b64_encoded = Base64.encode64(encrypted).encode('utf-8') #strict_encode64 guarantees no newlines, encode64 is default
puts "AES Key : '#{aes_key}'"
puts "AES IV : '#{aes_iv}'"
puts "Ciphertext(b64): '#{b64_encoded}'"
Ciphertext(b64): 'qz4eSQaFkQUkDOyJSbZf5W03HoldwtgvTLq0yJFRViKJnytf3PVSCGW2CYDj
O+tRqV20oxeB2VPa7NqN1TDSNQ==
'
Note the newlines chars after CYDj and after ==
I've seen the following: Perl & Ruby exchange AES encrypted information, but I'm not using padding=0

Newlines are not significant in Base64. You got exactly the same result from both languages.
While there should be absolutely no reason to do so, you could make the Perl version return the same string as the Ruby version as follows:
$base64 = encode_base64($encypted, '');
$base64 =~ s/\G.{60}\K/\n/sg;

The encode_base64 function takes a second parameter, called "eol" (end of line) which, by default, is '\n'.
The returned encoded string is broken into lines of no more than 76
characters each and it will end with $eol unless it is empty
Try:
$base64 = encode_base64($encypted, '');
instead.

Related

Ruby hmac sha256 hash differs for variable versus literal

HMAC SHA256 hash generated changes when variable is used in the hashing function than using the literal.
I have to concatenate 4 parameters to generate a message string that is hashed using secret key. The concatenated message string generates a different hash than using the value of message as a literal.
require 'base64'
require 'openssl'
securityKey = 'A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc='
content = 'hello'
id = '1000000855'
tsp = '1460852115'
guid = '75c6016eaa1e43b4807ba25232797714'
contentmd5 = Base64.encode64(OpenSSL::Digest::MD5.digest(content))
inputString = id + tsp + guid + contentmd5
puts inputString
#Input String is
#'1000000855146085211575c6016eaa1e43b4807ba25232797714XUFAKrxLKna5cZ2REBfFkg=='
digest = OpenSSL::Digest.new('sha256')
hmac = OpenSSL::HMAC.digest(digest, securityKey, inputString)
securityToken = Base64.encode64(hmac)
puts securityToken
#Hash generated is 7ihOEZNeoJMwjLt84I8WfN5b0VwgYNOg8abPA3nZ0SM=
digest = OpenSSL::Digest.new('sha256')
hmac = OpenSSL::HMAC.digest(digest, securityKey, '1000000855146085211575c6016eaa1e43b4807ba25232797714XUFAKrxLKna5cZ2REBfFkg==')
securityToken = Base64.encode64(hmac)
puts securityToken
#Hash generated is gPNytNGMbhg8b27rklqmEK/9xjNAcOq+7nldzyDL4g0=
looks like Base64.encode64 appends a "\n" to the end of its output so
from docs
encode64(bin) Returns the Base64-encoded
version of bin. This method complies with RFC 2045. Line feeds are
added to every 60 encoded characters.
this
contentmd5 = Base64.encode64(OpenSSL::Digest::MD5.digest(content))
returns
"XUFAKrxLKna5cZ2REBfFkg==\n"
not
"XUFAKrxLKna5cZ2REBfFkg=="
--
you can use strict_encode64 to not include line feeds so:
contentmd5 = Base64.strict_encode64(OpenSSL::Digest::MD5.digest(content))
returns
=> "XUFAKrxLKna5cZ2REBfFkg=="

Ruby to decrypt PHP encrypted text

I'd like to use Ruby to decrypt a string that's encrypted with PHP. The same decryption logic works fine with PHP, but the Ruby code returns garbage.
The working PHP example:
<?php
$_RIJNDAEL_KEY_ = "uUxJIpSKMbOQQdtm6Y4rPEXeE9TAKUns";
$_RIJNDAEL_IV_ = "PiToVoRjwlg8UwxUxQKI4w==";
$ciphertext = 'T353/s48iKzAf61b2dCOnqUApYa4xxjye8he4oAtJHyyCKl8sCbI33hfP6IqOsQZEIWeQBCsvy97xwJMPD8RwLG4J0wgX9Ihlti1vMar+5nrLrCR4lAfZcoJopoBt1JVnDAojLW+y0S0y5c4GCdB8YrHzj4jv70dg3yX8DxlAWE=';
$content = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $_RIJNDAEL_KEY_, base64_decode($ciphertext), MCRYPT_MODE_ECB, $_RIJNDAEL_IV_);
echo $content . "\n";
?>
The not-working Ruby example:
require "openssl"
require "digest"
require "uri"
require "base64"
data = Base64.decode64("T353/s48iKzAf61b2dCOnqUApYa4xxjye8he4oAtJHyyCKl8sCbI33hfP6IqOsQZEIWeQBCsvy97xwJMPD8RwLG4J0wgX9Ihlti1vMar+5nrLrCR4lAfZcoJopoBt1JVnDAojLW+y0S0y5c4GCdB8YrHzj4jv70dg3yX8DxlAWE=")
key = Base64.decode64('uUxJIpSKMbOQQdtm6Y4rPEXeE9TAKUns')
iv = Base64.decode64('PiToVoRjwlg8UwxUxQKI4w==')
aes = OpenSSL::Cipher.new('AES-128-ECB')
aes.decrypt
aes.padding = 0
aes.key = key
aes.iv = iv
plain = aes.update(data) + aes.final
puts plain
Could anyone shed some light ? Thanks.
Everything here is correct. But.
It is necessary to use AES-256-ECB in your example.
Here the code which is runnable:
require "openssl"
require "digest"
require "uri"
require "base64"
data = 'T353/s48iKzAf61b2dCOnqUApYa4xxjye8he4oAtJHyyCKl8sCbI33hfP6IqOsQZEIWeQBCsvy97xwJMPD8RwLG4J0wgX9Ihlti1vMar+5nrLrCR4lAfZcoJopoBt1JVnDAojLW+y0S0y5c4GCdB8YrHzj4jv70dg3yX8DxlAWE='
key = "uUxJIpSKMbOQQdtm6Y4rPEXeE9TAKUns"
#iv = 'PiToVoRjwlg8UwxUxQKI4w=='
aes = OpenSSL::Cipher::AES.new(256, :ECB)
# Without padding checksum|4033315172 will be truncated!
aes.padding = 0
aes.decrypt
aes.key = key
#aes.iv = iv
plain = aes.update(data.unpack('m')[0]) + aes.final
After decrypt I've got the following:
=> "date_add|2015-01-28 01:36:17\xC2\xA4id_lang|1\xC2\xA4id_currency|1\xC2\xA4id_guest|12165\xC2\xA4id_connections|10668\xC2\xA4checksum|4033315172\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Next. Without zero-padding in this example, last character of checksum will be truncated. If possible, don't use EBC mode. Here is why.
And finally, you don't need IV in this example.
Good luck!

AES-256-CFB cuts off message when using `random_iv`

I have two scripts for encrypting and decrypting messages using AES.
Here's encrypt.rb:
require 'openssl'
require 'base64'
require 'digest'
KEY = 'sekrit_key'
MESSAGE = "My Name is Rabbit and I don't know anything!"
cipher = OpenSSL::Cipher::AES256.new(:CFB)
cipher.encrypt
cipher.key = KEY
# hexdigest the IV to make sure encode64 doesn't fuck up
iv = Digest::MD5.hexdigest(cipher.random_iv)
cipher.iv = iv
encrypted = cipher.update(secret_message)+cipher.final
puts Base64.urlsafe_encode64(iv+'|'+encrypted.encode)
... and decrypt.rb:
require 'openssl'
require 'base64'
KEY = 'sekrit_key'
encrypted_message = STDIN.read.strip
parts = Base64.urlsafe_decode64(encrypted_message ).split('|')
iv = parts[0]
encrypted = parts[1]
decipher = OpenSSL::Cipher::AES256.new(:CFB)
decipher.decrypt
decipher.key = KEY
decipher.iv = iv
message = decipher.update(encrypted)+decipher.final
if message.eql?("My Name is Rabbit and I don't know anything!")
print '.'
else
puts
puts encrypted_message
puts message
end
When I now continuosly run both scripts, My output is cut off quite often!
$ while true; do ruby encrypt.rb | ruby decrypt.rb; done
....
YmZhNDg2ODJjNGZiOGIzZTcyMzAwYzMxZWUwNWI0Y2V8w2aJk930EL3gh3rfQsd2B3xZKy5wjoCzlZoYHBgmv6m51ZwAWQHGtCJoNRg=
My Name is Rabbi
..
YjMwNDQxOGRjMjg4NGEzOThmM2IwNGFiZDBiZTQxZGZ8OfLyjGQGKV3PPUpCvfL08IDuk7M7d3w7fj6F5Rql94jkRdwaCuuMfedqtFk=
My Name is Rabbit and
..
OWUxYzFlZWU5MTc4NGZjYWYxYzZiOGEwOTBjOGMxYzJ8g7I4X_Dt6K9ufByMhLBGlpoYCv8vlR0lTBqP-zS647tmmFh81rXdR8T-UkM=
My Name i
....
# and so on
Why are so many of my messages cut off?
Update: When using a fixed IV in encrypt.rb (eg. substitute cipher.random_iv with KEY) instead of a randomly generated, the problem does not occur.
The problem stems from the fact that you're treating binary data as strings. iv as well as encrypted.encode are binary and you're concatenating them with "|" (a string). Both iv as well as the message may contain the pipe character which will cause problems when splitting. In general, it's best to base64 both parts separately.
Here's working code:
encrypt.rb
require 'openssl'
require 'base64'
require 'digest'
KEY = 'sekrit_key123456' * 2
MESSAGE = "My Name is Rabbit and I don't know anything!"
cipher = OpenSSL::Cipher::AES256.new(:CFB)
cipher.encrypt
cipher.key = KEY
iv = cipher.random_iv
encrypted = cipher.update(MESSAGE)+cipher.final
puts Base64.strict_encode64(iv)+'|'+Base64.strict_encode64(encrypted.encode)
decrypt.rb
require 'openssl'
require 'base64'
KEY = 'sekrit_key123456' * 2 # key needs to be the right length
encrypted_message = STDIN.read.strip
parts = encrypted_message.split('|')
iv = Base64.strict_decode64(parts[0])
encrypted = Base64.strict_decode64(parts[1])
decipher = OpenSSL::Cipher::AES256.new(:CFB)
decipher.decrypt
decipher.key = KEY
decipher.iv = iv
message = decipher.update(encrypted)+decipher.final
if message.eql?("My Name is Rabbit and I don't know anything!")
print '.'
else
puts
puts encrypted_message
puts message
end
Also notice that random_iv already assigns iv to cipher, so you don't need to (see source under http://apidock.com/ruby/v1_9_3_125/OpenSSL/Cipher/random_iv ).

Difference in blowfish encryption between perl and ruby

Why is there a difference in blowfish encryption between Crypt::CBC (perl) and OpenSSL (ruby)?
Perl
use Crypt::CBC;
my $cipher = Crypt::CBC->new( -key => 'length32length32length32length32', -cipher => 'Blowfish' );
my $ciphertext = $cipher->encrypt_hex('test');
# ciphertext is 53616c7465645f5f409c8b8eb353823c06d9b50537c92e19
Ruby
require "rubygems"
require "openssl"
cipher = OpenSSL::Cipher::Cipher.new("bf-cbc")
cipher.encrypt
cipher.key = "length32length32length32length32"
result = cipher.update("test") << cipher.final
ciphertext = result.unpack("H*").first
# ciphertext is 16f99115a09e0464
Crypt::CBC seems to be prepending Salted__ to the output by default. Can you explain what is going on that is so different between these? Is there a way to make OpenSSL behave in a similar way to Crypt::CBC?
Crypt::CBC (perl) uses its own method to randomize the salt and initialization vector. Plus in the case of Blowfish it uses a key length of 56 as mentioned above.
Using the perl code from your example:
Perl
use Crypt::CBC;
my $cipher = Crypt::CBC->new( -key => 'length32length32length32length32', -cipher => 'Blowfish' );
my $ciphertext = $cipher->encrypt_hex('test');
# 53616c7465645f5f409c8b8eb353823c06d9b50537c92e19
To decode this using ruby (OpenSSL) requires a little tweaking to extract the key and initialization vector:
Ruby
require 'openssl'
# Hex string to decode(from above)
string = '53616c7465645f5f409c8b8eb353823c06d9b50537c92e19'
# Pack Hex
string = [string].pack('H*')
# Some Config
pass = 'length32length32length32length32'
key_len = 56;
iv_len = 8;
desired_len = key_len + iv_len;
salt_re = /^Salted__(.{8})/
#Extract salt
salt = salt_re.match(string)
salt = salt.captures[0]
data = '';
d = '';
while (data.length < desired_len)
d = Digest::MD5::digest("#{d}#{pass}#{salt}");
data << d;
end
#Now you have extracted your key and initialization vector
key = data.slice(0..key_len-1)
iv = data.slice(key_len .. -1)
# Trim string of salt
string = string[16..-1]
cipher = OpenSSL::Cipher::Cipher.new "bf-cbc"
cipher.decrypt
cipher.key_len = key_len
cipher.key = key
cipher.iv = iv
puts cipher.update(string) << cipher.final
# test
Turns out the blowfish key size defaults are different between these two. OpenSSL defaults to 16, Crypt::Blowfish defaults to 56.
You can override key size in Crypt::CBC by specifying -keysize => n, but unfortunately there does not appear to be a way to override the key size in OpenSSL.
The Openssl library defaults to 16 byte Blowfish key sizes, so for
compatibility with Openssl you may wish to set -keysize=>16
http://metacpan.org/pod/Crypt::CBC
Perl (specify keysize)
my $cipher = Crypt::CBC->new(
-key => 'length32length32length32length32',
-keysize => 16,
-cipher => 'Blowfish'
);

Ruby blowfish difference in the last digits

I have some testdata key/text/encrypted from an API provider and am now trying to yield the same encrypted result with the function below, but my result diverts from the provided one in the last 16 of 241 digits. Do you have an idea, what the reason may be?
I ensured, that 'bf-ecb' is the right mode, and experimented with url-encoding, but so far without success.
require 'openssl'
def encrypt(key, data)
cipher = OpenSSL::Cipher::Cipher.new('bf-ecb').send(:encrypt)
cipher.key = key
result = cipher.update(data) << cipher.final
hexed = ''
result.each_byte { |c| hexed << '%02x' % c }
hexed.upcase
end
UPDATE
Also trying to decrypt the example result results in an OpenSSL::Cipher::CipherError "bad decrypt"
If your last 16 digits (=128 bits) are incorrect then it is likely there is a problem with the last block. Probably this is a problem with padding, your encryption is using one form of padding while your decryption is expecting a different padding. I suggest that you explicitly specify the padding at both sides. PKCS5 or PKCS7 are the usual choice. Faulty padding will also explain the "bad decrypt" error message.
It was indeed a problem with the padding. I worked around it with deactivating it and implementing it by myself.
So far it works.
This is how it looks like:
require 'openssl'
def encrypt(key,data)
cipher = OpenSSL::Cipher::Cipher.new "bf-ecb"
cipher.padding = 0
cipher.key = key
cipher.encrypt
enhex(cipher.update padd data)
end
def decrypt(key,data,len)
cipher = OpenSSL::Cipher::Cipher.new "bf-ecb"
cipher.padding = 0
cipher.key = key
cipher.decrypt
(cipher.update dehex(data)).slice(0,len)
end
def enhex(data)
hexed = ''
data.each_byte { |c| hexed << '%02x' % c }
hexed.upcase
end
def dehex(data)
data.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
end
def padd(data)
data + " "*(8 - (data.length % 8))
end
You can simply do the blowfish encryption with ecb cipher mode just like this:
def blowfish_encrypt(key,data)
cipher = OpenSSL::Cipher::Cipher.new("bf-ecb").send :encrypt
cipher.key = key
cipher.update(data) << cipher.final
end
And you don't need to care about padding in this case.

Resources