第二部分:如何使Ruby AES-256-CBC和PHP MCRYPT_RIJNDAEL_128一起发挥得很好

don*_*ndo 10 php ruby openssl aes mcrypt

这个问题是我最后一个问题的延续,关于如何使Ruby AES-256-CBC和PHP MCRYPT_RIJNDAEL_128一起发挥得很好.我现在已经开始工作,但我仍然在努力朝着另一个方向努力.PHP生成的密码似乎具有提供的所有信息,但是我无法使用Ruby代码来解密它而不会出现错误.

这是我用来生成密码的PHP代码:

$cleartext = "Who's the clever boy?";
$key = base64_decode("6sEwMG/aKdBk5Fa2rR6vVw==\n");
$iv = base64_decode("vCkaypm5tPmtP3TF7aWrug==");
$cryptogram = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CBC, $iv);
$result = base64_encode($cryptogram);
print "\n'$result'\n";

RESULT
'JM0OxMINPTnF1vwXdI3XdKI0KlVx210CvpJllFja+GM='
Run Code Online (Sandbox Code Playgroud)

然后这是尝试在Ruby中解密:

>> cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
>> cipher.key = Base64.decode64("6sEwMG/aKdBk5Fa2rR6vVw==\n")
>> cipher.iv = Base64.decode64("vCkaypm5tPmtP3TF7aWrug==")
>> cryptogram = Base64.decode64('JM0OxMINPTnF1vwXdI3XdKI0KlVx210CvpJllFja+GM=')
>> cleartext = cipher.update(cryptogram)
=> "Who's the clever"
>> cleartext << cipher.final
OpenSSL::Cipher::CipherError: bad decrypt
 from (irb):100:in `final'
 from (irb):100
Run Code Online (Sandbox Code Playgroud)

令人沮丧的是,可以从加密字符串中获取整个明文.重复上述内容,但在密码中添加一个无意义的填充:

  >> cleartext = cipher.update(cryptogram + 'pad')
  => "Who's the clever boy?\000\000\000\000\000\000\000\000\000\000\000"
  >> cleartext << cipher.final
  OpenSSL::Cipher::CipherError: bad decrypt
   from (irb):119:in `final'
   from (irb):119
Run Code Online (Sandbox Code Playgroud)

在我的实际用例中,明文是结构化的(一个JSON字符串,因为你问),所以我觉得这一点我可以告诉使用这个方案并检测加密不良的输入而不执行cipher.final.但是,我不能容忍我的代码中的这种kludge,所以我想了解如何使ruby代码优雅地处理最后一个块.

caf*_*caf 15

问题是mcrypt没有填充最后一个块,而Ruby的OpenSSL绑定使用默认的OpenSSL填充方法,即PKCS填充.我无法真正改进OpenSSL文档中的描述:

PKCS填充通过添加n个值为n的填充字节来工作,以使数据的总长度为块大小的倍数.总是添加填充,因此如果数据已经是块大小的倍数,则n将等于块大小.例如,如果块大小为8并且要加密11个字节,则将添加5个值为5的填充字节.

在加密之前,您需要在PHP的明文末尾手动添加适当的填充.为此,请在加密之前在PHP端传递$cleartextpkcs5_pad函数(16作为块大小传递).

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

如果你也采用另一种方式(在Ruby中加密并用mcrypt解密),你必须在解密后去除填充字节.

旁注:即使明文已经是块大小的一个(整个填充块),你必须添加填充的原因是,当你解密时,你知道最后一个块的最后一个字节总是数量填充添加.否则,你无法区分具有单个填充字节的明文和没有填充字节的明文,恰好在该值中结束0x01.