如何将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
相关产品推荐
相关产品推荐

