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

MySQL升级到8并迁移服务器后PHP openssl_decrypt解密失败,求助解密数据库现有加密数据

MySQL升级到8并迁移服务器后PHP openssl_decrypt解密失败,求助解密数据库现有加密数据

首先,结合你MySQL升级+服务器迁移的背景,再加上遇到的两个解密错误,大概率是加密数据存储过程中被篡改或者密钥/环境兼容性出了问题,给你一步步排查和解决的思路:

一、先排查最可能的元凶:数据库存储的加密数据被损坏了

你的加密函数返回的是二进制数据($iv + $hmac + $ciphertext_raw都是二进制流),如果原来的EmployeeId字段用的是CHAR/VARCHAR这类字符串类型,而非BLOB/BINARY二进制类型,那么在MySQL从5.7升级到8、字符集切换到utf8mb4的过程中,二进制数据会被当作字符串进行编码转换,导致部分字节被替换成无效字符(比如�),最终读取出来的字符串和原始加密数据完全不一致。

验证&修复方法:

  1. 检查字段类型:先确认原数据库中EmployeeId的字段类型,如果是字符串类型,立刻改成BLOB(或MEDIUMBLOB,根据数据长度调整)。
  2. 尝试恢复原始字节:如果没有备份,试试在查询时将数据转回到原来的编码(比如原来用的是latin1):
    // 修改查询语句,强制用latin1编码读取EmployeeId
    $sql = " SELECT NetId, CONVERT(EmployeeId USING latin1) AS EmployeeId, PrimaryRole, FirstName, ... FROM Employee WHERE NetId = :field1 ";
    
    用这个转换后的数据去解密,因为latin1是单字节编码,能完整保留原始二进制字节的信息,转成utf8mb4时被破坏的数据可能会恢复。
  3. 检查数据长度:在解密函数里加调试代码,确认读取到的加密数据长度是否符合预期:
    public function decrypt($encrypted_string)
    {
        $cipher = 'AES-256-CBC';
        $ivlen = openssl_cipher_iv_length($cipher);
        $sha2len = 32;
        
        // 输出调试信息到错误日志
        error_log("读取到的加密数据长度:" . strlen($encrypted_string));
        error_log("预期最小长度(IV+HMAC):" . ($ivlen + $sha2len));
        error_log("实际截取的IV长度:" . strlen(substr($encrypted_string, 0, $ivlen)));
        error_log("实际截取的HMAC长度:" . strlen(substr($encrypted_string, $ivlen, $sha2len)));
        
        // 原解密代码...
    }
    
    如果实际长度比预期小,或者截取后的IV/HMAC长度不对,那肯定是数据被截断或篡改了。

二、检查密钥长度的兼容性问题

你用的是AES-256-CBC,它要求密钥必须是32字节(256位),新服务器的PHP/OpenSSL版本可能对密钥长度的检查更严格:旧环境可能会自动截断/补全不符合长度的密钥,而新环境直接抛出invalid key length错误。

验证&修复方法:

  1. 确认密钥实际字节长度:在加密和解密函数里,先输出密钥的字节数:
    error_log("密钥字节长度:" . strlen($this->encryption_key));
    
    如果不是32字节,要么调整密钥到32字节,要么用哈希函数把密钥转换成标准的32字节(推荐这种,兼容性更好):
    // 在加密和解密时,统一用sha256哈希生成32字节的密钥
    $aes_key = hash('sha256', $this->encryption_key, true);
    // 然后把$aes_key传给openssl_encrypt/openssl_decrypt
    $ciphertext_raw = openssl_encrypt($pure_string, $cipher, $aes_key, $options, $iv);
    // 解密时同理
    $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $aes_key, $options, $iv);
    
    注意:这个修改要同时应用到加密和解密函数,确保新旧数据的密钥处理逻辑一致。

三、检查PHP/OpenSSL版本差异

新服务器的PHP版本、OpenSSL版本可能和旧服务器不同,比如某些旧版本的OpenSSL允许非标准密钥长度,新版本则严格遵循规范。你可以在新旧服务器上分别执行phpinfo(),对比OpenSSL扩展的版本和配置。

如果是版本差异导致的,除了调整密钥长度,还可以尝试降级/升级OpenSSL版本(不过更推荐调整密钥逻辑,毕竟版本升级是趋势)。

四、临时修复:尝试恢复损坏的数据

如果数据已经被编码破坏,还可以试试用字符编码转换工具把读取到的字符串转成原始二进制:

// 把UTF-8编码的字符串转成latin1,恢复原始字节
$fixed_encrypted_string = mb_convert_encoding($encrypted_string, 'ISO-8859-1', 'UTF-8');
// 用转换后的数据解密
$EmployeeId = $OpensslEncryption->decrypt($fixed_encrypted_string);

最后提醒你:以后存储加密数据一定要用二进制字段(BLOB/BINARY),避免字符集转换带来的数据损坏;另外,加密函数最好返回十六进制字符串(就是你注释掉的bin2hex),这样存储在字符串字段里也不会有问题,兼容性更强。

备注:内容来源于stack exchange,提问作者ddavidd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 10:19:33