crypto/aes.newcipher的密钥长度必须严格为16、24或32字节,否则直接panic;它仅返回底层块加密器,不支持任意长度明文加解密,须配合cipher.aead(如gcm)或手动填充+模式(如cbc)使用。

crypto/aes.NewCipher 的输入密钥长度必须是 16、24 或 32 字节
crypto/aes.NewCipher 不接受任意长度的密钥,它严格要求密钥字节长度为 AES 支持的标准值:128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。传入其他长度(比如 17 字节或 31 字节)会直接 panic,错误信息为 "cipher: invalid key size"。
常见错误是直接用字符串字面量当密钥,比如 []byte("my-secret-key") —— 这只有 13 字节,不够。更安全的做法是用 crypto/rand 生成或用 sha256.Sum256 等哈希函数派生固定长度密钥:
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
log.Fatal(err)
}
block, err := aes.NewCipher(key) // ✅ 32 字节 OK
NewCipher 只返回底层分组密码,不提供加密模式或填充
aes.NewCipher 返回的是一个 cipher.Block 接口,仅实现 AES 的单次块加解密(16 字节输入 → 16 字节输出),**不是可以直接加密任意长度数据的“加密器”**。你必须自己组合模式(如 CBC、GCM)和填充(如 PKCS7)。
典型误用是试图直接用 block.Encrypt(dst, src) 加密超过 16 字节的数据 —— 它只会处理前 16 字节,其余被忽略,且不报错。
- 要加密可变长明文,得包装成
cipher.Stream(如 CFB、OFB)或cipher.AEAD(如 GCM) - 最常用的是
cipher.NewGCM(block),它自动处理 nonce、认证、填充(GCM 本身无填充) - 若坚持用 CBC,需手动实现 PKCS7 填充,并确保 IV 是随机且不可复用的
IV 和 nonce 必须每次加密都唯一,且不能硬编码
即使你用 aes.NewCipher 搭配 cipher.NewCBCDecrypter 或 cipher.NewGCM,如果 IV 或 nonce 复用,会导致严重安全问题(如 GCM 下密文可被篡改或密钥可被恢复)。
IV/nonce 不是密钥,可以随密文一起传输(通常前置),但绝不能固定:
// ❌ 危险:硬编码 IV
iv := []byte("1234567890123456") // 永远不变
// ✅ 正确:每次生成随机 IV(CBC)或 nonce(GCM)
iv := make([]byte, block.BlockSize())
if _, err := rand.Read(iv); err != nil {
log.Fatal(err)
}
GCM 要求 nonce 长度通常为 12 字节(gcm.NonceSize()),而 CBC 要求 IV 长度等于块大小(16 字节)。长度错也会导致 NewXXX 函数 panic。
注意 BlockSize() 和加密方向的内存安全
block.BlockSize() 固定返回 16,但别假设 dst 和 src 切片长度足够 —— block.Encrypt(dst, src) 要求 len(dst) >= 16 && len(src) >= 16,且 dst 和 src 不能重叠(即不能是同一底层数组的不同切片),否则行为未定义。
常见坑:
- 用
make([]byte, 16)当 dst,但 src 是data[:16],而 data 底层容量不足,导致越界写 - 把
block.Encrypt(data, data)当原地加密 —— Go 运行时不会阻止,但结果不可预测 - 对非 16 字节倍数的明文直接调用 Encrypt —— 不报错,但只处理前 16 字节,其余丢弃
真正要用原地操作,得确保 dst 和 src 地址不重叠,或者用 copy() 显式分离。











