第二部分:如何让 Ruby AES-256-CBC 和 PHP MCRYPT_RIJNDAEL_128 很好地协同工作 [英] Part II: How to make Ruby AES-256-CBC and PHP MCRYPT_RIJNDAEL_128 play well together

查看:30
本文介绍了第二部分:如何让 Ruby AES-256-CBC 和 PHP MCRYPT_RIJNDAEL_128 很好地协同工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是我上一个问题的延续,关于 如何让 Ruby AES-256-CBC 和 PHP MCRYPT_RIJNDAEL_128 一起玩得很好.我现在已经开始工作了,但我仍在努力走向另一个方向.PHP 生成的密码似乎包含提供的所有信息,但我无法获得 Ruby 代码以正确解密它.

This question is a continuation of my last one, regarding How to make Ruby AES-256-CBC and PHP MCRYPT_RIJNDAEL_128 play well together. I've got that working now, but I'm still struggling to go the other direction. The PHP generated cryptogram appears to have all the information that was provided, but I cannot get the Ruby code to decrypt it without error.

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

Here's the PHP code I'm using to generate the cryptogram:

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

RESULT
'JM0OxMINPTnF1vwXdI3XdKI0KlVx210CvpJllFja+GM='

然后尝试在 Ruby 中解密:

Then here's the attempt to decrypt in Ruby:

>> cipher = OpenSSL::Cipher::Cipher.new('aes-128-cbc')
>> cipher.key = Base64.decode64("6sEwMG/aKdBk5Fa2rR6vVw==
")
>> 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

真正令人沮丧的是,可以从加密字符串中获取整个明文.重复以上,但在密码中添加一个无意义的垫子:

What's really frustrating about this is that it's possible to get the entire cleartext out of that encrypted string. Repeating the above, but adding a nonsense pad to the cryptogram:

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

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

In my actual use case the cleartext is structured (a JSON string, since you ask), so I feel comfortable a this point that I could tell use this scheme and detect poorly encrypted input without performing the cipher.final. However, I can't tolerate this sort of kludge in my code, so I'd like to understand how to make the ruby code handle the final block gracefully.

推荐答案

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

The problem is that mcrypt isn't padding the last block, whereas Ruby's OpenSSL binding uses the default OpenSSL padding method, which is PKCS padding. I can't really improve on the description from the OpenSSL documentation:

PKCS 填充通过添加 n 个填充来工作值 n 的字节使总数据长度 a块大小的倍数.填充是总是添加所以如果数据已经块大小 n 的倍数将等于块大小.例如如果块大小是 8 和 11 字节是要加密,然后是 5 个填充字节将添加值 5.

PKCS padding works by adding n padding bytes of value n to make the total length of the data a multiple of the block size. Padding is always added so if the data is already a multiple of the block size n will equal the block size. For example if the block size is 8 and 11 bytes are to be encrypted then 5 padding bytes of value 5 will be added.

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

You'll need to manually add proper padding to the end of the cleartext in PHP before encrypting. To do that, pass your $cleartext through this pkcs5_pad function on the PHP side before you encrypt it (passing 16 as the blocksize).

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

如果你也走另一条路(用 Ruby 加密并用 mcrypt 解密),你必须在解密后去除填充字节.

If you also go the other way (encrypt in Ruby and decrypt with mcrypt), you'll have to strip off the padding bytes after decrypting.

旁注:即使明文已经是块大小的倍数(整个填充块),也必须添加填充的原因是,当您解密时,您知道最后一个块的最后一个字节总是添加的填充量.否则,您将无法区分具有单个填充字节的明文与刚好以值 0x01 结尾的没有填充字节的明文之间的区别.

Side note: The reason you have to add padding even if the cleartext is already a multiple of the blocksize (a whole block of padding), is so that when you are decrypting you know that the last byte of the last block is always the amount of padding added. Otherwise, you couldn't tell the difference between cleartext with a single padding byte and a cleartext with no padding bytes that just happened to end in the value 0x01.

这篇关于第二部分:如何让 Ruby AES-256-CBC 和 PHP MCRYPT_RIJNDAEL_128 很好地协同工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆