首页 > 解决方案 > 如果密码短于 16 个字符,为什么 mcrypt 和 openssl_encrypt 不能为带有 ecb 的河豚给出相同的结果?

问题描述

我已经阅读了一般建议: 不推荐使用 mcrypt,还有什么替代方案?PHP7.1 mcrypt 替代

并测试了以下解决方案: Migrating mcrypt with Blowfish and ECB to OpenSSL and php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING questions

他们不工作。这是我使用的代码:

$message = "My secret message";
$key = "mysecretpasswor"; // <- if you add one more character here, it's working
$iv = "\0\0\0\0\0\0\0\0";

$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_BLOWFISH, $key, $message, MCRYPT_MODE_ECB, $iv);
$encrypted_openssl = openssl_encrypt($message_padded, "bf-ecb", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);

printf("%s => %s\n", $message, base64_encode($encrypted_mcrypt));
printf("%s => %s\n", $message_padded, base64_encode($encrypted_openssl));

使用“DES-EDE3-CBC”之类的加密方法确实有效。但我无法更改使用的加密。我必须将旧代码迁移到新代码。有时会使用少于 16 个字符的键。

有什么建议么?

标签: phpopensslmcrypt

解决方案


首先,OPENSSL_NO_PADDING不应该与openssl_encrypt(). 它的文档提到OPENSSL_ZERO_PADDING,这(令人困惑地)意味着“没有填充”。这就是你想要的。

OPENSSL_NO_PADDING旨在与非对称加密一起使用。巧合的是,它的值为 3,等于OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING。这就是为什么您可以不正确地使用它而没有后果的原因(在这种情况下)。

您的密文是不同的,因为默认情况下模式下的函数将openssl_encrypt()在密钥长度小于 16 个字节bf-ecb时用 's 填充您的密钥。\0河豚mcrypt_encrypt()不需要这样做,也不会这样做。为了关闭该行为,请OPENSSL_DONT_ZERO_PAD_KEY在调用时使用该标志openssl_encrypt()。由于这个标志似乎没有被记录,你将不得不去源代码来了解它:-)。或阅读错误 #72362 OpenSSL Blowfish 加密对于短密钥不正确

有了这个,正确的调用openssl_encrypt()变为:

$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING | OPENSSL_DONT_ZERO_PAD_KEY;
$encrypted_openssl = openssl_encrypt($message_padded, "bf-ecb", $key, $opts);

测试它:

$ php bf.php
My secret message => JPO/tvAqFD2KCTqfv0l8uWLfPUWdZIxQ
My secret message => JPO/tvAqFD2KCTqfv0l8uWLfPUWdZIxQ

推荐阅读