php 5 升级到 php 7 后,因 mcrypt 扩展被彻底移除、openssl 加解密函数参数与填充规则变更、key/iv 长度校验变严格,以及二进制密文未 base64 编码导致截断等问题,原有加密逻辑必然失败,必须全面重构。

PHP 5 和 PHP 7 在加密相关函数上存在实质性不兼容,尤其是 mcrypt 扩展被彻底移除、openssl_encrypt/openssl_decrypt 行为变更、以及默认填充方式差异,直接导致老项目中依赖 mcrypt_encrypt 的加解密逻辑在 PHP 7+ 上必然失败。
mcrypt_* 函数在 PHP 7.2+ 已完全不可用
PHP 7.1 开始标记 mcrypt 为废弃,7.2 起彻底移除。调用 mcrypt_encrypt、mcrypt_module_open 等会报 Fatal error: Uncaught Error: Call to undefined function mcrypt_encrypt()。
- 不能靠安装扩展“恢复”——PHP 7.2+ 源码中已无该模块入口
- 替代方案只有
openssl_encrypt/openssl_decrypt,但二者参数顺序、默认模式、填充逻辑均不同 - 若原逻辑使用
MCRYPT_RIJNDAEL_128+MCRYPT_MODE_CBC,需映射为'AES-128-CBC',且 IV 长度必须严格为 16 字节
openssl_encrypt 默认使用 PKCS#7 填充,而 mcrypt 默认是零填充
老项目若没显式补位(如用 str_pad($data, 16 * ceil(strlen($data)/16), "<p>老项目若没显式补位(如用 <code>str_pad($data, 16 * ceil(strlen($data)/16), "\0")),在 PHP 5 下可能靠 mcrypt 自动截断或容忍不齐整数据;但 openssl_encrypt 严格要求输入长度是块大小整数倍,否则报 error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length。
openssl_encrypt 严格要求输入长度是块大小整数倍,否则报 error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length。
- 必须手动补位:PKCS#7 填充需按字节值补(如缺 3 字节则补
"\x03\x03\x03"),不能简单补"\0" - 解密后必须手动去 PKCS#7 填充,不能直接
rtrim($decrypted, "\0") - 推荐复用标准填充函数,例如:
function pkcs7_pad($data, $block_size = 16) { $pad = $block_size - (strlen($data) % $block_size); return $data . str_repeat(chr($pad), $pad); }
key 和 iv 处理方式不一致导致跨版本解密失败
mcrypt 对 key 长度不敏感(短 key 会内部 hash 或重复填充),而 openssl 要求 key 长度与算法严格匹配(如 AES-128 必须 16 字节)。同样,mcrypt 允许传入任意长度 IV 并自动截取,openssl 则要求 IV 长度精确等于算法块大小(AES 是 16)。
立即学习“PHP免费学习笔记(深入)”;
- 若原代码用 md5($key) 当 AES key,在 PHP 5 下可能“刚好能用”,但在 PHP 7 下需确认是否真的用了 16 字节 ——
md5()返回 32 字符十六进制串,应转为二进制:hex2bin(md5($key))或改用hash('sha256', $key, true)截前 16 字节 - IV 若来自 substr($str, 0, 16),要确保 $str 是二进制安全字符串(非 UTF-8 编码污染)
- 建议统一用
random_bytes(16)生成 IV,并和密文一起 base64 输出,避免编码歧义
最易被忽略的是:老项目常把加密结果直接 echo 或存数据库,没做 base64 编码,而二进制密文含不可见控制字符,入库/传输时会被截断或转义 —— 这类问题在 PHP 5 下可能因环境宽松“凑合跑通”,升级到 PHP 7 后反而暴露得更彻底。











