AESGCM要求密钥长度严格为16/24/32字节,字符串字面量因UTF-8编码导致长度不符;nonce须唯一、长12字节且与密文一同存储;gcm.Seal添加16字节认证标签,gcm.Open失败静默返回nil而非error。

为什么直接用 crypto/cipher 的 AESGCM 会 panic: "invalid key size"?
因为 AESGCM 不接受任意长度密钥,它只认 16(AES-128)、24(AES-192)或 32(AES-256)字节的密钥。传入 32 字节却仍报错?大概率是你用了字符串字面量当密钥——Go 中字符串底层是 UTF-8 编码,而 AES 要的是原始字节。别用 "my-secret-key" 这种,改用 []byte 显式构造。
常见错误现象:panic: crypto/aes: invalid key size 13(对应 "my-secret-key" 长度)
- 密钥必须是
[]byte,且长度严格为 16/24/32 - 不要用
md5.Sum或sha256.Sum直接转切片——它们的[...]byte类型不能直接传给gcm.Seal - 若从配置读密钥(如环境变量),务必用
hex.DecodeString或base64.StdEncoding.DecodeString解码后再校验长度
如何正确生成 nonce 并避免重放和重复?
AES-GCM 的 nonce 不是 IV,它要求「唯一性」而非「随机性」;重复使用同一密钥+同一 nonce 会导致密文完全可破解。Go 标准库不帮你管理 nonce,你得自己负责。
使用场景:服务端加密用户数据、API 请求签名、本地配置文件加密
立即学习“go语言免费学习笔记(深入)”;
- 推荐用
crypto/rand.Read(nonce)生成随机 nonce,但必须确保每次加密都用新 buffer(别复用 slice) - 如果需要可预测/可还原 nonce(如基于时间戳或计数器),必须保证全局单调递增 + 每个密钥独立维护计数器
- nonce 长度固定为
12字节(标准 GCM 推荐值),传给gcm.Seal前不能截断或填充 - 务必把 nonce 和密文一起存储/传输——通常拼在密文前,解密时先切出来
gcm.Seal 返回的密文为什么比原文长?多出来的 bytes 是什么?
多出的 16 字节是 GCM 认证标签(authentication tag),用于验证密文完整性和真实性。它不是“加密膨胀”,而是安全必需——没有它,GCM 就退化成不带认证的流加密,无法防篡改。
性能影响:tag 固定 16 字节,与原文长度无关;计算开销略高于纯 AES-CBC,但远低于 RSA 签名
-
gcm.Seal(dst, nonce, plaintext, additionalData)中,dst若为nil,会自动分配len(plaintext)+16字节 - 若传入非 nil
dst,必须确保 cap >= len(plaintext)+16,否则 panic -
additionalData(AAD)可为空(nil),但一旦用,加解密时必须完全一致,否则Open返回nil(不是 error)
解密失败时 gcm.Open 为什么静默返回 nil 而不报错?
这是 GCM 的设计特性:认证失败必须「不泄露任何信息」,包括失败原因。所以 gcm.Open 只有两种结果——成功返回明文,失败返回 nil。你不能靠 error 判断是密钥错、nonce 错还是密文被改。
容易踩的坑:直接用 if err != nil 判断失败,却忽略了 out == nil 才是真实失败信号
- 必须检查返回值是否为
nil,而不是只看 error - 典型写法:
plaintext, err := gcm.Open(nil, nonce, ciphertext, aad); if plaintext == nil { return errors.New("decryption failed") } - 如果业务允许部分降级(如日志中记录失败但不中断流程),千万别把
nil当空字符串处理,否则可能引入空指针或逻辑漏洞
最麻烦的点其实是密钥和 nonce 的生命周期管理——它们要安全生成、安全存储、安全传递,而 Go 的 crypto/cipher 只管最后一步。这一步做对了,前面全白搭。










