aes_encrypt返回二进制字符串(blob/varbinary类型),非乱码而是设计使然;存入varchar易因字符集不匹配导致截断或null,须用varbinary/blob存储,中文需先转utf8mb4或走to_base64路径,密钥长度须为16/24/32字节。

AES_ENCRYPT返回值本身就是二进制,不是“乱码”而是预期行为
调用 AES_ENCRYPT() 后看到一堆不可读字符(比如 諍=鈿巫? V極Ш),这不是 bug,是函数设计使然:它返回的是原始二进制字符串(BINARY 类型),MySQL 客户端按当前字符集(如 utf8mb4)强行尝试解码这些字节,自然显示为乱码。这不是数据损坏,只是“用错眼镜看二进制”。
存进 VARCHAR 字段却变 NULL 或截断,大概率是字符集/类型不匹配
把 AES_ENCRYPT() 结果直接 INSERT 到 VARCHAR 字段,极易出问题:
- 字段字符集是
latin1或utf8mb4,但二进制流里含非法字节序列 → 插入时被静默替换或截断 - 字段定义没加
COLLATE utf8mb4_bin→ 比较、索引失效,且隐式转换可能破坏数据 - 字段长度不够(如
VARCHAR(32))→ AES 输出长度可超 32 字节(尤其 CBC 模式+IV),直接被砍掉
正确做法是:加密字段必须用 VARBINARY 或 BLOB 类型,且长度至少设为 VARBINARY(255)(兼顾 128/256 位密钥输出上限)。
SELECT 出来显示乱码,根本原因常在连接层或客户端设置
即使字段类型正确,你仍可能看到乱码,检查这三点:
- 执行
SET NAMES utf8mb4后再查 —— 否则客户端用默认字符集(如latin1)解码二进制,必然错 - 应用代码里数据库连接 URL 是否带
?charset=utf8mb4(JDBC/PHP PDO 等都需显式声明) - 用命令行
mysql -u -p --default-character-set=utf8mb4连,避免终端环境干扰
如果只是想“看一眼”内容,别直接 SELECT 加密字段,改用:SELECT HEX(password) FROM user_data —— 十六进制全是 ASCII,绝对不乱码。
中文加密后解密失败或为空,90% 是没做编码桥接
对含中文的字符串(如 "张三123")直接 AES_ENCRYPT(),解密后用 AES_DECRYPT() 得到空字符串或乱码,常见原因:
- 明文本身不是有效 UTF-8(比如从 GBK 环境传入)→ 加密前先
CONVERT("张三123" USING utf8mb4) - 解密结果是二进制,直接当字符串输出 → 必须显式转码:
CONVERT(AES_DECRYPT(...) USING utf8mb4) - 更稳妥路径:全程走文本安全通道 —— 加密前
TO_BASE64("张三123"),再AES_ENCRYPT(...);解密后先AES_DECRYPT(...),再FROM_BASE64(...)
Base64 这步看似多此一举,实则是绕过所有字符集陷阱的最简方案:它把任意二进制转成纯 ASCII,存储、传输、调试全无压力。
真正容易被忽略的点:AES 密钥长度必须严格为 16/24/32 字节,传入 '123' 或 'mykey123'(10 字节)会导致整个加密静默失败,返回 NULL —— 而你可能还在排查字符集。











