求助:将Mcrypt加密算法转换为OpenSSL的实现方案(PHP7.2+兼容)
把Mcrypt加密的字符串用OpenSSL解密的实现方案
我来帮你搞定这个转换!先得拆解下你原来的Mcrypt加密代码的核心细节,这样才能精准用OpenSSL实现对应的解密逻辑:
原Mcrypt加密的关键特性
- 算法:RIJNDAEL-256(注意:这不是AES-256,AES的块大小固定为128位,而RIJNDAEL-256的块大小是256位)
- 模式:ECB(这个模式下IV是无效的,原代码里生成了IV但实际没用到)
- 填充方式:Mcrypt默认用零字节填充(当明文长度不是256位块的整数倍时,补
0x00) - Base64编码:自定义的URL安全Base64,把
+换成-,/换成_,去掉了=填充符
对应的OpenSSL解密代码
下面是完全匹配原加密逻辑的OpenSSL解密实现,每一步都加了注释说明对应原代码的逻辑:
class CryptoHandler { /** * 解密用原Mcrypt加密的字符串 * @param string $encryptedValue 加密后的URL安全Base64字符串 * @param string $key 加密时用的密钥 * @return string|false 解密后的明文,失败返回false */ static function decode($encryptedValue = NULL, $key = NULL) { if (!$encryptedValue || !$key) { return false; } // 还原自定义的URL安全Base64为标准Base64格式 $encoded = str_replace(array('-', '_'), array('+', '/'), $encryptedValue); // 补回可能被去掉的=填充符,确保Base64解码正常 $padding = strlen($encoded) % 4; if ($padding) { $encoded .= str_repeat('=', 4 - $padding); } // Base64解码得到加密后的二进制数据 $crypttext = base64_decode($encoded); if ($crypttext === false) { return false; } // 用OpenSSL解密,严格匹配原算法和模式 // OPENSSL_RAW_DATA:返回原始二进制数据,不自动处理Base64 // OPENSSL_NO_PADDING:禁用OpenSSL默认的PKCS#7填充,改用原Mcrypt的零填充逻辑 $decrypted = openssl_decrypt( $crypttext, 'rijndael-256', // 对应原MCRYPT_RIJNDAEL_256 $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, '' // ECB模式不需要IV,传空字符串即可 ); if ($decrypted === false) { return false; } // 移除零字节填充(原Mcrypt加密时自动补的0x00) // 注意:如果你的明文本身结尾有0x00字符,这一步会误删,这种场景需要调整去填充逻辑 $decrypted = rtrim($decrypted, "\x00"); return $decrypted; } // 如果你需要用OpenSSL实现和原Mcrypt完全一致的加密逻辑,也可以用这个方法 static function encode($value = NULL, $key = NULL) { if (!$value || !$key) { return false; } // 手动给明文加零字节填充,凑到RIJNDAEL-256的块大小(32字节)的整数倍 $blockSize = 32; $paddingLength = $blockSize - (strlen($value) % $blockSize); $paddedValue = $value . str_repeat("\x00", $paddingLength); // OpenSSL加密 $crypttext = openssl_encrypt( $paddedValue, 'rijndael-256', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, '' ); if ($crypttext === false) { return false; } // 用和原代码一致的自定义URL安全Base64编码 return self::safe_b64encode($crypttext); } private static function safe_b64encode($string = NULL) { $data = base64_encode($string); $data = str_replace(array('+','/','='),array('-','_',''),$data); return $data; } }
额外注意事项
- RIJNDAEL-256和AES的区别:别搞混这两个!AES是Rijndael的标准化实现,固定块大小128位,而RIJNDAEL-256的块大小是256位,所以OpenSSL里必须指定
rijndael-256,不能用aes-256-ecb,这是解密成功的关键。 - 填充逻辑的特殊情况:如果你的明文本身结尾有
0x00字符,那rtrim($decrypted, "\x00")会把这些合法字符删掉,这种场景你需要重新评估填充方式,但因为你只需要解密旧数据,只能按原Mcrypt的零填充逻辑处理。 - ECB模式的安全性:顺便提一句,ECB模式是不安全的加密模式,如果后续有新的加密需求,建议改用CBC或GCM模式,搭配随机生成的IV使用。
内容的提问来源于stack exchange,提问作者Tumul Kumar
相关产品推荐
相关产品推荐

