I'm generating data to send from a Ruby stack to a PHP stack. I'm using the OpenSSL::Cipher library on the Ruby side and the 'mcrypt' library in PHP. When I encrypt using 'aes-256-cbc' (256-bit block size) in Ruby I need to use MCRYPT_RIJNDAEL_128 (128-bit block size) in PHP to decrypt it. I suspect the Ruby code that is broken, because the cipher.iv_len is 16; I believe it should be 32:
>> cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
=> #<OpenSSL::Cipher::Cipher:0x3067c5c>
>> cipher.key_len
=> 16
>> cipher.iv_len
=> 16
>> cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
=> #<OpenSSL::Cipher::Cipher:0x306de18>
>> cipher.key_len
=> 32
>> cipher.iv_len
=> 16
So here's my test. On the Ruby side, first I generate the key and iv:
>> cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
>> cipher.encrypt
>> iv = cipher.random_iv
>> iv64 = [iv].pack("m").strip
=> "vCkaypm5tPmtP3TF7aWrug=="
>> key = cipher.random_key
>> key64 = [key].pack("m").strip
=> "RIvFgoi9xZaHS/0Bp0J9WDRyND6Z7jrd3btiAfcQ8Y0="
Then I use those keys to do the encryption:
>> plain_data = "Hi, Don, this is a string."
>> cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
>> cipher.encrypt
>> cipher.key = Base64.decode64(key64)
>> cipher.iv = Base64.decode64(iv64)
>> encrypted_data = cipher.update(plain_data)
>> encrypted_data << cipher.final
>> crypt64 = [encrypted_data].pack("m").strip
=> "5gfC/kJcnAV2fJI0haxnLcdraIKWgtu54UoznVxf8K0="
Here's the PHP decryption:
$ruby_crypt = "5gfC/kJcnAV2fJI0haxnLcdraIKWgtu54UoznVxf8K0=";
$encrypted_data = base64_decode($ruby_crypt);
$key = base64_decode("RIvFgoi9xZaHS/0Bp0J9WDRyND6Z7jrd3btiAfcQ8Y0=");
$iv = base64_decode("vCkaypm5tPmtP3TF7aWrug==");
$result = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted_data, MCRYPT_MODE_CBC, $iv);
$unencrypt = rtrim($result, "\x00..\x1F");
print "\nUnencrypted token:\n'$unencrypt'\n";
RESULT:
Unencrypted token:
'Hi, Don, this is a string.'
I'd prefer to use the longer block size. Clearly I'm misunderstanding the APIs. Help?
AES-256 is 340 billion-billion-billion-billion times harder to brute force than AES-128. To put this into perspective, the universe is 14 billion years old. It is therefore safe to say that even at its lower bit sizes, AES is highly resistant to brute force attacks from conventional computers.
AES 256 is virtually impenetrable using brute-force methods. While a 56-bit DES key can be cracked in less than a day, AES would take billions of years to break using current computing technology. Hackers would be foolish to even attempt this type of attack. Nevertheless, no encryption system is entirely secure.
AES-GCM is a more secure cipher than AES-CBC, because AES-CBC, operates by XOR'ing (eXclusive OR) each block with the previous block and cannot be written in parallel. This affects performance due to the complex mathematics involved requiring serial encryption.
CBC (short for cipher-block chaining) is a AES block cipher mode that trumps the ECB mode in hiding away patterns in the plaintext. CBC mode achieves this by XOR-ing the first plaintext block (B1) with an initialization vector before encrypting it.
I wrote an example that somebody else may find explanatory of the discussion above:
$ cat publisher.rb
#!/usr/bin/env ruby
require 'openssl'
require 'base64'
key = '7fc4d85e2e4193b842bb0541de51a497'
cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
cipher.encrypt()
iv = cipher.random_iv
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
cipher.encrypt()
cipher.key = key
cipher.iv = iv
crypt = cipher.update('This is my text')
crypt << cipher.final()
puts [Base64.encode64(crypt).strip(), Base64.encode64(iv).strip()].join('|')
$ cat consumer.php
$key256 = '7fc4d85e2e4193b842bb0541de51a497';
$fd = fopen("php://stdin", "r");
$tokens = '';
while (!feof($fd))
$tokens .= fread($fd, 1024);
fclose($fd);
$tokens = explode('|', trim($tokens));
$crypt = $tokens[0];
$iv = $tokens[1];
$crypttext = base64_decode($crypt);
$iv = base64_decode($iv);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key256, $crypttext, MCRYPT_MODE_CBC, $iv);
print $decrypted ."\n";
To test it, from command line try:
$ ruby publisher.rb | php consumer.php
This is my text
I don't know PHP, but reading through related questions on the sidebar, I see Converting Ruby AES256 decrypt function to PHP. This includes a reference to this page, pointing out that the 128 in MCRYPT_RIJNDAEL_128
refers to the block size of the encryption, not the key size. You'll notice that the key size that you've passed between ruby and PHP is 256 bits in both cases. In other words, this seems to be the expected behavior, and you are using the larger key already.
#!/usr/bin/ruby
require 'base64'
puts((Base64.decode64("RIvFgoi9xZaHS/0Bp0J9WDRyND6Z7jrd3btiAfcQ8Y0=").length * 8).to_s)
HTH
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With