Mcrypt弃用后如何解密?
Posted
技术标签:
【中文标题】Mcrypt弃用后如何解密?【英文标题】:How to decrypt after Mcrypt deprecation? 【发布时间】:2017-07-12 08:56:27 【问题描述】:我已将我的 php 版本更新到 7.1。 我有使用 mcrypt 加密数据的功能。 现在此功能已弃用。
我怎样才能在不回到旧版本的 php 的情况下解密数据。
这是我使用的代码:
public function encrypt($plaintext)
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$ciphertext = mcrypt_encrypt(self::CIPHER, $this->key, $plaintext, self::MODE, $iv);
return base64_encode($iv.$ciphertext);
public function decrypt($ciphertext)
$ciphertext = base64_decode($ciphertext);
$ivSize = mcrypt_get_iv_size(self::CIPHER, self::MODE);
if (strlen($ciphertext) < $ivSize)
throw new Exception('Missing initialization vector');
$iv = substr($ciphertext, 0, $ivSize);
$ciphertext = substr($ciphertext, $ivSize);
$plaintext = mcrypt_decrypt(self::CIPHER, $this->key, $ciphertext, self::MODE, $iv);
return rtrim($plaintext, "\0");
使用常量:
const CIPHER = MCRYPT_RIJNDAEL_128; // Rijndael-128 is AES
const MODE = MCRYPT_MODE_CBC;
我看到建议使用 OpenSSL。这就是我从现在开始使用的。但是如何使用这种方法解密旧数据呢?
谢谢
编辑: 我知道我可以使用 OpenSSL 作为替代方案。 这就是我从现在开始为内容所做的事情。 但我需要从我的旧内容中解密我的 mcrypted 代码。
*编辑请求@symcbean
尝试像这样使用 OpenSSL 解密:
public function decrypt($ciphertext)
$ciphertext = base64_decode($ciphertext);
if (!function_exists("openssl_decrypt"))
throw new Exception("aesDecrypt needs openssl php module.");
$key = $this->key;
$method = 'AES-256-CBC';
$ivSize = openssl_cipher_iv_length($method);
$iv = substr($ciphertext,0,$ivSize);
$data = substr($ciphertext,$ivSize);
$clear = openssl_decrypt ($data, $method, $key, 'OPENSSL_RAW_DATA'|'OPENSSL_ZERO_PADDING', $iv);
return $clear;
【问题讨论】:
PHP 7 - mcrypt deprecated, need alternative的可能重复 不是。我不需要替代品。我知道我可以使用 openssl。但我需要解密我的 mcrypted 内容。 @Ben:不同意 - 这是关于密码的讨论,接受的答案没有解决解密的具体问题。 你试过用openssl解密mcrypt密文吗?你的代码在哪里?发生了什么? 如果您以前使用过MCRYPT_RIJNDAEL_128
,我认为您的$method
应该是"AES-128-CBC"
。 openssl_decrypt
标志也是常量,而不是字符串,因此请删除 OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
之类的引号。
【参考方案1】:
需要注意的重要一点是mcrypt_encrypt
零填充输入数据,如果它不是块大小的倍数。如果数据本身有尾随零,这会导致结果不明确。
openssl_decrypt
不会自动删除零填充,因此您只剩下修剪尾随空值的可能性。
这是一个简单的例子:
$data = "Lorem ipsum";
$key = "1234567890abcdef";
$iv = "1234567890abcdef";
$encrypted = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
echo bin2hex($encrypted) . "\n";
$decrypted = openssl_decrypt(
$encrypted, "AES-128-CBC", $key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
echo var_export($decrypted, true) . "\n";
$result = rtrim($decrypted, "\0");
echo var_export($result, true) . "\n";
输出:
70168f2d5751b3d3bf36b7e6b8ec5843
'Lorem ipsum' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . '' . "\0" . ''
'Lorem ipsum'
【讨论】:
这给了我一个结果,但似乎错误是由 base64_decode 引起的。响应是所有未知字符,例如“?”等等。也许是 UTF 8 的问题?你怎么看? 认为这是 nginx 上的 base64 和 HTTP2 的问题?【参考方案2】:我解决了。 不知道它是否正确(猜不是) 但远程连接在具有较低 php 版本的服务器上。 解密所有内容并使用 OpenSSL 加密。
感谢您的建议!
【讨论】:
【参考方案3】:我在用mcrypt_encrypt
和openssl_decrypt
解密数据时也遇到了一些问题。下面的小测试使用 mcrypt 和 openssl 加密字符串(添加零填充和不添加),并使用这两种方法解密所有字符串。此示例使用 ECB 模式,但如果需要,您可以通过添加 IV 轻松地将其更改为 CBC。
// Setup key and test data
$key = hash("sha256", 'test', true);
$data = 'Hello World';
$enc = $dec = [];
// Encrypt with MCRYPT_RIJNDAEL_128 method
$enc['RIJ'] = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB));
// Encrypt with OpenSSL equivalent AES-256
$enc['AES'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
// Encrypt with OpenSSL equivalent AES-256 and added zero padding
if (strlen($data) % 8) $data = str_pad($data, strlen($data) + 8 - strlen($data) % 8, "\0");
$enc['AES0'] = base64_encode(openssl_encrypt($data, 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING));
// Decrypt all strings with MCRYPT_RIJNDAEL_128
$dec['mRIJ'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['RIJ']), MCRYPT_MODE_ECB));
$dec['mAES'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES']), MCRYPT_MODE_ECB));
$dec['mAES0'] = bin2hex(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($enc['AES0']), MCRYPT_MODE_ECB));
// Decrypt all strings with OpenSSL equivalent AES-256
$dec['oRIJ'] = bin2hex(openssl_decrypt(base64_decode($enc['RIJ']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
$dec['oAES'] = bin2hex(openssl_decrypt(base64_decode($enc['AES']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA));
$dec['oAES0'] = bin2hex(openssl_decrypt(base64_decode($enc['AES0']), 'aes-256-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING));
// Print results
print_r($enc);
var_dump($dec);
print_r
和 var_dump
输出如下:
Array
(
[RIJ] => YcvcTwAMLUMBCZXu5XqoEw==
[AES] => +AXMBwkWlgM1YDieGgekSg==
[AES0] => YcvcTwAMLUMBCZXu5XqoEw==
)
array(6)
["mRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["mAES"]=>
string(32) "48656c6c6f20576f726c640505050505"
["mAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oRIJ"]=>
string(32) "48656c6c6f20576f726c640000000000"
["oAES"]=>
string(22) "48656c6c6f20576f726c64"
["oAES0"]=>
string(32) "48656c6c6f20576f726c640000000000"
如果您需要与使用 mcrypt 的 openssl 方法相同的加密字符串,您将手动将零填充添加到字符串(示例中为 AES0
)。这样,您将获得与以前完全相同的加密和解密字符串。有关零填充的更多信息,您应该在此处查看 Joe 的答案:php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems
如果您不想手动将零填充添加到所有新消息,则需要不同的标志来解密旧的 mcrypt 加密消息和使用 openssl 加密的新消息。对于旧消息,您必须使用OPENSSL_ZERO_PADDING
标志(示例中为$dec['oRIJ']
),而您不能将其用于openssl 加密消息(示例中为$dec['oAES']
)。在我的情况下,我使用了这种方法,因为 openssl 的默认行为对我来说似乎更正确,因为 mcrypt 一个 - 如果你加密一个 11 个字节的字符串,你会在解密后得到一个 11 个字节的字符串。正如您在示例中看到的那样,mcrypt 或 openssl 以及添加的零填充并非如此。在这些情况下,您必须手动删除尾随零才能取回原始数据。
【讨论】:
1.问题是使用带有 IV 的 CBC 模式,这个答案没有,因此没有回答这个问题。 2.“加密的字符串对于两种方法都不一样”是错误的,相同的输入加密的数据应该是一样的。 1) 问题是“如何在 Mcrypt 弃用后解密?”这个答案所指的。该示例可以轻松调整为任何特定模式。 2)原因是乔的回答已经解释了缺少的零填充。问题也是关于解密的,我关于加密的额外信息只是“额外的”。 考虑升级答案以解决代码中的这些问题。以上是关于Mcrypt弃用后如何解密?的主要内容,如果未能解决你的问题,请参考以下文章