为什么在PHP中加密的东西与在Ruby中加密的相同字符串不匹配?

时间:2012-02-08 01:15:16

标签: php ruby openssl mcrypt

以下是我的要求:

我需要使用AES加密(包括随机iv)在PHP中加密字符串,对其进行Base64编码,然后对其进行URL编码,以便将其作为URL参数传递。

我试图在PHP和Ruby中获得相同的结果,但我无法使其工作。

这是我的PHP代码:

function encryptData($data,$iv){
    $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
    $iv_size = mcrypt_enc_get_iv_size($cipher);
    if (mcrypt_generic_init($cipher, 'g6zys8dlvvut6b1omxc5w15gnfad3jhb', $iv) != -1){
        $cipherText = mcrypt_generic($cipher,$data );
        mcrypt_generic_deinit($cipher);
        return $cipherText;
    }
    else {
        return false;
    }
}
$data = 'Mary had a little lamb';
$iv = '96b88a5f0b9efb43';
$crypted_base64 = base64_encode(encryptData($data, $iv));

这是我的Ruby代码:

module AESCrypt
  def AESCrypt.encrypt(data, key, iv)
    aes = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
    aes.encrypt
    aes.key = key
    aes.iv = iv
    aes.update(data) + aes.final      
  end
end

plaintext = "Mary had a little lamb"
iv = "96b88a5f0b9efb43"
@crypted = AESCrypt::encrypt(plaintext, "g6zys8dlvvut6b1omxc5w15gnfad3jhb", iv)
@crypted_base64 = Base64.encode64(@crypted)
@crypted_base64_url = CGI.escape(@crypted_base64)

令人愤怒的是,两个代码示例都生成相似的但不是相同的哈希值。例如,上面的代码生成(base64编码,而不是URL编码):

PHP: /aRCGgLBMOOAarjjtfTW2Qg2OtbPDLhx3KmgfgMzDJU=

Ruby: /aRCGgLBMOOAarjjtfTW2XIZhZ9VjBx8PdozxSL8IE0=

有谁能解释我在这里做错了什么?此外,我更容易(因为我是一个Ruby人,通常不是PHP)修复Ruby代码而不是PHP代码。因此,如果您想在Ruby中提供与PHP配对良好的解决方案,我将非常感激。

哦,而且,在制作中,iv实际上是随机的,但是对于这个例子,我将它设置为永久相同,以便可以比较输出。

修改

感谢Eugen Rieck的回答,我找到了解决方案。 Ruby填充块,但PHP没有,你必须手动完成。将PHP代码更改为以下内容,您将获得上述Ruby代码可以轻松解密的加密字符串:

$iv = '96b88a5f0b9efb43';
$data = 'Mary had a little lamb';

function encryptData($data,$iv){
    $key = 'g6zys8dlvvut6b1omxc5w15gnfad3jhb';
    $padded_data = pkcs5_pad($data);
    $cryptogram = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $padded_data, MCRYPT_MODE_CBC, $iv);
    return $cryptogram;
}

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

1 个答案:

答案 0 :(得分:8)

事实证明,这很容易:填充是罪魁祸首。

AES是块密码,因此它适用于固定大小的块。这意味着,最后一个块将全部填充,并且,您知道,标准的好处是,有很多可供选择。 PHP使用零填充,您必须查看AESCrypt以找出Ruby使用的内容。

各种JS AES库和PHP存在同样的问题。我们总是在做自己的填充,因为这已经让我陷入血红色的愤怒很多次。

当然这解释了,为什么第一部分(携带信息)是相同的,而第二部分(携带填充)是不同的。