PHP多语言字符串加密乱码的根本原因是编码不统一:必须先用mb_detect_encoding探测原始编码,再用mb_convert_encoding转UTF-8并校验,同时确保key/IV为二进制安全、Base64处理保留padding、清除BOM。

PHP 加密多语言字符串时乱码,根本原因不是加密函数有问题,而是加密前没做统一编码处理——mb_convert_encoding() 或 iconv() 没用对,或根本没用。
加密前必须强制转成 UTF-8(且校验是否已为 UTF-8)
很多中文、日文、阿拉伯文字符串实际是 GBK、Shift-JIS、ISO-8859-6 等编码,直接传给 openssl_encrypt() 或 mcrypt_encrypt()(已废弃)会触发不可见的截断或填充错误,解密后出现乱码或长度异常。
- 先用
mb_detect_encoding($str, ['UTF-8', 'GBK', 'BIG5', 'EUC-JP', 'SJIS'], true)探测原始编码,注意第三个参数true表示 strict 检测,避免误判 - 探测结果为空?说明可能含非法字节,此时应拒绝处理或用
mb_convert_encoding($str, 'UTF-8', 'UTF-8')强制重解释(有损,但比崩溃好) - 确认目标编码后,统一转成 UTF-8:
$utf8_str = mb_convert_encoding($str, 'UTF-8', $detected_encoding) - 转完务必用
mb_check_encoding($utf8_str, 'UTF-8')校验,失败则说明转换出问题,不能继续加密
openssl_encrypt() 的 IV 和 key 必须严格二进制安全
很多人用字符串拼接生成 key 或 IV,比如 md5('mykey'),这看似方便,但 md5() 返回的是 32 字符十六进制字符串,不是 16 字节二进制数据——直接当 AES-128-CBC 的 key 用,等效于用了错误长度和内容的密钥,导致加解密不匹配。
- key 应该是原始二进制:用
hex2bin(md5('mykey'))或更推荐hash('sha256', 'mykey', true)(true参数返回 raw binary) - IV 同理,必须是恰好
openssl_cipher_iv_length('AES-128-CBC')字节(通常是 16),且每次加密都该用random_bytes(16)生成新 IV - IV 不需要保密,但必须和密文一起存储/传输;解密时若 IV 错一位,整个明文全乱,且无报错提示
Base64 编码密文时别漏掉 padding,解密前要还原
加密后得到的是二进制数据,直接 echo 或存数据库会损坏。常见做法是 base64 编码,但 PHP 的 base64_encode() 默认保留 = 补位符,而某些前端或协议(如 URL 传参、HTTP Header)会丢掉 =,导致解密失败。
立即学习“PHP免费学习笔记(深入)”;
- 入库或传输前,用
rtrim(base64_encode($ciphertext), '=')去掉 padding(URL-safe base64) - 解密前,需补回 padding:
$padded = str_pad($encoded, strlen($encoded) % 4, '=', STR_PAD_RIGHT),再base64_decode($padded) - 更稳妥的做法:加密后立即用
base64_encode(),解密前用原样base64_decode(),中间环节确保不经过 URL 解析或表单提交等自动去 = 的流程
最易被忽略的一点:多语言字符串里可能含 BOM(如 UTF-8 BOM \xEF\xBB\xBF),它不算可见字符,但会让 mb_strlen() 多算 3 字节,影响填充逻辑;加密前建议用 ltrim($str, "\xEF\xBB\xBF") 清除开头 BOM。











