加密前须确认密钥管理方式:AES-256需32字节密钥,禁用硬编码与字符串直转,推荐sha256哈希后截取;GCM模式优于CBC,需12字节唯一nonce,密文结构为nonce+ciphertext+auth_tag;大文件应流式处理,解密失败必须显式校验error。

加密前必须确认密钥管理方式
Go 本身不强制密钥存储策略,但 crypto/aes 和 crypto/cipher 要求密钥长度严格匹配(如 AES-256 需 32 字节),硬编码密钥或直接用字符串转 []byte 极易出错。常见错误是调用 md5.Sum([]byte("pass")) 后忘了取 .Sum(nil),导致密钥只有 16 字节(md5 是 16 字节,AES-256 需要 32)。
实操建议:
- 用
sha256.Sum256生成 32 字节密钥,再截断或拼接:例如sha256.Sum256([]byte("my-secret-key")).Sum(nil)[:32] - 避免从环境变量直接读字符串后调用
[]byte(env)—— UTF-8 编码长度不可控,应先哈希再裁剪 - 若需用户输入口令,务必加盐(salt),且 salt 必须随密文一起保存(如前缀 16 字节),否则无法解密
使用 AES-GCM 模式比 CBC 更安全也更简单
CBC 模式需要手动处理填充(crypto/padding)、IV 管理、MAC 校验,而 crypto/cipher.NewGCM 内置认证加密,一次调用完成加密+签名,解密失败直接 panic 或返回 error(不会静默解出乱码)。
实操建议:
- 初始化时用
aes.NewCipher(key)得到 cipher.Block,再传给cipher.NewGCM(block) - IV(nonce)长度必须为 12 字节(GCM 推荐值),用
rand.Read(nonce[:])生成,**每次加密都必须不同** - 密文结构建议为:
nonce + ciphertext + auth_tag(GCM 的Seal方法自动追加 tag,长度 16 字节) - 不要复用 IV+密钥组合,否则 GCM 安全性崩塌 —— 这是线上事故最高发点
读写文件时别直接加密整个 []byte
大文件(>10MB)全量加载进内存会触发 GC 压力甚至 OOM,且无法中断或校验进度。Go 的 io.ReadWriter 接口天然支持流式加解密。
实操建议:
- 用
os.Open和os.Create获取*os.File,再套一层cipher.StreamReader/cipher.StreamWriter(仅适用于 CTR、OFB 等流模式) - 若坚持用 GCM,只能分块:每次读 ≤ 64KB → 加密 → 写入,但注意每块需独立 nonce,且需在文件头声明块大小和总块数
- 更稳妥做法是改用
golang.org/x/crypto/nacl/secretbox(基于 XSalsa20-Poly1305),它接受任意长度的 nonce,且 API 更贴近流式场景
解密失败时别忽略 error 或 panic
GCM 解密失败通常返回 cipher: message authentication failed,原因可能是:密钥错、nonce 错、密文被篡改、或文件截断。如果只是吞掉 error 继续执行,后续 json.Unmarshal 或 xml.Decode 会报更晦涩的格式错误。
实操建议:
- 解密函数必须返回
([]byte, error),调用方显式检查err != nil - 对关键配置文件,可在加密后立即解密验证(
encrypt → decrypt → compare),作为 Save() 的内置 sanity check - 日志中记录失败时的文件名和 nonce 前 8 字节(不记完整 nonce,防泄露),方便排查是否密钥版本不一致
AES-GCM 也只是个精致的陷阱。


















