PHP 7.2 openssl_encrypt 和 mcrypt_encrypt 生成不同的值

标签 php openssl mcrypt

我正在将项目中加密数据和使用 mcrypt 的所有功能迁移到 openssl。

进行测试时我发现,使用相同的 key 加密相同的数据会得到不同的结果。

解密时,我使用这两个函数都得到了正确的结果;问题是我与外部提供商共享此信息,并且只有使用 mcrypt 加密才能成功解密数据。

这是测试代码:

// Configuration.
$data = 'FOO';
$secret = '111222333444555666777888';
$iv = 'ABCDEFGH';

// Encrytp & decrypt with mcrypt.
$encMcrypt = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $secret, utf8_encode($data),  MCRYPT_MODE_CBC, $iv));
$decMcrypt = mcrypt_decrypt(MCRYPT_3DES, $secret, hex2bin($encMcrypt), MCRYPT_MODE_CBC, $iv);

// Encrytp & decrypt with openssl.
$encOpenSSL = bin2hex(openssl_encrypt(utf8_encode($data), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA, $iv));
$decOpenSSL = openssl_decrypt(hex2bin($encOpenSSL), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA, $iv);

// Result.
echo "mcrypt encrypt: $encMcrypt <br>";
echo "openssl encrypt: $encOpenSSL <br>";
echo "mcrypt decrypt: $decMcrypt <br>";
echo "openssl decrypt: $decOpenSSL";

结果:

mcrypt encrypt: 3f9bd8d5f844ff67
openssl encrypt: b2f4b9aeb07e1ca4
mcrypt decrypt: FOO
openssl decrypt: FOO

有谁知道为什么会得到不同的结果?

谢谢!

最佳答案

不同的是mcrypt_encrypt/mcrypt_decrypt使用Zero-Paddingopenssl_encrypt/openssl_decrypt使用PKCS7-Padding 。这可以通过为 openssl 应用零填充来轻松验证:为此,必须使用标志 OPENSSL_ZERO_PADDING 禁用 PKCS7-Padding(重要:尽管名称如此,此标志并不意味着使用零填充,而是根本不应用填充),并且明文必须使用 0 值填充到下一个整数倍 block 大小(对于 Triple-DES,8 字节),除非长度已对应于 block 大小的整数倍:

<?php

function zeroPadding($data, $size) {
    $oversize = strlen($data) % $size;
    return $oversize == 0 ? $data : ($data . str_repeat("\0", $size - $oversize)); 
}

// Something is wronguration.
$data = 'FOO';

$secret = '111222333444555666777888';
$iv = 'ABCDEFGH';

// Encrytp & decrypt with mcrypt.
$encMcrypt = bin2hex(mcrypt_encrypt(MCRYPT_3DES, $secret, utf8_encode($data), MCRYPT_MODE_CBC, $iv));
$decMcrypt = mcrypt_decrypt(MCRYPT_3DES, $secret, hex2bin($encMcrypt), MCRYPT_MODE_CBC, $iv);

// Encrytp & decrypt with openssl.
$encOpenSSLZeroPadding = bin2hex(openssl_encrypt(utf8_encode(zeroPadding($data, 8)), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv));
$decOpenSSLZeroPadding = openssl_decrypt(hex2bin($encOpenSSLZeroPadding), 'DES-EDE3-CBC', $secret, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);

// Result.
echo "data padded: " . bin2hex(zeroPadding($data, 8)) . "<br>";
echo "mcrypt encrypt: $encMcrypt <br>";
echo "openssl encrypt: $encOpenSSLZeroPadding <br>";
echo "mcrypt decrypt: $decMcrypt <br>";
echo "mcrypt decrypt: " . bin2hex($decMcrypt) . "<br>";
echo "openssl decrypt: $decOpenSSLZeroPadding" . "<br>";
echo "openssl decrypt: " . bin2hex($decOpenSSLZeroPadding);

输出如下:

data padded: 464f4f0000000000
mcrypt encrypt: 3f9bd8d5f844ff67
openssl encrypt: 3f9bd8d5f844ff67
mcrypt decrypt: FOO
mcrypt decrypt: 464f4f0000000000
openssl decrypt: FOO
openssl decrypt: 464f4f0000000000

可以在 mcrypt 上下文中使用 PKCS7 填充,而不是在 openssl 上下文中使用零填充。无论使用这两种变体中的哪一种,使用相同的填充 mcryptopenssl 结果都是相同的!

应该注意(请参阅十六进制输出),mcrypt 在解密过程中不会删除零填充(与 openssl 不同,它会在解密过程中删除 PKCS7-Padding) )。另外,零填充是 unreliable与 PKCS7-Padding 相比。如果要求允许(在您的情况下,由于外部提供者的原因可能不是这种情况),因此应该使用 PKCS7-Padding。

此外,两个发布的变体都使用相同的算法,即 Triple-DESCBC -模式。 Triple-DES 的 block 大小为 8 字节, key 大小为 24 字节。 Triple-DES 与 DES 不同,但它基于 DES,因为它由三个 DES 运行组成(加密-解密-加密 = ede)。 mcrypt 使用两个参数指定 Triple-DES/CBC:MCRYPT_3DES(三重 DES)和 MCRYPT_MODE_CBC(CBC 模式),而 openssl 仅使用一个参数,DES-EDE3-CBC

有几个Keying-Options对于三重 DES。 3TDEA 使用三个独立的 DES key ,并在 openssl 的上下文中使用 DES-EDE3-CBC 指定,它需要 24 字节 key ,2TDEA 使用两个独立的 key ,可以在 openssl 上下文中指定,也可以使用 DES-EDE-CBC 指定,其中需要 16 字节 key 。

Triple-DES 比现代 AES 慢得多,但具有相当的安全性。与填充一样,如果可能,您应该切换到 AES。

关于PHP 7.2 openssl_encrypt 和 mcrypt_encrypt 生成不同的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58234665/

相关文章:

visual-studio-code - Visual Studio Code 无法识别已安装的 OpenSSL [ibp-vscode-extension]

php - mcrypt_generic 与 mcrypt_encrypt

php - mcrypt 警告但仍解密数据

php - 创建一个有效的单词计数器,包括中文/日语和其他重音语言

php - 删除孤立记录 - mysql

python - Pyenv、Python安装失败: OpenSSL development header is not installed

php - mcrypt 加密将 s 串 '%00' 添加到字符串末尾

php - 检索最后插入的 iD,然后上传到 MySQL。 #PDO #PHP

php - 通过 PHP 函数和 Javascript 刷新数据库信息

c - RSA使用openssl库加密500字节数据