aes.newcipher只接受16、24或32字节密钥,否则直接panic;cbc模式需手动pkcs#7填充且明文长度必须为16倍数,否则cryptblocks静默截断末尾数据导致解密丢失;iv必须每次随机生成并前置传输,复用iv会破坏语义安全。

aes.NewCipher 只接受 16、24 或 32 字节密钥,其他长度直接 panic;CBC 模式下不填充分组数据会静默截断,解密后内容丢失——这不是 bug,是设计使然。
为什么 aes.NewCipher 总 panic “invalid key size”?
Go 的 AES 实现对密钥长度零容忍:必须严格为 16(AES-128)、24(AES-192)或 32(AES-256)字节。任何偏差都会触发 panic。
- 别用字符串字面量直接转
[]byte:比如"mykey"是 5 字节,"this-is-32-char-key-xxxxxxxxxx"看似长但可能是 33 字节(含不可见字符) - 检查长度要用
len([]byte(key)),不是len(key)—— 后者对含中文、emoji 的字符串返回 rune 数,不是字节数 - 安全做法是派生密钥:用
golang.org/x/crypto/scrypt.Key或pbkdf2.Key从密码生成固定长度密钥,参数设N=32768, r=8, p=1(scrypt)或iter=100000, keylen=32(PBKDF2) - 硬编码密钥只用于测试;生产环境密钥应通过环境变量或 Vault 注入,且加载后立即校验长度
cipher.NewCBCEncrypter 加密时为何丢数据?
cipher.NewCBCEncrypter 不做填充,也不校验输入是否对齐——它只按块处理,CryptBlocks 会静默跳过末尾不足一块的数据。
- 明文长度必须是
aes.BlockSize(即 16)的整数倍,否则最后一块被丢弃,解密后变短或乱码 - 必须手动 PKCS#7 填充:计算
n = 16 - len(plaintext)%16,追加n个值为n的字节;空明文也要填 16 个0x10 - 别用 zero-padding:解密时无法区分真实
0x00和填充字节,PKCS#7 可无歧义移除 - 填充后长度 = 原长度 + n,目标密文缓冲区要预留
aes.BlockSize(IV)+ 填充后长度
IV 复用或固定会导致什么后果?
IV 不保密,但绝不能复用。同一密钥下重复 IV,会让相同明文前缀生成相同密文前缀,攻击者可识别模式甚至恢复部分明文。
- 每次加密都调用
crypto/rand.Read(iv)生成新 IV,长度固定为aes.BlockSize(16 字节) - IV 必须和密文一起传输:常规做法是前置拼接——
append(iv, ciphertext...),解密时取前 16 字节作 IV - 别用时间戳、计数器或
md5生成 IV:它们不具备密码学随机性,易预测 - 文件加密时,IV 和 salt(如用 scrypt)都得存进文件头,建议格式:32 字节 salt + 16 字节 IV + 密文
大文件流式加密为何不能直接 io.Copy?
io.Copy 按底层 reader 的 chunk 大小读取,不保证每次读到的字节数是 16 的倍数,导致分块边界错位,填充逻辑失效。
- 分块读取缓冲区大小设为 16 的倍数(如
65536),每次读满或 EOF 时单独处理该块 - 非最后一块无需填充;最后一块必须显式 PKCS#7 填充后再加密
- 写入时先写 IV(仅首次),再写每块密文;解密端严格按顺序读 IV → 逐块解密 → 最后一块去填充
- 内存敏感场景下,避免一次性
os.ReadFile整个文件;用bufio.NewReaderSize控制读取粒度
真正难的不是写对 CryptBlocks,而是确保密钥长度、填充边界、IV 随机性和流式分块四者严丝合缝——漏掉任意一环,加密就形同虚设。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











