加密前必须确认的三个前提是:iv(nonce)必须随机且不重用、密钥须经pbkdf2等安全派生、明文需按pkcs7填充;aes-gcm是推荐方案,但需严格遵循nonce唯一性、salt保存、分块处理及tag校验。

加密前必须确认的三个前提
Go 标准库不提供开箱即用的“文件加密器”封装,crypto/aes、crypto/cipher 这些包只提供底层原语,直接套用容易出错。你得自己处理 IV 生成、填充(PKCS7)、密钥派生(不能直接用密码字符串)、AEAD 模式选择等环节。跳过任一环节,加密后的文件要么解不开,要么被篡改也无法察觉。
-
crypto/aes只实现 AES 算法本身,不负责密钥扩展或模式封装 - 明文长度不是块大小(16 字节)整数倍时,
crypto/cipher.NewCBCEncrypter会 panic,必须手动填充 - 单纯用
sha256.Sum256对密码哈希一次当密钥,等同于裸密钥——攻击者可暴力穷举,应使用golang.org/x/crypto/pbkdf2
用 AES-GCM 实现安全的文件加解密
AES-GCM 是目前 Go 中最推荐的对称加密方式:它同时保证机密性与完整性,且无需手动处理填充和 MAC 计算。但注意:crypto/cipher.NewGCM 要求 nonce 长度固定为 12 字节,且绝不能重用同一密钥+nonce 组合。
- 每次加密前调用
rand.Read(nonce[:])生成随机 nonce,并把它和密文一起写入输出文件头部(如前 12 字节) - 密钥必须由密码派生而来:用
pbkdf2.Key,salt 随机生成(建议 16 字节),迭代次数 ≥ 100000 - 解密时先读取前 12 字节作为 nonce,再用相同 salt 和迭代参数派生密钥,最后传给
block.Open - 不要用
io.Copy直接加密大文件——内存会爆,要用bufio.NewReader分块读取,每块单独调用seal,但注意 GCM 的 nonce 必须全局唯一,不能每块重置
常见错误:解密失败但无明确报错
Go 的 block.Open 在认证失败时返回 nil, cipher.ErrMessageTooShort 或 nil, crypto/ecdsa: verification failed 类似模糊错误,实际原因是 GCM tag 校验失败。这通常意味着:
- 加密和解密用的密钥不一致(比如 salt 被硬编码但没保存,或 PBKDF2 迭代次数不同)
- nonce 被截断或错位(例如读取文件时跳过了前 12 字节,却仍用原始密文调用
Open) - 密文在传输/存储过程中被修改(哪怕只改一个字节),GCM 会直接拒绝,不会返回解密结果
- 用
os.Create写密文后没调用Close(),导致部分数据未刷盘,解密时读到不完整数据
简化方案:用第三方库 github.com/secure-io/sio
如果你不需要深度控制加密细节,sio 库封装了安全默认值:自动处理 salt、nonce、HKDF 密钥派生、AES-GCM,并内置防侧信道比较。它比手写更难出错,且 API 极简:
enc, _ := sio.Encrypt(os.Stdout, []byte("password"), nil)
io.Copy(enc, os.Stdin) // stdin 是原文,stdout 是密文
// 解密同理,用 sio.Decrypt
但它要求密码至少 8 字节,且不支持自定义迭代次数或 salt 长度——这些是它换来的安全性保障。如果项目允许引入外部依赖,这比反复调试标准库更省时间。
真正麻烦的从来不是“怎么调函数”,而是“为什么这个函数在这个上下文里必须这么调”。IV 重复、salt 丢弃、tag 忽略校验……这些细节不会报编译错误,但会让加密形同虚设。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











