aes_decrypt()返回null或空字符串并非函数失效,而是解密链路中断:密钥长度非16/24/32字节、cbc模式下iv缺失、hex未还原、字段类型不匹配(如varchar存二进制),或convert转码失败;典型现象是length()显示有字节数但结果为空,说明解密成功但二进制输出被隐式转换丢弃。

为什么AES_DECRYPT()返回NULL或空字符串
不是函数失效,而是解密链路上某个环节断了:密钥长度不对、IV缺失(CBC模式下)、HEX未还原、字段类型不匹配。最典型现象是AES_DECRYPT()返回NULL或长度为0的字符串,但LENGTH(AES_DECRYPT(...))却显示有字节数——说明解密成功了,只是结果被MySQL当二进制丢弃或转码失败。
- 密钥必须严格为16/24/32字节,
'mykey'(5字节)会静默失败;建议用UNHEX(LEFT(SHA2('your_key', 256), 32))生成32字节密钥 - CBC模式必须传IV,且IV必须是16字节二进制值,不能是字符串;例如
UNHEX('000102030405060708090a0b0c0d0e0f') - 如果加密时用了
HEX(AES_ENCRYPT(...))存VARCHAR,解密前必须先UNHEX(encrypted_col),否则传入的是十六进制字符串而非二进制 - 解密结果是二进制,直接SELECT会触发隐式字符集转换;必须显式
CONVERT(AES_DECRYPT(...) USING utf8mb4)或CAST(... AS CHAR)
解密语句怎么写才不会丢数据
关键在“还原→解密→转码”三步不能少,顺序错一步就变NULL。尤其注意:HEX存、UNHEX读;二进制解密、字符转码输出。
- 假设字段
pwd_enc存的是HEX(AES_ENCRYPT('hello', @key, 'AES-256-CBC', @iv)),则解密必须写成:CONVERT(AES_DECRYPT(UNHEX(pwd_enc), @key, 'AES-256-CBC', @iv) USING utf8mb4) - 如果原始明文含非UTF-8内容(如二进制blob),不要用
CONVERT,改用TO_BASE64(AES_DECRYPT(...))避免乱码 - 别在WHERE里直接用
AES_DECRYPT()做条件匹配——性能极差,且可能因NULL导致逻辑错误;应提前解密后缓存或用应用层比对
建表和字段类型怎么配才不出错
字段类型选错,等于把加密结果往墙上撞。VARCHAR+utf8mb4默认会截断二进制、丢字节、报Warning;VARBINARY或BLOB才是原生支持者。
- 推荐方案:字段定义为
VARCHAR(255) COLLATE utf8mb4_bin,配合HEX()存取——绕开所有字符集陷阱 - 不推荐方案:
VARCHAR(255)无COLLATE,或TEXT没指定binary collation;插入时MySQL会报Incorrect string value警告,实际存入的是截断后的无效值 - 若坚持存二进制,必须用
VARBINARY(255)或BLOB,且连接字符集设为binary(SET NAMES binary),否则客户端可能二次转码
带盐解密时盐值怎么参与运算
MySQL没有内置加盐机制,盐必须和密文一起存,并在解密时动态拼密钥。盐不是装饰,是安全前提;漏掉或拼错,解密必然失败。
- 插入时生成盐:
SUBSTR(SHA2(RAND(), 256), 1, 16),存入salt列 - 加密用密钥:
CONCAT(@static_key, salt),不是@static_key单独用 - 解密时先查出
salt,再构造相同密钥:AES_DECRYPT(UNHEX(pwd_enc), CONCAT(@static_key, salt), ...) - 盐必须每条记录独立生成,不能全局复用;否则批量破解风险陡增
CONVERT(... USING utf8mb4)里的utf8mb4——它不是可选项,是硬性依赖。如果原始明文是GBK编码,或者含控制字符,这个转换就会失败或失真。这时候得退回到BASE64路径,而不是强行转字符集。











