AES-GCM是文件加密默认选择,因其天然支持AEAD认证,一次调用完成加密与完整性校验;需严格使用32字节密钥、12字节唯一nonce,且密文结构须固化salt、nonce与密文三段头部。

直接用 crypto/aes + cipher.NewGCM 是最稳妥的选择,其他模式(如 CFB、CBC)要么不带完整性校验,要么需手动处理填充和 IV 管理,容易出错。
为什么别用 CFB 或 CBC 模式做文件加密
CFB 和 CBC 是传统分组密码模式,但它们只提供机密性,不防篡改。比如你加密一个配置文件,攻击者修改密文后解密可能得到看似合法但被篡改的数据(如把 "port": 3306 改成 "port": 9999),程序照常运行却埋下隐患。
- CFB 没有认证标签,无法检测密文是否被篡改
- CBC 需要 PKCS#7 填充,且 IV 必须随机且不可复用;若重复使用同一 IV 加密不同文件,会泄露明文前缀信息
- 标准库中
cipher.NewCBCEncrypter不校验 IV 长度,传入错误长度(如 16 字节 AES 却给 8 字节 IV)会导致 panic 或静默错误
cipher.NewGCM 的正确初始化方式
GCM 模式同时提供加密和认证,是 Go 官方推荐的现代做法。关键点不是“能不能用”,而是“怎么避免踩坑”:
-
nonce长度必须严格等于gcm.NonceSize()(通常为 12 字节),不能硬写make([]byte, 12)就完事——得调用它获取真实值 -
nonce必须唯一,但不必保密;建议每次加密都用io.ReadFull(rand.Reader, nonce)生成 - 密钥长度只能是 16、24 或 32 字节(对应 AES-128/192/256);传入 31 字节密钥会触发
crypto/aes: invalid key size错误 - 加密后输出 =
nonce+ciphertext,解密时先切出 nonce,再用相同 key 初始化 GCM 实例
读写大文件时别一次性加载到内存
用 ioutil.ReadFile / ioutil.WriteFile 处理几 MB 以下的小文件没问题,但加密日志或数据库 dump 文件(几百 MB)会 OOM。
立即学习“go语言免费学习笔记(深入)”;
- 改用
os.Open+io.Copy流式加解密:对输入流逐块加密,写入输出流 - GCM 不支持流式加密(
cipher.Stream接口不兼容),所以得自己分块:每块 ≤ 16MB,加密后追加写入,同时维护全局 nonce(首次生成,后续块用计数器派生) - 更简单稳妥的做法:用
golang.org/x/crypto/chacha20poly1305替代 AES-GCM——它原生支持流式加密,且 nonce 只需 24 字节,抗重放能力更强
密钥管理不是加密逻辑的一部分
代码里写死 key := []byte("my-super-secret-key-1234567890abcdef") 只适合 demo,生产环境必须剥离:
- 从环境变量读取时,确保用
os.Getenv("ENCRYPTION_KEY")而非os.LookupEnv——后者不报错但返回空字符串,导致密钥为 0 字节 - 若用密码派生密钥(如 PBKDF2),务必固定迭代次数(如
100000)和 salt 长度(如 16 字节),salt 必须随密文一起保存(通常 prepended) - 绝对不要用
md5或sha1做密钥派生——它们设计目标是快速,反而利于暴力破解;改用crypto/sha256+pbkdf2.Key
真正麻烦的从来不是“怎么调 API”,而是 nonce 是否唯一、密钥是否真随机、大文件是否 OOM、以及密钥到底存在哪——这些细节不显眼,但任何一个出错都会让整个加密形同虚设。


















