Mcrypt 弃用后如何解密?

How to decrypt after Mcrypt deprecation?

我已将 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, "[=10=]");
}

有常量:

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;
}

需要注意的重要一点是,如果输入数据不是块大小的倍数,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, "[=10=]");
echo var_export($result, true) . "\n";

输出:

70168f2d5751b3d3bf36b7e6b8ec5843
'Lorem ipsum' . "[=11=]" . '' . "[=11=]" . '' . "[=11=]" . '' . "[=11=]" . '' . "[=11=]" . ''
'Lorem ipsum'

我解决了。 不知道这是否是正确的方法(猜不对) 但是远程连接到 php 版本较低的服务器上。 解密所有内容并使用 OpenSSL 加密。

感谢您的建议!

我在解密使用 mcrypt_encryptopenssl_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, "[=10=]");
$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_rvar_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"
}

如果您需要使用 openssl 方法使用与使用 mcrypt 时相同的加密字符串,您需要手动将零填充添加到字符串中(示例中的 AES0)。这样您将获得与以前完全相同的加密和解密字符串。有关零填充的一些其他信息,您应该在此处查看 Joe 的回答:

如果您不想手动将零填充添加到所有新邮件,您将需要不同的标志来解密旧的 mcrypt 加密邮件和使用 openssl 加密的新邮件。对于旧消息,您必须使用 OPENSSL_ZERO_PADDING 标志(示例中的 $dec['oRIJ']),而您不得将其用于 openssl 加密消息(示例中的 $dec['oAES'])。在我的例子中,我使用了这种方法,因为 openssl 的默认行为对我来说似乎比 mcrypt 更正确——如果你用 11 个字节加密一个字符串,你会在解密后得到一个有 11 个字节的字符串。正如您在示例中看到的那样,使用 mcrypt 或使用 openssl 和添加的零填充时情况并非如此。在这些情况下,您必须手动删除尾随零才能取回原始数据。