crypto/aes包仅提供aes块加密,不支持完整加解密流程;必须手动组合模式、填充、iv管理及编码,否则易出错或不安全。

Go 语言标准库的 crypto/aes 包本身只提供底层分组加密能力(即 AES 块加密),**不直接支持完整加解密流程**。你必须自己组合模式(如 CBC、CFB)、填充(如 PKCS7)、IV 管理和编码,否则极易出错或不安全。
为什么直接用 aes.NewCipher 加密会失败或解密乱码?
aes.NewCipher 返回的是一个纯块加密器(block cipher),它只对恰好 16 字节的输入做一次 AES 变换。常见错误包括:
- 传入长度不是 16 字节的明文(比如
"hello"),c.Encrypt(out, src)会 panic 或静默越界写入 - 重复用同一密钥加密相同明文,得到完全相同的密文(ECB 模式缺陷),暴露数据模式
- 没做填充,导致解密后末尾多出垃圾字节或 panic
- 没处理 IV(初始化向量),在 CBC/CFB 等模式下无法正确解密
所以不能只写 c.Encrypt(out, []byte(plaintext)) 就完事——这本质上是 ECB 模式,且未校验长度。
如何安全地用 CBC 模式加密?必须补全 + IV + cipher.BlockMode
CBC 是最常用也相对易用的模式,但需三步缺一不可:
- 对明文做 PKCS7 填充(
pkcs7Padding),确保长度是 16 字节整数倍 - 生成随机 IV(16 字节),并和密文一起保存/传输(IV 不需要保密,但绝不能复用)
- 用
cipher.NewCBCEncrypter包装块加密器,再调用CryptBlocks
示例关键片段:
func AesEncryptCBC(origData, key []byte) []byte {
block, _ := aes.NewCipher(key)
blockSize := block.BlockSize()
origData = pkcs7Padding(origData, blockSize)
iv := make([]byte, blockSize)
rand.Read(iv) // 注意:生产环境用 crypto/rand
blockMode := cipher.NewCBCEncrypter(block, iv)
encrypted := make([]byte, len(origData))
blockMode.CryptBlocks(encrypted, origData)
return append(iv, encrypted...) // IV 放密文前
}
解密时先拆出前 16 字节为 IV,再用 NewCBCDecrypter 处理剩余部分,最后去掉 PKCS7 填充。
encoding/hex 和 encoding/base64 选哪个?
二进制密文不能直接当字符串打印或存数据库,必须编码。两者区别很实际:
-
hex.EncodeToString输出全是 0–9 a–f,长度翻倍(每字节变 2 字符),适合调试或日志 -
base64.StdEncoding.EncodeToString输出更短(约 33% 增长),且是标准文本协议(HTTP、JSON)友好格式,**生产环境优先选它** - 别混用:加密用 hex 编码,解密却用 base64 解码,会直接报
illegal base64 data
注意:无论选哪个,编解码必须成对,且不能在中间做任何字符串截断或大小写转换。
密钥长度不匹配会触发什么错误?
aes.NewCipher 对密钥长度极其严格:必须是 16、24 或 32 字节(对应 AES-128/192/256)。常见陷阱:
- 用字符串字面量写密钥,比如
"mykey"只有 5 字节 → 报错invalid key size - 误把十六进制字符串当密钥(如
"a1b2c3..."),实际是 32 个字符而非 16 字节 → 需先hex.DecodeString - 从配置读密钥时没 trim 换行符或空格,导致长度多 1 → 同样触发
invalid key size
建议:密钥统一用 32 字节(AES-256),生成方式用 crypto/rand.Read(key),避免手输错误。
真正麻烦的不是写几行加密代码,而是 IV 怎么安全传递、填充怎么无损去除、错误处理是否覆盖所有边界(比如解密时填充验证失败)、以及是否无意中复用了 IV —— 这些细节不显眼,但一错就导致整个加密形同虚设。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











