将 Crypto++ AES 加密移植到 PHP 的 mcrypt 时密钥大小不正确
Posted
技术标签:
【中文标题】将 Crypto++ AES 加密移植到 PHP 的 mcrypt 时密钥大小不正确【英文标题】:Incorrect key size when porting Crypto++ AES encryption to PHP's mcrypt 【发布时间】:2011-05-02 10:37:17 【问题描述】:之前我设法将一些 C++ CryptoPP Rijndael_128 CBC 代码移植到 MCrypt php,但现在我遇到了 CFB 模式的问题。 C++ 和 PHP 结果不匹配(第一个字节匹配,但这可能是巧合,其他一切都不匹配)。通过一些诊断,PHP 的 mcrypt 似乎没有正确设置密钥长度?
这是 C++(为简单起见,删除了诊断和杂项):
CFB_Mode<AES>::Encryption encryptor(g_encrypt_key, AES::DEFAULT_KEYLENGTH, g_encrypt_iv);
StringSource ss( sInput.c_str(), true,
new StreamTransformationFilter( encryptor,
new HexEncoder( new StringSink( sEncryptedOut ) )
));
这是 PHP:
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CFB, '')
mcrypt_generic_init($cipher, $g_encrypt_key, $g_encrypt_iv);
$sEncryptedOutput = mcrypt_generic( $cipher, $sInput);
mcrypt_generic_deinit($cipher);
mcrypt_module_close($cipher);
g_encrypt_key
和 g_encrypt_iv
都是 16 字节长,并且字节匹配 C++ 和 PHP 版本。对于 PHP 版本,它是由字节构造的二进制字符串(是的,我已经检查过它们是相同的)。
我添加了对 PHP 版本的调用,以检查 $cipher
的块大小、密钥大小等。
块大小和iv大小都是16;支持的密钥大小报告为 16、24 和 32 - 均符合预期。
我认为问题出在哪里,密钥大小被报告为 32 字节。查看 mcrypt 文档,设置密钥大小的唯一方法是提供所需大小的密钥。但我传递了一个 16 字节的密钥!那么为什么它报告存在一个 32 字节的密钥呢?如果 CFB 模式必须使用 32 字节的密钥,那么 CryptoPP 为什么会接受它呢?解决办法是什么?我可以强制 PHP 使用已提供的 16 字节密钥吗?还是我缺少一个参数,它在 CryptoPP 中的默认设置与在 MCrypt 中的设置不同?
我正在使用 CFB 模式,因为我想最小化生成的加密数据的长度。填充将引入的几个字节在此应用程序中很重要。
我需要能够在 C++ 中加密/解密,但只能在 PHP 中加密。 AES 对我的应用程序来说可能是多余的——我需要的最低要求是“对字节进行良好的加扰”,这样数据中各个字节的功能就不明显了。
【问题讨论】:
我注意到 mcrypt 的 OFB 和 NOFB 都报告了 32 字节的密钥大小(与 CFB 一样)。但是,如果我对 Crypto++ 使用 OFB_Mode 并为 mcrypt 使用 NOFB 模式,我会得到匹配的结果。这应该足以满足我的迫切需求,所以现在问题变成了一个学术问题:为什么密钥大小不同,为什么它们会产生不同的结果? mcrypt 已损坏 - 提交错误报告。多年来一直如此,我不明白为什么这是一个反复出现的问题。多年来,我一直在回答 Crypto++ 的这类 mycrypt 问题。 【参考方案1】:已经有一段时间了,但几年前我在使用 CFB 的 mcrypt 和 openSSL 方面遇到了一些类似的问题。最后,我发现 mcrypt 在 CFB 模式下使用了与 openssl 不同的默认反馈链大小。也就是说,我相信CFB中的openSSL AES128使用了128位的块大小和反馈大小,而mcrypt使用了128位的块大小和8位的反馈大小。我无法证实这一点,当时只是根据阅读一些旧论坛帖子的猜测。不管该理论的真实性如何,我并不是唯一一个或第一个遇到这个特定问题的人。
我的解决方案是自己使用 nOFB。根据PHP mcrypt library referenceMCRYPT_MODE_NOFB
强制反馈链等于算法的块大小,在这种情况下,AES128 (Rijndael) 的 128 位块/反馈与 mcrypt 模块的manpage 对 nOFB 的状态相匹配.这很好,因为我发现所有内容都说 nOFB 反馈与块大小同步。因此,对于 AES128,nOFB 中的 mcrypt 和 OpenSSL 现在都是 128 位密钥/iv/块/反馈大小,并且一切正常。
就 PHP 报告 256 位密钥大小(32 字节)而言,返回当前密码算法密钥大小的函数实际上返回了最大密钥大小,这在文档中没有明确说明。我知道这一点是因为我现在一直用于各种项目的小班与 openSSL 和 CBC 或 nOFB 中的任何其他 AES 库完美配合。如果 mcrypt 用额外的 128 位空字符串或其他内容填充我的 128 位(16 字符)密钥,则情况不会如此,并且无论如何在技术上都不正确。
不是一个很好的答案,但我得到的最好答案是基于几年前对密码学的一次非常业余的尝试。
【讨论】:
所以不只是我。我想知道反馈大小,但找不到查询或更改它的方法。 PHP 的 mcrypt 库实际上有两种查询密钥大小的方法。是的,我找到了您描述的方法。另一个仅基于算法和块模式 - 而不是提供的密钥。换句话说,事后看来,两者都不受提供的密钥的影响。 午餐时我很好奇,所以我做了一些挖掘,找到了codeproject.com/KB/security/BlockCiphers.aspx。看起来您可以在 AES128 CFB 中打开一个 Crypto++ 对象,该对象具有 64 位反馈大小,适用于 mcrypt,而不是 128 位默认大小。非常有趣的阅读。除了使用不同的密码模式(例如 OFB 与 CFB)之外,我也找不到配置 mcrypt 反馈大小的方法。 哎呀,我错了。 mcrypt 中的默认 CFB 为 8 位,因此您需要创建具有 8 位(构造函数为 1 字节)反馈大小的 Crypto++ 对象,以兼容 mcryptcrypto++ AES18 CFB。 mcrypt 的工作似乎有点傻,因为 n^8 位的模糊性远小于 n^128,但我可能没有正确理解。 是的,我认为我们处于同一水平。我们不是加密书呆子,但我们需要让加密为我们工作! 我可能会将您的帖子标记为答案,但我的周末很忙,所以可能要到星期一。【参考方案2】:查看 phpseclib:
http://phpseclib.sourceforge.net/
您可以根据需要设置密钥大小和块大小。
例如。 $aes->setKeyLength(128) 或 $aes->setKeyLength(256);
【讨论】:
谢谢,我在基准测试中注意到它声称非常快。然而,它是一个外部包含,而不是一个链接库(如 mcrypt)。我现在已经启动并运行了我的应用程序,但它可能对遇到同样问题的其他人有用。【参考方案3】:我有这个问题 - 几点。默认情况下,PHP Rijndael 模式将反馈循环设置为 8 位 - AES 需要与 IV/Key 的长度相同。
您可以通过使用模式“ncfb”而不是“cfb”或 MCRYPT_MODE_CFB 来做到这一点。
编写与 aes_cfb_128 兼容的 PHP 的完整详细信息在此 Security Stackexchange 问题中:aes cfb 128 decryption /encryption problem between Erlang and PHP。简短的是(来自 Tom Leek):
...对于 CFB 和 OFB(它们彼此不同且不能互换使用),您必须担心“反馈长度”,它不一定在各种加密库中以高清晰度记录。加密和解密都必须使用相同的反馈长度才能互操作。
【讨论】:
以上是关于将 Crypto++ AES 加密移植到 PHP 的 mcrypt 时密钥大小不正确的主要内容,如果未能解决你的问题,请参考以下文章
python Crypto AES-256-ECB 与PHP之间完成加解密
无法使用AES / ECB / PKCS5Padding将加密方法从Java复制到PHP