不能直接用 aes.newcipher 做加解密,因为它仅提供裸 aes 块加密,不支持非整块输入、无 iv 和填充机制,易导致 panic 和 ecb 模式缺陷;必须封装 cbc/gcm 等完整模式,明确处理密钥(16/24/32 字节)、随机 iv(16 字节)、pkcs7 填充,并确保编码与错误处理。

为什么不能直接用 aes.NewCipher 做加解密
aes.NewCipher 返回的是纯块加密器,只处理恰好 16 字节的输入。传入 "hello" 这种非整块长度明文会 panic;重复加密相同内容得到相同密文(ECB 模式缺陷);没 IV、没填充、没编码,根本不是可用的“加解密”,只是裸 AES 变换。
封装前必须明确的三要素:密钥、IV、填充
这三项不一致,Go 和其他语言(如 PHP/Java)就无法互通,也容易在自己项目里解密失败。
- 密钥长度必须是
16/24/32字节 ——[]byte("1234567890123456")行,[]byte("password")不行(8 字节) - IV 必须每次加密随机生成(
crypto/rand.Reader),长度固定aes.BlockSize(16 字节),且必须和密文一起传输(通常前置) - CBC 模式下必须手动做 PKCS7 填充,GCM 模式则自动处理,但 nonce 长度要为 12 字节且不可复用
推荐封装结构:一个函数完成 IV + 填充 + 加密 + 编码
避免调用方拼错顺序或漏步骤。比如 CBC 封装可这样写:
func AesEncryptCBC(plainText, key []byte) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", err
}
padded := pkcs7Padding(plainText, aes.BlockSize)
mode := cipher.NewCBCEncrypter(block, iv)
cipherText := make([]byte, len(padded))
mode.CryptBlocks(cipherText, padded)
return base64.StdEncoding.EncodeToString(append(iv, cipherText...)), nil
}
对应解密函数必须先 base64 解码,再拆出前 16 字节为 IV,剩余部分解密,最后 pkcs7Unpadding。别省略任何一环。
生产环境绕不开的两个坑
一是密钥硬编码或从配置读取后没 trim 换行符,导致实际长度多 1–2 字节,aes.NewCipher 直接 panic;二是 IV 复用(比如用 key[:16] 或固定字符串),让相同明文每次加密结果一致,失去语义安全。
真正落地时,密钥应从环境变量读取后经 sha256.Sum256(...).Sum(nil)[:32] 派生;IV 必须走 crypto/rand;密文必须含 IV 且用 base64 编码;所有错误路径都要显式返回,不能忽略 io.ReadFull 的 err。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











