crypto/aes 仅提供块加密器,需组合 cipher 模式、pkcs#7 填充、随机 iv/nonce 及编码封装才安全;密钥必须为 16/24/32 字节,否则 panic;cbc 解密不报错但输出乱码,gcm 更安全且带完整性校验。

crypto/aes 本身不提供安全的加解密能力——它只返回一个块加密器,用错就 panic 或解密出乱码。真正能落地的对称加密,必须组合 crypto/cipher 模式、严格填充、随机 IV/nonce,并做编码封装。
crypto/aes.NewCipher 报 "invalid key size" 怎么办
这个错误只有一种原因:传给 aes.NewCipher 的密钥字节长度不是 16、24 或 32。
-
[]byte("1234567890123456")是 16 字节,合法;[]byte("mypass")是 6 字节,直接 panic - 中文密钥如
[]byte("密码123")在 UTF-8 下是 9 字节(“密”“码”各 3 字节),也不合法 - 从环境变量读取后没
strings.TrimSpace,末尾带换行符,长度多 1–2 字节,同样触发 panic
正确做法是固定派生或截取:
// 推荐:SHA256 派生后取前 32 字节(适配 AES-256)
key := sha256.Sum256([]byte(os.Getenv("AES_KEY"))).Sum(nil)[:32]
// 或明确指定 16 字节(AES-128),但别硬编码
key := []byte("1234567890123456") // 必须恰好 16 字节,不可增减
CBC 模式下解密全是乱码,为什么没报错
cipher.NewCBCDecrypter 的 CryptBlocks 方法从不返回 error——它只是机械执行异或和轮函数。密钥错、IV 错、密文被截断,它都照常输出垃圾字节。
- 解密前必须先切出前
aes.BlockSize(即 16 字节)作为 IV,不能直接拿整个密文喂给NewCBCDecrypter - 明文必须 PKCS#7 填充,解密后必须校验末尾字节值:若最后一个字节是
0x0F,则倒数 15 字节也得全是0x0F,否则说明填充损坏 - 常见错误是跳过填充校验,直接
string(decrypted),结果传给json.Unmarshal时 panic
该选 CBC 还是 GCM 模式
GCM 是现代首选,CBC 已不推荐用于新项目。
-
cipher.NewGCM返回 AEAD 接口,自动带完整性校验,解密失败会明确返回cipher.AEADDecryptError,而不是静默乱码 - GCM 的 nonce 长度建议 12 字节(Go 默认),用
io.ReadFull(rand.Reader, nonce)生成,且绝对禁止复用 - CBC 没有认证能力,攻击者翻转密文某字节,解密后只影响当前块和下一字节——看似无害,实则可能绕过 token 校验逻辑
文件加密时最容易忽略的两个硬伤
大文件场景下,仅靠 ioutil.ReadFile + 内存加密会爆内存;而盲目套 io.Copy 又会破坏块对齐,导致最后一块解密失败。
- 流式加密必须分块处理:缓冲区大小设为 64KB(需是 16 的倍数),每块单独 PKCS#7 填充 → 加密 → 写入
- IV 必须每次加密都重新生成,且明文前置到密文开头(标准做法),解密时才能直接读取
- 密钥和 IV 绝不能硬编码,也不能从配置文件明文读取后直译成
[]byte;必须通过环境变量注入,并在运行时用安全方式派生
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











