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

我正在生成要从Ruby堆栈发送到PHP堆栈的数据。 我在Ruby端使用OpenSSL :: Cipher库,在PHP使用’mcrypt’库。 当我在Ruby中使用’aes-256-cbc’(256位块大小)进行加密时,我需要在PHP中使用MCRYPT_RIJNDAEL_128(128位块大小)来解密它。 我怀疑Ruby代码被破坏了,因为cipher.iv_len是16; 我相信它应该是32:

>> cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc') => # >> cipher.key_len => 16 >> cipher.iv_len => 16 >> cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc') => # >> cipher.key_len => 32 >> cipher.iv_len => 16 

所以这是我的考试。 在Ruby方面,首先我生成密钥和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=" 

然后我使用这些密钥进行加密:

 >> 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 <> crypt64 = [encrypted_data].pack("m").strip => "5gfC/kJcnAV2fJI0haxnLcdraIKWgtu54UoznVxf8K0=" 

这是PHP解密:

 $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.' 

我更喜欢使用更长的块大小。 显然我误解了API。 救命?

我不知道PHP,但是阅读侧栏上的相关问题,我看到将Ruby AES256解密函数转换为PHP 。 这包括对此页面的引用,指出MCRYPT_RIJNDAEL_128中的128指的是加密的块大小,而不是密钥大小。 您会注意到,在两种情况下,您在ruby和PHP之间传递的密钥大小为256位。 换句话说,这似乎是预期的行为,并且您已经使用了更大的密钥。

 #!/usr/bin/ruby require 'base64' puts((Base64.decode64("RIvFgoi9xZaHS/0Bp0J9WDRyND6Z7jrd3btiAfcQ8Y0=").length * 8).to_s) 

HTH

我写了一个例子,其他人可能会找到上述讨论的解释:

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

要测试它,请从命令行尝试:

$ ruby​​ publisher.rb | php consumer.php

这是我的文字

我遇到了麻烦,因为PHP使用的密码小于8个字符。 在这种情况下,需要添加0,以使其与PHP兼容:

mcrypt-encrypt手册页“密钥

用于加密数据的密钥。 如果它小于所需的密钥大小,则用’\ 0’填充。 最好不要将ASCII字符串用于键。 http://php.net/manual/en/function.mcrypt-encrypt.php建议使用mhash函数从字符串创建一个键。“

 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() 

我来给你看一些代码。

PHP代码:

 $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代码:

 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== 

正如您所看到的,我在ruby中使用AES-128,因为private_key的大小为16.因此,如果private_key的大小为32,则必须使用AES-256。

公式: size_of_private_key * 8。