我正在将项目的所有加密数据并使用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
有人知道原因是因为他们得到不同的结果吗?
谢谢!
答案 0 :(得分:2)
区别在于mcrypt_encrypt
/ mcrypt_decrypt
使用Zero-Padding,而openssl_encrypt
/ openssl_decrypt
使用PKCS7-Padding。通过为openssl
应用零填充可以很容易地验证这一点:为此,必须使用标志OPENSSL_ZERO_PADDING
禁用PKCS7-Padding( important :尽管有这个标志,并不意味着使用零填充,而是完全不应用填充),并且必须使用0
值将明文填充为块大小的下一个整数倍(8
个字节) (对于Triple-DES)),除非长度已经对应于blocksize的整数倍:
<?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
不是在openssl
上下文中使用零填充,而是可以在mcrypt
上下文中使用PKCS7填充。无论使用两个变体中的哪个变体,mcrypt
和openssl
的相同填充结果都是相同的!
应该注意的是(参见十六进制输出)mcrypt
不会在解密期间删除零填充(与openssl
会在解密期间删除PKCS7填充不同)。此外,与PKCS7填充相比,零填充为unreliable。如果要求允许(您的情况可能不是这样,因为外部提供者),因此应使用PKCS7-Padding。
此外,两个发布的变体都使用相同的算法,即Triple-DES模式下的CBC。 Triple-DES的块大小为8
个字节,密钥大小为24
个字节。三重DES与DES不同,但是在某种意义上说,它是基于DES的,它由三个DES运行组成(encryption-decryption-encryption = ede)。 mcrypt
使用两个参数MCRYPT_3DES
(Triple-DES)和MCRYPT_MODE_CBC
(CBC模式)指定Triple-DES / CBC,而openssl
仅使用一个参数{{ 1}}。
Triple-DES有多个Keying-Options。 DES-EDE3-CBC
使用三个独立的DES密钥,并在3TDEA
和openssl
的上下文中指定,DES-EDE3-CBC
个字节的密钥,24
使用两个独立的密钥,并且可以在2TDEA
的上下文中指定,也可以与openssl
一起指定,后者需要一个DES-EDE-CBC
个字节的密钥。
Triple-DES比现代AES慢得多,但具有相当的安全性。与填充一样,如果可能,您应该切换到AES。
答案 1 :(得分:1)
如我所见,这两种方法都使用不同的算法,请参见this question。您应该尝试使用des-ede3
作为openssl密码方法,与mcrypt的MCRYPT_3DES
等效。
答案 2 :(得分:1)
Mcrypt和openssl是两种不同的密码,因此它们以不同的方式加密和解密数据。
您可以使用openSSL的des-ede3方法,但实际上应该使用AES:Security Comparison of AES and DES
如果外部提供商使用mcrypt_decrypt解密发送给他们的数据,则他们将无法解密使用openssl加密的数据,反之亦然。
如果您转向更安全的AES openSSL加密,您的外部提供商也将需要更改解密数据的方式。
(在旁注中,很高兴您转到openssl-mcrypt被遗弃在php 7.1左右)