Question

I want to use rijndael aes128 for encryption in ruby. I have this code:

cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
cipher.encrypt
cipher.key = 'abcdef0123456789abcdef0123456789'
cipher.iv = '0000000000000000'
encrypted =   cipher.update('2~1~000024~0910~20130723092446~T~00002000~USD~F~375019001012120~0~0~00000000000~')
encrypted << cipher.final

which is not working. But using this PHP function:

<?php 
 function hex2bin($hex_string) 
  {
     return pack('H*', $hex_string);
  } 
 $data_to_encrypt = '2~1~000024~0910~20130723092446~T~00002000~USD~F~375019001012120~0~0~00000000000~';
  $key = 'abcdef0123456789abcdef0123456789'; 
  $iv = '0000000000000000'; 
  $key = hex2bin($key);
  $iv = hex2bin($iv); 
  $data_encrypted = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data_to_encrypt, MCRYPT_MODE_CBC, $iv));
  echo "Data encrypted: ".strtoupper($data_encrypted)."<br/>"; echo "Length: ".strlen($data_encrypted)."<br/>";
  ?>

I got my desired output:

0D5835AFEBEE04C6DC2421538DB7C38A1283970EB31F21A47D2E3CC623D29EF0461279C7ACF93B031BE2B69CE45C9339554957F29EF609F019EEC975983A03B537622D7E0F196BE148F1C7CBB88E602A

How can I get my Ruby code to produce the same output?

Was it helpful?

Solution

The problem is that mcrypt isn't padding the last block, whereas Ruby's OpenSSL binding uses the default OpenSSL padding method, which is PKCS padding. I can't really improve on the description from the OpenSSL documentation:

PKCS padding works by adding n padding bytes of value n to make the total length of the data a multiple of the block size. Padding is always added so if the data is already a multiple of the block size n will equal the block size. For example if the block size is 8 and 11 bytes are to be encrypted then 5 padding bytes of value 5 will be added. You'll need to manually add proper padding to the end of the cleartext in PHP before encrypting. To do that, pass your $cleartext through this pkcs5_pad function on the PHP side before you encrypt it (passing 16 as the blocksize).

function pkcs5_pad ($text, $blocksize)
{
   $pad = $blocksize - (strlen($text) % $blocksize);
   return $text . str_repeat(chr($pad), $pad);
}

If you also go the other way (encrypt in Ruby and decrypt with mcrypt), you'll have to strip off the padding bytes after decrypting.

Side note: The reason you have to add padding even if the cleartext is already a multiple of the blocksize (a whole block of padding), is so that when you are decrypting you know that the last byte of the last block is always the amount of padding added. Otherwise, you couldn't tell the difference between cleartext with a single padding byte and a cleartext with no padding bytes that just happened to end in the value 0x01.

OTHER TIPS

require 'openssl'

cleartext = '2~1~000024~0910~20130723092446~T~00002000~USD~F~375019001012120~0~0~00000000000~'
key = 'abcdef0123456789abcdef0123456789'
iv = '0000000000000000'

cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
cipher.encrypt
cipher.padding = 0
cipher.key = [key].pack('H*')
cipher.iv = [iv].pack('H*')
encrypted = cipher.update(cleartext)
encrypted << cipher.final
puts encrypted.unpack('H*').first.upcase

Output:

0D5835AFEBEE04C6DC2421538DB7C38A1283970EB31F21A47D2E3CC623D29EF0461279C7ACF93B031BE2B69CE45C9339554957F29EF609F019EEC975983A03B537622D7E0F196BE148F1C7CBB88E602A
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top