PHP 7.2 openssl_encrypt和mcrypt_encrypt生成不同的值

时间:2019-10-04 10:32:24

标签: php openssl mcrypt

我正在将项目的所有加密数据并使用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

有人知道原因是因为他们得到不同的结果吗?

谢谢!

3 个答案:

答案 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填充。无论使用两个变体中的哪个变体,mcryptopenssl的相同填充结果都是相同的!

应该注意的是(参见十六进制输出)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-OptionsDES-EDE3-CBC使用三个独立的DES密钥,并在3TDEAopenssl的上下文中指定,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左右)