crypto/aes不直接加密文件,仅处理内存字节块;文件加密需自行串联读、加、写三步,关键包括:每次随机生成16字节iv并前置存储、优先用aes-gcm避免手动填充与hmac、大文件须流式处理、密钥必须经scrypt等kdf派生、解密时严格校验auth tag。

crypto/aes 本身不加密文件,它只处理内存里的字节块。所谓“文件加密”,是你自己把读、加、写三步串起来——漏掉任何一环,比如 IV 复用、没校验认证标签、大文件全读进内存,加密就等于没做。
IV 必须随机生成且随密文一起保存
常见错误是用密钥前 16 字节当 IV:iv := key[:aes.BlockSize],这会让相同明文每次加密出完全一样的密文,攻击者一眼就能识别重复模式。
正确做法是每次加密都调用 crypto/rand.Read 或 io.ReadFull(rand.Reader, iv) 生成新 IV,并把它明文写在密文最前面(标准做法):
- IV 长度固定为
aes.BlockSize(16 字节),不能少也不能多 - 解密时先从文件头读出这 16 字节,再用它初始化解密器
- IV 不需要保密,但绝不能复用——哪怕同一密钥下加密两个不同文件
AES-GCM 模式比 CBC 更安全也更简单
CBC 模式必须手动处理 PKCS#7 填充、还要额外加 HMAC 防篡改;而 cipher.NewGCM 一步到位:加密输出自带认证标签(tag),解密失败直接返回 cipher.ErrAuthFailed,不会吐出乱码。
关键点:
-
gcm.NonceSize()通常是 12 字节(不是 16),别硬写死 - 加密时用
gcm.Seal(nonce, nonce, plaintext, nil),结果是nonce + ciphertext + tag - 解密时必须把整个密文(含末尾 tag)传给
gcm.Open,不能只传密文部分
大文件不能一次性读进内存
用 ioutil.ReadFile 加密一个 2GB 的日志文件?Go 进程会直接 OOM。流式处理是唯一可行路径,但 GCM 不支持分块加密——它的 tag 只能在全部数据处理完后生成。
所以实际方案是:
- 先写 12 字节 nonce 到目标文件
- 边读源文件(例如 64KB 缓冲区)、边加密、边写入密文(不含 tag)
- 全部写完后,追加 16 字节的认证 tag
- 解密端按顺序:读 nonce → 读全部密文+tag → 调用
gcm.Open一次性验证解密
密钥不能来自原始密码字符串
用户输的 "myPass123" 直接当 AES-256 密钥?熵太低,GPU 暴力几秒就破。必须用密钥派生函数(KDF)扩展:
- 用
golang.org/x/crypto/scrypt,参数至少设N=32768, r=8, p=1 - salt 必须每次随机生成(32 字节),和 IV 一样存进输出文件头部
- 别用
sha256.Sum256替代 KDF——它太快,毫无抗暴力能力
cipher.NewGCM(block).Open 不 panic,也不报错,只是返回 nil, cipher.ErrAuthFailed。如果你没检查这个 error,就会误以为解密成功,把一堆乱码当配置加载进去。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











