必须用cipher.stream而非cipher.block,因后者无状态、无法保证流式加解密连续性;ctr/cfb/ofb等模式提供cipher.stream实现,而ecb/cbc不适用流式场景。

加密解密必须用流式 cipher.Stream,不能用 cipher.Block
很多人一上来就用 crypto/aes.NewCipher 拿到 cipher.Block,然后想手动分块异或——这会直接导致解密失败。因为文件流传输要求「逐块加/解密且状态连续」,cipher.Block 是无状态的纯块变换,而 cipher.Stream(比如 crypto/cipher.NewCTR 或 NewXORKeyStream)才维护内部计数器或反馈状态,保证加解密可逆且无缝衔接。
常见错误现象:decrypt: crypto/cipher: invalid buffer overlap 或解密后文件开头正常、后面全是乱码——基本就是误用了 Block 接口。
- CTR、OFB、CFB 模式都提供
cipher.Stream实现;ECB/CBC 不适合流式(CBC 需要 padding 且无法增量解密) - 密钥和 IV 必须固定长度:AES-256 要 32 字节密钥 + 16 字节 IV;IV 绝对不可复用,每次加密需新生成并随数据一起传(如前置 16 字节)
- 别自己拼接 IV 和密文:先写 IV,再写加密流,解密端先读 16 字节作为 IV,再构建 stream 解密后续内容
用 io.Pipe 实现透明管道,避免内存堆积
如果把整个文件读进内存再加密,大文件(>100MB)直接 OOM。正确做法是用 io.Pipe 构造一个内存无缓冲的「虚拟管道」,让加密 goroutine 和传输 goroutine 并发跑在两端。
示例关键链路:
pr, pw := io.Pipe()
go func() {
defer pw.Close()
aesBlock, _ := aes.NewCipher(key)
stream := cipher.NewCTR(aesBlock, iv)
// 注意:pw 是 io.Writer,stream.XORKeyStream 是 []byte 写入目标
// 所以要用 io.Copy + stream 封装成 writer
cipherWriter := &cipherWriter{W: pw, Stream: stream, Buf: make([]byte, 64*1024)}
io.Copy(cipherWriter, srcFile)
}()
// 此时 pr 可直接传给 HTTP response.Body 或 net.Conn.Write
这里 cipherWriter 是个自定义 io.Writer,每次 Write 时调用 stream.XORKeyStream(dst, src),不缓存整块数据。
- 别用
bytes.Buffer中转:它会吃光内存 -
io.Pipe的阻塞特性天然限流,比 channel 手动控制更轻量 - 务必检查
pw.Close()是否在 goroutine 末尾调用,否则pr.Read会永远阻塞
解密端必须严格按「IV + 密文」顺序读取,且复用同一 stream 实例
解密不是“拿密文丢给函数就行”,而是要精确拆分:前 N 字节是 IV(比如 AES-CTR 固定 16),剩余才是密文流。一旦 IV 读错位置或长度不对,后续全部解密失败,且毫无提示。
典型翻车点:
- HTTP header 里带了
Content-Length,但没把 IV 长度算进去 → 解密端读不满 IV 就 EOF,panic - 用
bufio.Reader包裹连接后,提前调用Peek或ReadByte导致 IV 被吞掉一部分 - 每个连接复用同一个
cipher.Stream实例(CTR 计数器是 stateful 的),但多个并发请求混用 → 解密错乱
安全做法:用 io.LimitReader(pr, ivSize) 先读 IV,确认长度匹配;再用剩余的 pr 构建新 stream 解密主体。不要试图“重置” stream —— CTR 没有 Reset 方法,必须新建。
AEAD 模式(如 GCM)不适合纯流式透明传输
虽然 crypto/cipher.AEAD 更安全(带认证),但它要求「完整明文输入 + nonce + 额外数据」才能生成密文+tag,且解密也必须拿到完整密文+tag 才能验证。这意味着你无法边收边解、边解边传——必须攒够整个文件(或至少一个 chunk + tag)才能开始解密。
所以真实场景中:
- 传输大文件、低延迟要求(如视频流、实时日志转发)→ 选 CTR/XOR(快、真流式、无认证)
- 小文件、强完整性要求(如配置文件、密钥文件)→ 用 GCM,但得接受「全量加密/解密」模式
- 折中方案:CTR 加密 + 单独计算并追加 HMAC-SHA256(放在文件末尾),解密端先流式解密,最后校验 HMAC —— 但要注意 HMAC 本身也要流式计算(
hmac.New+io.MultiWriter)
CTR 本身不防篡改,如果传输链路不可信,跳过「透明」追求「安全」,就得放弃流式,或者引入额外校验层。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











