PHP 7.2 openssl_encrypt 和 mcrypt_encrypt 生成不同的值
PHP 7.2 openssl_encrypt and mcrypt_encrypt generate different values
我正在将项目中加密数据和使用 mcrypt 的所有功能迁移到 openssl。
进行测试我发现,使用相同的密钥加密相同的数据会得到不同的结果。
解密时,我用这两个函数都得到了正确的结果;问题是我与外部提供商共享此信息,并且只有在使用 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
有谁知道这是为什么,因为他们得到不同的结果?
谢谢!
据我所知,这两种方法使用不同的算法,请参阅 。您应该尝试使用 des-ede3
作为 openssl 密码方法,这相当于 mcrypt 的 MCRYPT_3DES
.
Mcrypt 和 OpenSSL 是两种不同的密码,因此它们以不同的方式加密和解密数据。
您可以使用 openSSL 的 des-ede3 方法,但实际上您应该使用 AES:Security Comparison of AES and DES
如果您的外部提供商使用 mcrypt_decrypt 解密您发送给他们的数据,那么他们将无法解密使用 openssl 加密的数据,反之亦然。
如果您转向更安全的 AES openSSL 加密,您的外部提供商也需要更改解密数据的方式。
(附带说明一下,您转向 openssl 是件好事 - mcrypt 是大约 php 7.1 的废弃软件)
不同的是mcrypt_encrypt
/ mcrypt_decrypt
uses Zero-Padding and openssl_encrypt
/ openssl_decrypt
uses PKCS7-Padding。这可以通过为 openssl
应用零填充来轻松验证:为此,必须使用标志 OPENSSL_ZERO_PADDING
禁用 PKCS7-Padding(重要:尽管名称,这个标志并不意味着使用了零填充,而是根本没有应用填充),并且明文必须用 0
值填充到块大小的下一个整数倍(8
bytes for Triple-DES)除非长度已经对应于块大小的整数倍:
<?php
function zeroPadding($data, $size) {
$oversize = strlen($data) % $size;
return $oversize == 0 ? $data : ($data . str_repeat("[=10=]", $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
上下文中使用零填充。无论使用这两种变体中的哪一种,具有相同的填充 mcrypt
和 openssl
结果是相同的!
应该注意(参见十六进制输出)mcrypt
在解密期间不会删除零填充(不像 openssl
在解密期间删除 PKCS7-Padding)。此外,与 PKCS7-Padding 相比,零填充 unreliable。如果要求允许(在您的情况下可能不是这种情况,因为外部提供者),因此应该使用 PKCS7-Padding。
此外,两个已发布的变体都使用相同的算法,即 Triple-DES in CBC-mode. Triple-DES has a blocksize of 8
bytes and a keysize of 24
bytes. Triple-DES is not identical to DES,但基于 DES,因为它由三个 DES 运行组成(加密-解密-加密 = ede)。 mcrypt
指定 Triple-DES/CBC 有两个参数,MCRYPT_3DES
(Triple-DES) 和 MCRYPT_MODE_CBC
(CBC-mode),而 openssl
只使用一个参数,DES-EDE3-CBC
.
Triple-DES 有多个 Keying-Options。 3TDEA
使用三个独立的 DES 密钥,并在 openssl
的上下文中指定 DES-EDE3-CBC
,它需要一个 24
字节的密钥,2TDEA
使用两个独立的密钥和可以在 openssl
的上下文中指定,或者用 DES-EDE-CBC
指定,它需要一个 16
字节的键。
Triple-DES 比现代 AES 慢得多,但具有相当的安全性。与填充一样,如果可能,您应该切换到 AES。
我正在将项目中加密数据和使用 mcrypt 的所有功能迁移到 openssl。
进行测试我发现,使用相同的密钥加密相同的数据会得到不同的结果。
解密时,我用这两个函数都得到了正确的结果;问题是我与外部提供商共享此信息,并且只有在使用 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
有谁知道这是为什么,因为他们得到不同的结果?
谢谢!
据我所知,这两种方法使用不同的算法,请参阅 des-ede3
作为 openssl 密码方法,这相当于 mcrypt 的 MCRYPT_3DES
.
Mcrypt 和 OpenSSL 是两种不同的密码,因此它们以不同的方式加密和解密数据。
您可以使用 openSSL 的 des-ede3 方法,但实际上您应该使用 AES:Security Comparison of AES and DES
如果您的外部提供商使用 mcrypt_decrypt 解密您发送给他们的数据,那么他们将无法解密使用 openssl 加密的数据,反之亦然。
如果您转向更安全的 AES openSSL 加密,您的外部提供商也需要更改解密数据的方式。
(附带说明一下,您转向 openssl 是件好事 - mcrypt 是大约 php 7.1 的废弃软件)
不同的是mcrypt_encrypt
/ mcrypt_decrypt
uses Zero-Padding and openssl_encrypt
/ openssl_decrypt
uses PKCS7-Padding。这可以通过为 openssl
应用零填充来轻松验证:为此,必须使用标志 OPENSSL_ZERO_PADDING
禁用 PKCS7-Padding(重要:尽管名称,这个标志并不意味着使用了零填充,而是根本没有应用填充),并且明文必须用 0
值填充到块大小的下一个整数倍(8
bytes for Triple-DES)除非长度已经对应于块大小的整数倍:
<?php
function zeroPadding($data, $size) {
$oversize = strlen($data) % $size;
return $oversize == 0 ? $data : ($data . str_repeat("[=10=]", $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
上下文中使用零填充。无论使用这两种变体中的哪一种,具有相同的填充 mcrypt
和 openssl
结果是相同的!
应该注意(参见十六进制输出)mcrypt
在解密期间不会删除零填充(不像 openssl
在解密期间删除 PKCS7-Padding)。此外,与 PKCS7-Padding 相比,零填充 unreliable。如果要求允许(在您的情况下可能不是这种情况,因为外部提供者),因此应该使用 PKCS7-Padding。
此外,两个已发布的变体都使用相同的算法,即 Triple-DES in CBC-mode. Triple-DES has a blocksize of 8
bytes and a keysize of 24
bytes. Triple-DES is not identical to DES,但基于 DES,因为它由三个 DES 运行组成(加密-解密-加密 = ede)。 mcrypt
指定 Triple-DES/CBC 有两个参数,MCRYPT_3DES
(Triple-DES) 和 MCRYPT_MODE_CBC
(CBC-mode),而 openssl
只使用一个参数,DES-EDE3-CBC
.
Triple-DES 有多个 Keying-Options。 3TDEA
使用三个独立的 DES 密钥,并在 openssl
的上下文中指定 DES-EDE3-CBC
,它需要一个 24
字节的密钥,2TDEA
使用两个独立的密钥和可以在 openssl
的上下文中指定,或者用 DES-EDE-CBC
指定,它需要一个 16
字节的键。
Triple-DES 比现代 AES 慢得多,但具有相当的安全性。与填充一样,如果可能,您应该切换到 AES。