如果密码短于 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_PADDING
是 intended 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
我已阅读一般建议:
并测试了以下解决方案:
它们不起作用。这是我使用的代码:
$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_PADDING
是 intended 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