dongyu3659
2009-12-07 20:33
浏览 161
已采纳

如何使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 2009-12-07 21:17
    已采纳

    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

    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • dqg95034 2010-12-06 10:46

    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

    评论
    解决 无用
    打赏 举报
  • douxie9471 2013-09-05 15:24

    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()
    
    评论
    解决 无用
    打赏 举报
  • duanjia1865 2017-07-11 07:40

    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.

    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题