aes-gcm是go中推荐的文件加密方案,因其内置机密性与完整性校验;需严格使用12字节随机nonce并前置存储,密钥长度须为16/24/32字节,大文件应分块流式处理,避免oom。

加密前必须先确认文件是否支持随机读写
很多实际场景下(比如日志轮转、大文件流式处理),直接对整个文件做 AES 加密会失败——因为 os.OpenFile 默认以只读打开,而某些加解密流程需要反复 seek。更隐蔽的问题是:如果文件是管道(/dev/stdin)或网络 socket 返回的 *os.File,它根本不支持 Seek,调用 file.Seek(0, 0) 会返回 io.Seeker is not supported 错误。
实操建议:
- 用
file.Stat()检查os.FileMode是否含os.ModeDevice或os.ModeNamedPipe,这类文件必须走流式加解密,不能依赖Seek - 加密前统一用
os.OpenFile(path, os.O_RDWR, 0)打开,确保可读可写;若仅需加密不覆盖原文件,改用os.Open+ 新文件写入 - 对大文件(>100MB),避免一次性
io.ReadAll(file),改用bufio.NewReader(file)分块读取,每块加密后写入新文件
使用 crypto/aes + crypto/cipher 时 IV 必须随密文一起存储
Go 标准库的 cipher.BlockMode(如 cipher.NewCBCEncrypter)不自动管理初始化向量(IV)。如果每次加密都硬编码 make([]byte, 16) 作为 IV,会导致相同明文生成相同密文,严重削弱安全性;而如果完全不保存 IV,解密时无法还原原始数据。
实操建议:
- 加密时生成随机 IV:
iv := make([]byte, block.BlockSize())+rand.Read(iv),然后将 IV 放在密文最前面(如前 16 字节),再写入加密数据 - 解密时先读取前 16 字节作为 IV,再用剩余字节解密;务必校验读取长度,防止
EOF导致iv不完整 - 不要用文件名、时间戳等可预测值派生 IV;也不要把 IV 存在单独文件里——和密文分离会增加运维复杂度且易丢失
文件解密失败常见于 padding 验证与 EOF 边界判断
Go 的 cipher.BlockMode 不自带 PKCS#7 填充,需手动实现。如果加密时用了填充但解密时没去除,或去除逻辑错误(比如把末尾 0x05 当作填充却实际是有效数据),就会出现「解密后内容乱码」或「panic: crypto/cipher: invalid buffer size」。
实操建议:
- 加密后数据长度一定是 block size(如 AES 是 16)的整数倍;解密前先检查
len(ciphertext) % block.BlockSize() != 0,不满足直接报错,避免后续 panic - PKCS#7 填充去除必须严格:取最后一个字节
padLen := ciphertext[len(ciphertext)-1],验证padLen 且最后 <code>padLen字节全等于padLen,否则视为损坏 - 流式解密(如边读边解)时,最后一块可能不满 16 字节,此时需缓存到内存等凑够一块再处理,不能强行解密
生产环境必须添加完整性校验,仅加密不防篡改
AES-CBC 等模式能保密,但无法检测密文是否被恶意修改。攻击者翻转密文某字节,可能导致解密后明文某字节出错——这种错误可能静默发生(比如 JSON 解析跳过非法字段),导致业务逻辑异常。
实操建议:
- 在加密后、写入文件前,计算密文(含 IV)的 HMAC-SHA256,追加在密文末尾(或另存为
.sig文件);解密前先校验签名,失败则拒绝处理 - 不要复用加密密钥做 HMAC,应从主密钥派生两个子密钥:
hmacKey := hkdf.New(sha256.New, masterKey, nil, []byte("hmac")) - 若用
golang.org/x/crypto/nacl/secretbox,它已内置 AEAD,比手拼 AES+HMAC 更安全,但注意其 nonce 长度固定为 24 字节,不可复用
真正难的不是写出加解密函数,而是确保每种边界情况(空文件、权限不足、磁盘满、并发读写)都有明确响应路径。尤其当加密逻辑嵌入 CLI 工具或 HTTP handler 时,错误信息必须包含具体失败点(比如「failed to write IV: permission denied」),而不是笼统抛 crypto.ErrMessageTooLong。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











