aes_encrypt 返回 null 是因密钥长度(16/24/32字节)、cbc模式与iv、hex编码存储三者未对齐;须用sha2生成密钥、显式传16字节iv、hex封装密文、盐iv按记录独立生成并存储。

AES_ENCRYPT 返回 NULL 不是函数坏了,而是密钥、模式、字段类型三者没对齐
密钥长度必须是 16/24/32 字节,否则静默失败
传入 'abc' 或 'mykey123' 这类短字符串,MySQL 会补零或截断,但加解密两端若长度不一致,AES_ENCRYPT() 和 AES_DECRYPT() 都返回 NULL,且不报错。
- 用
SHA2('your_master_key', 256)生成 64 字符 hex 字符串,再取前 32 位转二进制:UNHEX(LEFT(SHA2('your_master_key', 256), 64)) - 避免硬编码密钥;生产环境应从会话变量(如
@aes_key)或配置表读取 - 密钥不能含非 ASCII 字符或控制符——
CONVERT('密钥' USING utf8mb4)确保字节长度可预期
必须显式指定 CBC 模式并传入 IV,ECB 已淘汰
MySQL 5.7.6+ 支持带 IV 的 AES-CBC,但默认仍走 ECB(无 IV、不安全)。不显式传 IV 就用 ECB,加密相同明文永远得相同密文,极易被统计攻击。
- IV 必须是 16 字节二进制值,例如:
UNHEX('000102030405060708090a0b0c0d0e0f') - 每次加密必须用新 IV,且需和密文一起存储(比如存进
iv_hex字段) - 调用格式为:
AES_ENCRYPT(plain_text, key, iv),三参数缺一不可;漏掉iv参数就退化为 ECB
加密结果不能直接插进 VARCHAR,必须 HEX 编码后存储
AES_ENCRYPT() 输出是二进制流,如果目标字段是 VARCHAR + utf8mb4_general_ci,插入时会被字符集转换截断或污染,后续 UNHEX() 也还原不出原始密文。
- 推荐建表方式:
encrypted_data VARCHAR(512) COLLATE utf8mb4_bin(不用VARBINARY,省去类型强约束) - 写入时套一层
HEX():HEX(AES_ENCRYPT('hello', @key, @iv)) - 读取时先
UNHEX()再AES_DECRYPT():AES_DECRYPT(UNHEX(encrypted_data), @key, @iv) - 解密后若要转字符串,加
CONVERT(... USING utf8mb4),但仅当原始明文是合法 UTF-8 时才安全
带盐加密必须把盐和密文分开存,且盐不可复用
AES_ENCRYPT 本身不支持盐。所谓“加盐”,本质是把随机盐拼进密钥,所以盐必须随记录持久化,且每次加密独立生成。
- 生成盐建议用:
SUBSTR(SHA2(RAND(), 256), 1, 16)或UUID()(后者更随机) - 密钥构造:用
CONCAT(@static_key, salt),而非简单拼接明文 - 表结构至少两字段:
salt VARCHAR(36)+data_enc VARCHAR(512) - 解密时先查出 salt,再拼密钥,再
AES_DECRYPT(UNHEX(data_enc), CONCAT(@static_key, salt), @iv)
最容易被忽略的是:IV 和 salt 都必须按记录粒度生成并存储,不能全局复用;密钥长度、HEX/UNHEX、CBC 模式三者只要一个没对齐,解密就静默失败——而你查 LENGTH() 会发现密文确实存进去了,只是再也解不出来。











