【问题标题】:How can I solve this OpenSSL "EVP_DecryptFinal_ex:bad decrypt" error in PHP?如何解决 PHP 中的这个 OpenSSL“EVP_DecryptFinal_ex:bad decrypt”错误?
【发布时间】:2021-05-27 02:23:16
【问题描述】:

设置:

session_start();

function set_encryption_method() {

if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > 3600) {
    unset($_SESSION['cipher']);
    unset($_SESSION['iv']);
    unset($_SESSION['last_activity']);
}

$cipher = 'aes-256-cbc';
$iv = random_bytes(16);    
    
    if (in_array($cipher, openssl_get_cipher_methods())) {
        if (!isset($_SESSION['cipher'])) {
            $_SESSION['cipher'] = $cipher;
        }
        if (!isset($_SESSION['iv'])) {
            $_SESSION['iv'] = $iv;
        }
        $_SESSION['last_activity'] = time();
    } else {
        die('Encryption method not supported!');
    }
}

set_encryption_method();

加密:

function encrypt_string($key, $string) {
    // $key is a constant stored in a database
    return rawurlencode(base64_encode(openssl_encrypt($string, $_SESSION['cipher'], $key, 0, $_SESSION['iv'])));
}

解密:

function decrypt_string($key, $encrypted) {
    // $key is a constant stored in a database
    return openssl_decrypt(rawurldecode(base64_decode($encrypted)), $_SESSION['cipher'], $key, 0, $_SESSION['iv']);
}

当使用适当的参数调用decrypt_string() 时,它会抛出此错误:digital envelope routines evp_decrypt_final_ex: bad decrypt。如果我对 iv 进行硬编码,那么它可以正常工作。

我做错了什么?

【问题讨论】:

  • 您为什么不调试“openssl_encrypt”和“openssl_decrypt”的输入,然后“跟踪数据”以找出IV get 的更改位置?
  • 很遗憾,我无法控制服务器,公司也不允许安装 XDebug 之类的东西
  • 回显 iv 的 base64_Encode 和 strlen。 strlen 确认它已设置,base64_encode 确认它们都是相同的。
  • 你能提供更多关于你的环境的信息吗?我cannot reproduce.
  • 通常情况下,您会将加密数据和 IV 存储在一起,因为只有在两者都保持不变的情况下才能进行解密。您能否确认您使用相同的 IV 进行加密和解密?您是否不会在两个不同的会话中调用加密和解密功能?或者在两者之间调用set_encryption_method(),而一个多小时过去了?

标签: php encryption openssl


【解决方案1】:

错误消息是(间接)由您使用不同的 IV 进行加密和解密这一事实引起的。根据您的描述,尚不清楚这是如何发生的,但让我提出一些可以完全避免您的问题的建议。

首先,对于 EAS-CBC,多次使用相同的 IV + 组合键并不是一个好主意。您可以在Is AES in CBC mode secure if a known and/or fixed IV is used? 的答案中找到一些讨论。您没有提及您如何使用不同的功能,但在 3600 秒内,您使用的是相同的 IV + 组合键。

要解决此问题,您可以为您执行的每次加密生成一个随机 IV。然后,您可以将 IV 与加密数据一起存储; IV 不需要或不应该是秘密的。以下两个函数是您的修改,通过在加密后连接两者并在解密前拆分两者:

function encrypt_string($key, $string) {
    // $key is a constant stored in a database
    $iv = random_bytes(16);
    $ciphtxt = openssl_encrypt($string, $_SESSION['cipher'], $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($iv.$ciphtxt);
}

function decrypt_string($key, $encrypted) {
    // $key is a constant stored in a database
    $combo = base64_decode($encrypted);
    $iv = substr($combo, 0, 16);
    $ciphtxt= substr($combo, 16);
    return openssl_decrypt($ciphtxt, $_SESSION['cipher'], $key, OPENSSL_RAW_DATA, $iv);
}

注意标志OPENSSL_RAW_DATA的使用。正如the documentation for openssl_encrypt 提到的(不太清楚),如果您不提供该标志,结果将是base64-ed。你自己在做base64,所以我添加了标志。这也使得处理 IV 和密文的(反)连接变得更容易。


关于bad decrypt 错误发生的几句话:当填充字节与预期不同时发出。由于您使用了不正确的 IV 进行解密,因此 AES 解密不会产生原始数据,因此填充字节很可能没有正确的值。

【讨论】:

    【解决方案2】:

    首先指出:为什么要使用 URL 编码/解码?

    那些用于通过浏览器传递参数;并且浏览器通常会为您进行解码,因此作为基本规则 - 如果您在 PHP 中编写了“urldecode”,那么您可能出错了,因为您可能正在解码已经解码的内容。 (或者没有在正确的地方使用,或者在不必要的时候使用)

    不过,Base64 是(或应该是)url 安全的,所以我建议取消 urlencode/decode 看看会发生什么。


    第二点地址:需要按照你设置的相反顺序撤消urlencode/base64转换。

    所以rawurlencode(base64_encode(openssl_encrypt( 反转是openssl_decrypt(base64_encode(rawurldecode(你有 base64 和 url 解码翻转)。但是完全取消 urlencode/decode(除非你坚持需要它)也可以解决这个问题。

    【讨论】:

    • 感谢您的回复。我删除了rawurlencoderawurldecode,但我仍然收到相同的bad decrypt 错误。
    猜你喜欢
    • 1970-01-01
    • 2016-07-29
    • 2022-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多