mcrypt_encrypt 到 openssl_encrypt 和 OPENSSL_ZERO_PADDING 问题
Posted
技术标签:
【中文标题】mcrypt_encrypt 到 openssl_encrypt 和 OPENSSL_ZERO_PADDING 问题【英文标题】:mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems 【发布时间】:2017-05-02 02:28:43 【问题描述】:我有这个mcrypt_encrypt
电话,对于给定的$key
、$message
和$iv
:
$string = mcrypt_encrypt(MCRYPT_3DES, $key, $message, MCRYPT_MODE_CBC, $iv);
我想将 mcrypt_encrypt
调用更改为 openssl_encrypt
调用,以保证未来的发展。
通过$mode = 'des-ede3-cbc'
或$mode = '3DES';
和$options = true
我得到更相似的响应,但不完全相同。有没有其他方法可以称之为完美匹配?
我正在为 lorem-ipsum $message
+$key
组合得到这个(base64_encoded),所以我开始相信一个函数或另一个函数在加密之前填充了一些消息......
对于 mcrypt:
"Y+JgMBdfI7ZYY3M9lJXCtb5Vgu+rWvLBfjug2GLX7uo="
对于openssl:
"Y+JgMBdfI7ZYY3M9lJXCtb5Vgu+rWvLBvte4swdttHY="
尝试使用 $options
传递 OPENSSL_ZERO_PADDING,但传递了除 1(OPENSSL_RAW_DATA 或 true 之外的任何内容) 产生一个空字符串 ...
既不使用OPENSSL_ZERO_PADDING
也不使用OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
工作... :(
我正在使用“OpenSSL 1.0.2g 2016 年 3 月 1 日”。
已经阅读this q&a,但对我没有帮助。不是有填充问题的onlyone,但到目前为止还没有解决方案。第二个答案谈到 adding 填充到 mcrypt 调用,我真的很想从 openssl 加密调用中删除填充...
【问题讨论】:
你永远不应该得到完美的匹配,永远。这就是初始化向量的用途。每次使用 same 算法和 same 密钥加密 same 有效负载时,如果您想要安全,您应该得到完全不同的输出。如果您为相同的输入获得相同的输出,则您的加密很弱。这就是加密中 IV 的意义所在。加密后,您将带有 IV 的加密有效负载传送到另一端。 如果我使用相同的有效载荷,相同的算法,相同的 IV;我认为我应该得到相同的输出。没关系,我应该为每个调用使用不同的 IV:对两个调用(mcrypt 和 openssl)使用相同的 IV,我认为我应该得到相同的输出,对吗?我开始相信这与消息填充有关,因为输出的乞求是相同的。 如果一切都一样,那么你应该得到相同的输出,对。openssl_encrypt
的第四个参数控制填充。您可以使用$encrypted = openssl_encrypt($data, $alg, $key, OPENSSL_ZERO_PADDING, $iv);
加密并检查是否得到相同的输出。在www.php.net/openssl_encrypt,您可以阅读 cmets 了解如何使用第 4 个参数。
OPENSSL_ZERO_PADDING
不起作用,至少对我来说……但是是的 - 填充是您无法获得相同结果的原因。但是,您的加密方案有很多错误,您不妨将其替换为新的 - 请为此使用库,不要自行滚动。
现在即将了解如何进行此调用并获得与使用 mcrypt 相同的结果。感谢您的 cmets 加密。这既不是完整的解决方案,我也无权更改此代码库中我想要的所有内容。
【参考方案1】:
mcrypt_encrypt 零填充输入数据,如果它不是块大小的倍数。如果数据本身有尾随零,这会导致结果不明确。显然 OpenSSL 不允许您在这种情况下使用零填充,这解释了错误的返回值。
您可以通过手动添加填充来规避此问题。
$message = "Lorem ipsum";
$key = "123456789012345678901234";
$iv = "12345678";
$message_padded = $message;
if (strlen($message_padded) % 8)
$message_padded = str_pad($message_padded,
strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");
$encrypted_mcrypt = mcrypt_encrypt(MCRYPT_3DES, $key,
$message, MCRYPT_MODE_CBC, $iv);
$encrypted_openssl = openssl_encrypt($message_padded, "DES-EDE3-CBC",
$key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
printf("%s => %s\n", bin2hex($message), bin2hex($encrypted_mcrypt));
printf("%s => %s\n", bin2hex($message_padded), bin2hex($encrypted_openssl));
这将两者打印为相等。
4c6f72656d20697073756d => c6fed0af15d494e485af3597ad628cec
4c6f72656d20697073756d0000000000 => c6fed0af15d494e485af3597ad628cec
【讨论】:
【参考方案2】:mcrypt_encrypt
使用零将消息填充到块大小。因此,您可以在原始数据的尾部添加零,然后对块进行加密。
使用OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING
应该可以。如果没有,那么您可以自己从解密数据中删除填充。
【讨论】:
我为什么要花时间在这上面?OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING
不起作用。可能是 phps openssl 实现中的错误或其他什么?从未加密的数据中删除多余的字符对我不起作用。此字符串用作验证签名:我提供了一个和一些数据,创建此加密数据以匹配收到的签名。我不控制另一边的系统。修改 mcrypt_ 调用使其匹配 openssl_ 对我没有帮助,我需要 openssl_ 来匹配 mcrypt_ :)
您可以自己添加零填充,然后调用openssl_encrypt。这也将添加 openssl 的填充。然后删除该填充(最后 8 个字节)。然后输出应该匹配 mcrypt_encrypt
我不确定我是否在关注。您可以将此作为答案的一部分发布,并附上代码示例来说明您的意思吗?谢谢。以上是关于mcrypt_encrypt 到 openssl_encrypt 和 OPENSSL_ZERO_PADDING 问题的主要内容,如果未能解决你的问题,请参考以下文章
PHP:OpenSSL 等价于 mcrypt:MCRYPT_3DES?