如果密码短于 16 个字符,为什么 mcrypt 和 openssl_encrypt 不会为使用 ecb 的河豚给出相同的结果?

Why are mcrypt and openssl_encrypt not giving the same results for blowfish with ecb if password is shorter than 16 chars?

我已阅读一般建议:

并测试了以下解决方案:

它们不起作用。这是我使用的代码:

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

$message_padded = $message;

if(strlen($message_padded) % 8) {
  $message_padded = str_pad($message_padded, strlen($message_padded) + 8 - strlen($message_padded) % 8, "[=11=]");
}

$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 个字符的密钥。

有什么建议吗?

首先,OPENSSL_NO_PADDING不应该与openssl_encrypt()一起使用。 Its documentation 提到 OPENSSL_ZERO_PADDING,这(令人困惑)意味着 'no padding'。这就是你想要的。

OPENSSL_NO_PADDINGintended for use with asymmetric cryptography。巧合的是,它的值为 3,等于 OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING。这就是为什么你可以错误地使用它而不会产生后果的原因(在这种情况下)。

您的密文不同,因为默认情况下 bf-ecb 模式下的函数 openssl_encrypt() 如果长度小于 16 字节,将用 [=19=] 填充您的密钥。河豚不需要这样做,mcrypt_encrypt() 不会那样做。为了关闭该行为,请在调用 openssl_encrypt() 时使用标志 OPENSSL_DONT_ZERO_PAD_KEY。由于似乎没有记录此标志,因此您必须转到 the source code to learn about it :-). Or read Bug #72362 OpenSSL Blowfish encryption is incorrect for short keys.

有了这个, 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