将 PHP AES 加密从 mcrypt 迁移到 openssl return 不同的加密字符串
Migrate PHP AES encryption from mcrypt to openssl return different encrypted string
我正在尝试将我们现有的使用 mcrypt 的加密函数更改为 OpenSSL.But 它会创建不同的加密字符串。我们如何解决这个问题?
<?php
$str = 'test';
$method = 'AES-128-CBC';
$key = 'o6xSYYAVl2eapPI2';
$iv = 'fedcba9876543210';
function encrypt_mcrypt($str=NULL,$key,$iv) {
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', $iv);
mcrypt_generic_init($td,$key,$iv);
$encrypted = mcrypt_generic(@$td,@$str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex(@$encrypted);
}
function encrypt_openssl($str=NULL,$key,$iv) {
$encrypted =openssl_encrypt($str, 'AES-256-CBC', $key, OPENSSL_RAW_DATA,$iv);
return bin2hex(@$encrypted);
}
echo 'Mcrypt:'.encrypt_openssl($str,$key,$iv);
echo '<br/>';
echo 'Openssl:'.encrypt_mcrypt($str,$key,$iv);
输出
Mcrypt:0ab40f383b421ba465c0cbbcded97319
Openssl:57c86f3089535b3acfbe65cecbb662b9
两种代码都使用不同的 AES 变体和填充。 mcrypt 代码应用 AES-128 和零填充,openssl 代码应用 AES-256 和 PKCS7 填充。为确保两个密文匹配,两个代码必须使用相同的 AES 变体和填充。
mcrypt 根据密钥大小识别 AES 变体。由于 $key
是一个 16 字节的密钥,因此使用 AES-128。 openssl 根据第二个参数中传递的规范确定 AES 变体。太短的键用 0 值填充到所需的长度,太长的键被截断。这里指定了AES-256-CBC
,即。 e.使用 AES-256。因此,16 字节的密钥 $key
用 0 值填充并扩展到 32 字节的大小。
mcrypt 隐式使用零填充进行加密,在解密过程中不会隐式 删除。不支持 PKCS7 填充。 openssl 隐式应用 PKCS7 填充进行加密,在解密过程中被隐式删除。不支持零填充。如果openssl代码要用Zero padding或者mcyrpt代码要PKCS7 padding,这个要自己实现。
关于从mcrypt迁移到openssl,修改了openssl代码在下文中与 mcrypt 代码在功能上相同,即使用 AES-128 和零填充。对于 AES 变体,只有规范 AES-256-CBC
必须更改为 AES-128-CBC
。关于填充,必须使用 OPENSSL_ZERO_PADDING
禁用默认的 PKCS7 填充并且必须实现零填充本身(请注意 OPENSSL_ZERO_PADDING
标志仅禁用填充,但不启用零填充;名称选择错误) :
<?php
$str = "test";
$key = 'o6xSYYAVl2eapPI2';
$iv = 'fedcba9876543210';
function encrypt_openssl($str = NULL, $key, $iv) {
$encrypted = openssl_encrypt(
zeroPad($str, 16), // Zero pad plaintext
'AES-128-CBC', // Choose AES-128
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, // Disable PKCS7 Padding
$iv);
return bin2hex(@$encrypted);
}
function zeroPad($text, $bs) {
$pad = $bs - strlen($text) % $bs;
return ($pad < 16) ? $text . str_repeat("[=10=]", $pad) : $text;
}
echo 'Openssl:'.encrypt_openssl($str,$key,$iv); // Openssl:57c86f3089535b3acfbe65cecbb662b9
为了与您的结果进行比较,请注意您混淆了输出的标签,即 openssl 结果被标记为 Mcrypt
,反之亦然!
最后一点:一般来说,PKCS7 填充比零填充更可靠,因为前者包含填充长度的信息。零填充不是这种情况,因此在删除填充时(即解密后)无法区分常规字节和填充字节。还有不同的零填充变体,例如如果明文的长度已经对应于块大小的整数倍(此变体使用 mcrypt),则一个不填充,在这种情况下,另一个用完整的块填充。
我正在尝试将我们现有的使用 mcrypt 的加密函数更改为 OpenSSL.But 它会创建不同的加密字符串。我们如何解决这个问题?
<?php
$str = 'test';
$method = 'AES-128-CBC';
$key = 'o6xSYYAVl2eapPI2';
$iv = 'fedcba9876543210';
function encrypt_mcrypt($str=NULL,$key,$iv) {
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', $iv);
mcrypt_generic_init($td,$key,$iv);
$encrypted = mcrypt_generic(@$td,@$str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex(@$encrypted);
}
function encrypt_openssl($str=NULL,$key,$iv) {
$encrypted =openssl_encrypt($str, 'AES-256-CBC', $key, OPENSSL_RAW_DATA,$iv);
return bin2hex(@$encrypted);
}
echo 'Mcrypt:'.encrypt_openssl($str,$key,$iv);
echo '<br/>';
echo 'Openssl:'.encrypt_mcrypt($str,$key,$iv);
输出
Mcrypt:0ab40f383b421ba465c0cbbcded97319
Openssl:57c86f3089535b3acfbe65cecbb662b9
两种代码都使用不同的 AES 变体和填充。 mcrypt 代码应用 AES-128 和零填充,openssl 代码应用 AES-256 和 PKCS7 填充。为确保两个密文匹配,两个代码必须使用相同的 AES 变体和填充。
mcrypt 根据密钥大小识别 AES 变体。由于 $key
是一个 16 字节的密钥,因此使用 AES-128。 openssl 根据第二个参数中传递的规范确定 AES 变体。太短的键用 0 值填充到所需的长度,太长的键被截断。这里指定了AES-256-CBC
,即。 e.使用 AES-256。因此,16 字节的密钥 $key
用 0 值填充并扩展到 32 字节的大小。
mcrypt 隐式使用零填充进行加密,在解密过程中不会隐式 删除。不支持 PKCS7 填充。 openssl 隐式应用 PKCS7 填充进行加密,在解密过程中被隐式删除。不支持零填充。如果openssl代码要用Zero padding或者mcyrpt代码要PKCS7 padding,这个要自己实现。
关于从mcrypt迁移到openssl,修改了openssl代码在下文中与 mcrypt 代码在功能上相同,即使用 AES-128 和零填充。对于 AES 变体,只有规范 AES-256-CBC
必须更改为 AES-128-CBC
。关于填充,必须使用 OPENSSL_ZERO_PADDING
禁用默认的 PKCS7 填充并且必须实现零填充本身(请注意 OPENSSL_ZERO_PADDING
标志仅禁用填充,但不启用零填充;名称选择错误) :
<?php
$str = "test";
$key = 'o6xSYYAVl2eapPI2';
$iv = 'fedcba9876543210';
function encrypt_openssl($str = NULL, $key, $iv) {
$encrypted = openssl_encrypt(
zeroPad($str, 16), // Zero pad plaintext
'AES-128-CBC', // Choose AES-128
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, // Disable PKCS7 Padding
$iv);
return bin2hex(@$encrypted);
}
function zeroPad($text, $bs) {
$pad = $bs - strlen($text) % $bs;
return ($pad < 16) ? $text . str_repeat("[=10=]", $pad) : $text;
}
echo 'Openssl:'.encrypt_openssl($str,$key,$iv); // Openssl:57c86f3089535b3acfbe65cecbb662b9
为了与您的结果进行比较,请注意您混淆了输出的标签,即 openssl 结果被标记为 Mcrypt
,反之亦然!
最后一点:一般来说,PKCS7 填充比零填充更可靠,因为前者包含填充长度的信息。零填充不是这种情况,因此在删除填充时(即解密后)无法区分常规字节和填充字节。还有不同的零填充变体,例如如果明文的长度已经对应于块大小的整数倍(此变体使用 mcrypt),则一个不填充,在这种情况下,另一个用完整的块填充。