我可以在ruby中复制PHP's AES加密的确切行为吗?


Can I replicate the exact behaviour of PHP's AES encryption in ruby?

我正在用Ruby on Rails重新构建一个PHP web应用程序,并且非常希望避免强制所有现有用户重置他们的加密密码。PHP网站使用mcrypt_encrypt与AES-256-ECB,我不能为我的生命使用ruby的OpenSSL得到相同的密文。我也不能解密它们(这在原则上是好的),因为实际存储在用户数据库中的是AES密文的MD5哈希。

我已经阅读了前面这些密切相关的问题和非常有用的答案:

  • 如何让Ruby AES-256-CBC和PHP MCRYPT_RIJNDAEL_128很好地发挥
  • 第二部分:如何使Ruby AES-256-CBC和PHP MCRYPT_RIJNDAEL_128很好地协同工作

包括那里引用的页面,如果我理解正确的话,PHP和ruby实现使用不同的填充方法。既然我不得不忍受PHP的工作方式,有没有办法在ruby/OpenSSL上强制使用相同的填充方法?我使用的是ruby 1.9.2-p180。

下面是PHP的示例代码:

$salt = "12345678901234567890123456789012";
$plain = "password";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$cipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $plain, MCRYPT_MODE_ECB, $iv);
echo md5($cipher);

输出:6337137fd88148250fd135a43dbeb84a

和ruby中的

require 'openssl'
salt = "12345678901234567890123456789012"
plain = "password";
c = OpenSSL::Cipher.new("AES-256-ECB")
c.encrypt
c.key = salt
cipher = c.update(plain)
cipher << c.final
puts Digest::MD5.hexdigest(cipher)

输出:18dee36145c07ab83452aefe2590c391

实际上不是一般的openssl解决方案,但也许对您来说有一个工作示例是可以的。

require 'mcrypt'
require 'openssl'
plaintext = 'password'
puts plaintext
key = '12345678901234567890123456789012'
enc = Mcrypt.new(:rijndael_256, :ecb, key, nil, :zeros)
encrypted = enc.encrypt(plaintext)
puts Digest::MD5.hexdigest(encrypted)

我使用了一个额外的宝石(红宝石-mcrypt)。似乎是openssl的一个问题。实际上,问题似乎是Openssl不支持零填充,而是使用无填充或default-openssl-padding。由于在php中使用零填充,因此在ruby中也必须使用零填充。

我的机器上php脚本的输出:

[~/test] ➔ php5 t.php 
6337137fd88148250fd135a43dbeb84a

,对于ruby脚本:

[~/test] ➔ ruby t2.rb 
password
6337137fd88148250fd135a43dbeb84a

和我的ruby版本:

[~/test] ➔ ruby -version
ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux]

如果键大小在PHP端不是标准的,您需要用零填充键到下一个有效的键大小,以使ruby端像这样工作:

php_encrypted = string_encoded_with_php_mcrypt
key = "longerthan16butnot24".to_a.pack('a24')
enc = Mcrypt.new(:rijndael_256, :ecb, key, nil, :zeros)
enc.decrypt(php_encrypted)

在这种情况下,下一个有效的密钥长度是24。

For:rijndael_256有效的密钥长度为:16,24,32

你可以得到更多关于算法的信息:

Mcrypt.algorithm_info(:rijndael_256

如果您可以使用其他加密方法,您可以尝试TEA块加密。我采用了跨Ruby, JS, ActionScript的方法。它也应该与PHP一起工作。Github的repo在这里