在 PHP 中无法将 mcrypt 升级为 openssl 解密
Failing to upgrade mcrypt to openssl decryption in PHP
我正在将遗留应用程序从 PHP 7.0 升级到 7.2,当我为 openssl 切换 mcrypt 时,我的解密功能不起作用。
我尝试了 , and Gists like https://gist.github.com/odan/c1dc2798ef9cedb9fedd09cdfe6e8e76 等现有答案,但我仍然无法使代码正常工作。
任何人都可以阐明我做错了什么吗?
旧代码
function decrypt($value, $key) {
$ivLength = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = substr($value, 0, $ivLength);
return rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
hash('sha256', $key, true),
substr($value, $ivLength),
MCRYPT_MODE_CBC,
$iv
),
"[=11=]"
);
}
新代码(不适用于现有输入)
function decrypt($value, $key) {
$ivLength = openssl_cipher_iv_length('AES-128-CBC');
$iv = substr($value, 0, $ivLength);
// Note: $key is hashed because it was hashed in the old encrypt function below
return openssl_decrypt(substr($value, $ivLength), 'AES-128-CBC', hash('sha256', $key, true), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
}
对于上下文,以下是旧代码对值进行加密的方式:
function encrypt($value, $key) {
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
return $iv . mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
hash('sha256', $key, true),
$value,
MCRYPT_MODE_CBC,
$iv
);
}
此外,$value
中的原始值在新代码中输出到错误日志时的长度是旧代码的两倍,看起来相似但字符更多。
这里的主要问题是密钥大小。您正在使用 returns 256 位哈希的 SHA256 创建密钥,因此您使用的是带有 256 位密钥的 AES / Rijndael。
Rijndael-128中的数字定义了块大小,密钥大小由我们使用的密钥长度决定。 AES-128 中的数字定义密钥大小(块大小不变,128 位),如果实际密钥长度与此数字不同,则缩短或扩展密钥(零字节)以适合所选密钥尺寸。
这意味着您的 mcrypt
代码在 CBC 模式下使用带有 256 位密钥的 Rijndael-128 (AES)。 openssl
等价物是 AES-256-CBC,如果我们使用这个算法,那么 mcrypt
和 openssl
应该会产生兼容的结果。
function decrypt_mcrypt_with_openssl($value, $key) {
$iv = substr($value, 0, 16);
$ciphertext = substr($value, 16);
$key = hash('sha256', $key, true);
$plaintext = openssl_decrypt(
$ciphertext, 'AES-256-CBC', $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
return rtrim($plaintext, "[=10=]");
}
SHA256 不适合作为密钥推导函数。如果您从密码中导出密钥,则可以使用 hash_pbkdf2
with a random salt and a high enough number of iterations. Or you could create a cryptographically secure pseudo-random key with openssl_random_pseudo_bytes
.
我认为最好完全停止使用 mcrypt
,只使用 openssl
。 Mcrypt 不支持 PKCS7 填充,它不提供任何 authenticated encryption 算法,并且不再维护。
我正在将遗留应用程序从 PHP 7.0 升级到 7.2,当我为 openssl 切换 mcrypt 时,我的解密功能不起作用。
我尝试了
任何人都可以阐明我做错了什么吗?
旧代码
function decrypt($value, $key) {
$ivLength = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = substr($value, 0, $ivLength);
return rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
hash('sha256', $key, true),
substr($value, $ivLength),
MCRYPT_MODE_CBC,
$iv
),
"[=11=]"
);
}
新代码(不适用于现有输入)
function decrypt($value, $key) {
$ivLength = openssl_cipher_iv_length('AES-128-CBC');
$iv = substr($value, 0, $ivLength);
// Note: $key is hashed because it was hashed in the old encrypt function below
return openssl_decrypt(substr($value, $ivLength), 'AES-128-CBC', hash('sha256', $key, true), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
}
对于上下文,以下是旧代码对值进行加密的方式:
function encrypt($value, $key) {
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
return $iv . mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
hash('sha256', $key, true),
$value,
MCRYPT_MODE_CBC,
$iv
);
}
此外,$value
中的原始值在新代码中输出到错误日志时的长度是旧代码的两倍,看起来相似但字符更多。
这里的主要问题是密钥大小。您正在使用 returns 256 位哈希的 SHA256 创建密钥,因此您使用的是带有 256 位密钥的 AES / Rijndael。
Rijndael-128中的数字定义了块大小,密钥大小由我们使用的密钥长度决定。 AES-128 中的数字定义密钥大小(块大小不变,128 位),如果实际密钥长度与此数字不同,则缩短或扩展密钥(零字节)以适合所选密钥尺寸。
这意味着您的 mcrypt
代码在 CBC 模式下使用带有 256 位密钥的 Rijndael-128 (AES)。 openssl
等价物是 AES-256-CBC,如果我们使用这个算法,那么 mcrypt
和 openssl
应该会产生兼容的结果。
function decrypt_mcrypt_with_openssl($value, $key) {
$iv = substr($value, 0, 16);
$ciphertext = substr($value, 16);
$key = hash('sha256', $key, true);
$plaintext = openssl_decrypt(
$ciphertext, 'AES-256-CBC', $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
return rtrim($plaintext, "[=10=]");
}
SHA256 不适合作为密钥推导函数。如果您从密码中导出密钥,则可以使用 hash_pbkdf2
with a random salt and a high enough number of iterations. Or you could create a cryptographically secure pseudo-random key with openssl_random_pseudo_bytes
.
我认为最好完全停止使用 mcrypt
,只使用 openssl
。 Mcrypt 不支持 PKCS7 填充,它不提供任何 authenticated encryption 算法,并且不再维护。