dongyu3659
dongyu3659
2009-12-07 20:33

如何使Ruby AES-256-CBC和PHP MCRYPT_RIJNDAEL_128一起发挥得很好

已采纳

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 "
Unencrypted token:
'$unencrypt'
";

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?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

4条回答

  • douyan8413 douyan8413 12年前

    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

    点赞 评论 复制链接分享
  • duanjia1865 duanjia1865 4年前

    Let me show you some code.

    PHP code:

    $privateKey = "1234567890123456"; # the size is 16.
    $data = "hello";
    $iv = "0123456789012345";
    
    $result = mcrypt_encrypt(
      MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv
    )
    
    $base64str = base64_encode($result);
    $base64str = str_replace("+", "-",  $base64str);
    $base64str = str_replace("/","_",  $base64str);
    
    # => f-WffBXnf122NcVBUZ6Rlg==
    

    Ruby code:

    require 'base64'
    require 'openssl'
    
    private_key = "1234567890123456"
    data = "hello"
    iv = "0123456789012345"
    
    cipher = OpenSSL::Cipher::AES.new(128, :CBC) 
    cipher.encrypt
    
    cipher.padding = 0 # we must disable padding in ruby.
    cipher.key = private_key
    cipher.iv = iv
    block_size = cipher.block_size
    
    # Add padding by yourself.
    data = data + "\0" * (block_size - data.bytesize % block_size)
    result = cipher.update(data) + cipher.final
    
    Base64.urlsafe_encode64(result)
    # ==> f-WffBXnf122NcVBUZ6Rlg==
    

    As you can see I am using AES-128 in ruby because the size of private_key is 16. So you have to use AES-256 if the size of your private_key is 32.

    Formula: size_of_private_key * 8.

    点赞 评论 复制链接分享
  • douxie9471 douxie9471 8年前

    I had troubles because the PHP was using a password smaller than 8 characters. In this case one needs to add the 0, to make it compatible with PHP:

    mcrypt-encrypt manual page "key

    The key with which the data will be encrypted. If it's smaller than the required keysize, it is padded with '\0'. It is better not to use ASCII strings for keys. http://php.net/manual/en/function.mcrypt-encrypt.php It is recommended to use the mhash functions to create a key from a string."

    require 'openssl'
    cipher = OpenSSL::Cipher.new('DES-ECB')
    cipher.encrypt
    key =  'passwrd'[0...7].ljust(8, 0.chr)  #Pad the key smaller than 8 chars
    cipher.key = key
    encrypted = cipher.update('33')
    encrypted << cipher.final
    dec = Base64.encode64(encrypted).strip()
    
    点赞 评论 复制链接分享
  • dqg95034 dqg95034 11年前

    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 ."
    ";
    

    To test it, from command line try:

    $ ruby publisher.rb | php consumer.php

    This is my text

    点赞 评论 复制链接分享

相关推荐