aes密钥长度必须严格为16、24或32字节;ecb不安全,应使用cbc并确保iv随机且唯一;填充须用pkcs#7而非zero padding;解密失败需分层排查iv、密钥、填充及长度。

为什么 aes.NewCipher 总 panic: "invalid key size"?
Go 的 AES 实现对密钥长度极其严格,只接受 16(AES-128)、24(AES-192)或 32(AES-256)字节的密钥,少一个字节或多一个字节都会直接 panic。常见错误是拿字符串字面量当密钥,比如 "mysecret"(8 字节)或 "this-is-a-32-char-long-key-123456"(33 字节)。
- 用
sha256.Sum256或md5.Sum128固定输出长度再截取:例如sha256.Sum256([]byte("passphrase")).[0:32] - 别用
strings.Repeat补位 —— 这不增加熵,还可能引入空字节导致解密失败 - 如果从配置读密钥,务必检查
len([]byte(key)),不是len(key)(后者对含中文或 emoji 的字符串会出错)
ECB 模式能用吗?为什么 cipher.NewCBCEncrypter 要自己传 IV?
不能用 ECB。它对相同明文块总生成相同密文块,像 "admin:true" 和 "user:false" 可能暴露结构,实际中基本等同于“没加密”。
CBC 是更安全的起点,但 IV 必须随机且每次加密不同:
- IV 长度必须等于 AES 块大小(
16字节),和密钥长度无关 - IV 不需要保密,但绝不能复用 —— 同一密钥下重复 IV 会让前两个块的异或关系可推测
- 推荐把 IV 放在密文开头:先写
iv,再写加密后数据,解密时前16字节读作 IV
// 加密时 iv := make([]byte, aes.BlockSize) rand.Read(iv) // 注意 error 检查 block, _ := aes.NewCipher(key) mode := cipher.NewCBCEncrypter(block, iv) ciphertext := make([]byte, len(plaintext)) mode.CryptBlocks(ciphertext, plaintextPadded) result := append(iv, ciphertext...)
填充怎么处理?pkcs7 和 zero padding 有什么区别?
Go 标准库不内置填充,必须手动补足到 16 字节整数倍。PKCS#7 是通用做法,而 zero padding 在明文末尾补 \x00,但无法区分真实 \x00 和填充位,解密时必然出错。
立即学习“go语言免费学习笔记(深入)”;
- PKCS#7 填充规则:需补
n字节,则填n个字节值为n;若原文本刚好是 16 的倍数,则额外补 16 个\x10 - 解密后必须验证最后一个字节值 ≤
16,且末尾n字节全等于n,否则报错(防篡改) - 别用
bytes.Repeat([]byte{0}, n)—— 这是 zero padding,不是 PKCS#7
解密失败时只返回乱码,怎么快速定位是哪步错了?
Go 的 AES 解密不会主动报错,错 IV、错密钥、错填充都会产出看似“成功”的乱码。排查要分层:
- 先确认密文长度 ≥
16(IV) +16(至少一个密文块) - 检查解密用的
key和加密是否完全一致(注意字符串编码、空格、换行) - 把 IV 单独提取出来,用
hex.EncodeToString(iv)打印比对 - 临时改用已知正确的测试密钥/IV(如全 0 的
[32]byte)跑通流程,再换回业务密钥
AES 本身简单,真正复杂的是密钥管理、IV 生成、填充验证、错误传播这四点。漏掉任意一个,都会让加密变成“看起来工作、其实裸奔”的状态。










