You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将PHP中mcrypt替换为openssl并保证加密解密兼容?

PHP 5.4至8.1升级:mcrypt与OpenSSL加密兼容问题修复

将PHP应用从5.4升级到8.1后,原mcrypt加密解密模块替换为OpenSSL实现,但两套函数无法互相兼容(各自可独立加密解密,但用mcrypt加密的数据无法用OpenSSL解密,反之亦然),且无法通过旧版本PHP中转处理,需直接让OpenSSL实现兼容旧mcrypt逻辑。

旧mcrypt加密解密实现

static function encrypt($key, $plain, $salt = null)
{
    if (is_null($salt))
    {
        $salt = QuickBooks_Encryption::salt();
    }
    
    $plain = serialize(array( $plain, $salt ));
    
    $crypt = mcrypt_module_open('rijndael-256', '', 'ofb', '');

    if (false !== stripos(PHP_OS, 'win') and 
        version_compare(PHP_VERSION, '5.3.0')  == -1) 
    {
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($crypt), MCRYPT_RAND);    
    }
    else
    {
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($crypt), MCRYPT_DEV_URANDOM);
    }

    $ks = mcrypt_enc_get_key_size($crypt);
    $key = substr(md5($key), 0, $ks);
    
    mcrypt_generic_init($crypt, $key, $iv);
    $encrypted = base64_encode($iv . mcrypt_generic($crypt, $plain));
    mcrypt_generic_deinit($crypt);
    mcrypt_module_close($crypt);
    
    return $encrypted;
}

static function decrypt($key, $encrypted)
{
    $crypt = mcrypt_module_open('rijndael-256', '', 'ofb', '');
    $iv_size = mcrypt_enc_get_iv_size($crypt);
    $ks = mcrypt_enc_get_key_size($crypt);
    $key = substr(md5($key), 0, $ks);
    
    $encrypted = base64_decode($encrypted);
    
    mcrypt_generic_init($crypt, $key, substr($encrypted, 0, $iv_size));
    $decrypted = trim(mdecrypt_generic($crypt, substr($encrypted, $iv_size)));
    mcrypt_generic_deinit($crypt);
    mcrypt_module_close($crypt);
        
    $tmp = unserialize($decrypted);
    $decrypted = current($tmp);
    
    return $decrypted;
}

原OpenSSL尝试实现

static function new_encrypt($key, $plain, $salt = null)
{
    if (is_null($salt))
    {
        $salt = QuickBooks_Encryption::salt();
    }
    
    $plain = serialize(array( $plain, $salt ));
    
    $method = "AES-256-OFB";
    $iv_len = openssl_cipher_iv_length($method);
    $iv = openssl_random_pseudo_bytes($iv_len);

    $key = substr(md5($key), 0, 32);
    $encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv);

    return base64_encode($iv . $encrypted);
}

static function new_decrypt($key, $encrypted)
{
    $iv_size = openssl_cipher_iv_length('aes-256-ofb');
    $key = substr(md5($key), 0, 32);

    $encrypted = base64_decode($encrypted);
    $iv = substr($encrypted, 0, $iv_size);
    $encrypted = substr($encrypted, $iv_size);

    $decrypted = openssl_decrypt($encrypted, 'aes-256-ofb', $key, OPENSSL_RAW_DATA, $iv);
    $tmp = unserialize($decrypted);
    $decrypted = current($tmp);

    return $decrypted;
}

核心问题分析

  • 算法块大小不匹配:旧mcrypt使用的rijndael-256是**块大小256位(32字节)**的Rijndael算法;而原OpenSSL代码中的AES-256-OFB是标准AES算法,块大小固定为128位(16字节),两者底层加密逻辑完全不同,导致无法兼容。
  • 解密后处理不一致:旧代码解密后对结果执行了trim()操作,而原OpenSSL代码没有,若加密数据末尾存在空白字符,会导致unserialize失败。

修复后的兼容版OpenSSL实现

static function new_encrypt($key, $plain, $salt = null)
{
    if (is_null($salt))
    {
        $salt = QuickBooks_Encryption::salt();
    }
    
    $plain = serialize(array( $plain, $salt ));
    
    // 使用与mcrypt一致的块大小256位的Rijndael-OFB算法
    $method = "rijndael-256-ofb";
    $iv_len = openssl_cipher_iv_length($method);
    $iv = openssl_random_pseudo_bytes($iv_len);

    // 与旧代码密钥处理逻辑完全对齐:取md5十六进制字符串的前32个字符作为密钥
    $ks = 32;
    $key = substr(md5($key), 0, $ks);
    $encrypted = openssl_encrypt($plain, $method, $key, OPENSSL_RAW_DATA, $iv);

    return base64_encode($iv . $encrypted);
}

static function new_decrypt($key, $encrypted)
{
    $method = "rijndael-256-ofb";
    $iv_size = openssl_cipher_iv_length($method);
    $ks = 32;
    $key = substr(md5($key), 0, $ks);

    $encrypted = base64_decode($encrypted);
    $iv = substr($encrypted, 0, $iv_size);
    $encrypted = substr($encrypted, $iv_size);

    $decrypted = openssl_decrypt($encrypted, $method, $key, OPENSSL_RAW_DATA, $iv);
    // 与旧代码一致,添加trim处理
    $decrypted = trim($decrypted);
    $tmp = unserialize($decrypted);
    $decrypted = current($tmp);

    return $decrypted;
}

验证说明

  • 确保你的OpenSSL版本支持rijndael-256-ofb算法,可通过var_dump(openssl_get_cipher_methods())查看支持的列表。
  • 修复后的函数可以解密旧mcrypt加密的数据,同时加密的数据也能被旧mcrypt函数解密(如需双向兼容)。

内容的提问来源于stack exchange,提问作者LostPhysx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 02:09:18