直接用 os.ReadFile + aes.NewCipher 加密文件是错的——会爆内存、丢数据、解密乱码且不防篡改;必须手动 PKCS#7 填充、随机生成并保存 salt/IV、用 scrypt/pbkdf2 派生密钥、清零密钥内存、显式校验解密错误。

直接用 os.ReadFile + aes.NewCipher 加密文件是错的——它会爆内存、丢数据、解密乱码,而且不防篡改。
为什么不能直接用 io.Copy 套 AES-CBC
因为 cipher.NewCBCEncrypter 要求输入长度严格对齐 16 字节,而 io.Copy 不保证块边界。常见现象是:大文件加密后解密,末尾几字节变成乱码或截断,且无错误提示。
- 必须手动做 PKCS#7 填充,
CryptBlocks不自动补位,未填充就调用会静默截断 -
io.ReadFull(rand.Reader, iv)必须成功,否则 IV 是零值,复用导致密文可预测 - 小文件([]byte;大文件必须分块(如 64KB),每块单独填充+加密+写入,块大小需是 16 的倍数
aes.NewGCM 比 cipher.NewCBCEncrypter 更值得优先选
GCM 自带认证标签(tag),能同时防窃听和篡改,不用额外加 HMAC 或 PKCS#7 填充——但代价是不能真正“流式”加密大文件,因为 tag 只能在全部加密完成后生成。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- nonce 推荐 12 字节(不是 16),用
crypto/rand.Read生成,和密文一起存入文件头部 - 加密时先写 nonce,再写密文,最后追加 16 字节 tag;解密时要严格按此顺序读取并切片
- 如果硬要处理超大文件(如 >500MB),得用临时文件或 mmap 分段加密,避免内存溢出
密钥和 IV 绝对不能硬编码或复用
用户输的密码(如 "myp@ss")不能直接当 AES 密钥用——太短、熵低、易被 GPU 暴力破解。
- 派生密钥必须用
golang.org/x/crypto/scrypt或pbkdf2,参数至少设N=32768, r=8, p=1(scrypt)或iter=131072(pbkdf2) - salt 和 IV 都要随机生成,每次加密各调一次
crypto/rand.Read,且都存进输出文件前部(如 salt 32 字节 + IV 16 字节) - 密钥切片用完立刻
bytes.Fill(key)清零,防止被内存 dump 抓取
最易被忽略的一点:解密失败时 aesgcm.Open 返回 cipher.ErrAuthFailed,而 cipher.NewCBCDecrypter 完全不报错——你拿到的只是乱码,必须显式校验返回 error,不能只看 len(dst) > 0 就认为成功。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










