go crypto模块不提供开箱即用加解密函数,仅提供底层原语;aes.newcipher要求密钥长度严格为16/24/32字节;cbc需随机iv、pkcs#7填充、iv明文前置;gcm对nonce重用零容忍;rsa须配合aes混合加密;所有密钥材料必须源自crypto/rand。

Go 标准库的 crypto 模块本身不提供“开箱即用”的加解密函数,它只提供底层原语(如 aes.NewCipher、cipher.NewGCM),所有安全加解密都必须由你显式组合——不是模块不好用,而是 Go 故意不替你做关键安全决策。
为什么 aes.NewCipher 一调就 panic?
因为密钥长度必须严格为 16 / 24 / 32 字节,少一个或多一个都会触发 crypto/aes: invalid key size。这不是 bug,是设计约束。
- 常见错误:直接用
[]byte("mykey123")(8 字节)或[]byte("password-123456789012345678901234")(33 字节)传入 - 中文/emoji 密钥容易误判长度:
len("?")是 4(UTF-8 字节数),不是 1 - 从配置或环境变量读取后,务必
strings.TrimSpace去首尾空白,再校验len(keyBytes) - 生产推荐派生方式:
sha256.Sum256([]byte(rawKey)).Sum(nil)[:32](适配 AES-256)
CBC 模式下解密乱码,八成是 IV 或填充没对齐
CBC 不是“把明文塞进去就完事”,它要求三件事同时成立:随机 IV、PKCS#7 填充、IV 明文前置。缺一不可。
-
iv必须每次加密全新生成:iv := make([]byte, aes.BlockSize)+io.ReadFull(rand.Reader, iv) - 绝不能复用:
iv := key[:16]或硬编码[]byte("fixed-iv-12345678")会让相同明文输出完全一致 - 填充必须用 PKCS#7:原文长度刚好是 16 倍数时,仍要补 16 个
\x10;解密后必须用标准逻辑截掉,不能靠bytes.TrimRight - 密文结构应为
append(iv, encrypted...),解密时先切前 16 字节作 IV,再解剩余部分
GCM 模式更省心,但 nonce 重用等于密钥泄露
cipher.NewGCM 返回 AEAD 接口,自动处理认证加密、无需手动填充、也无需额外 HMAC,但对 nonce 极度敏感。
-
nonce长度由gcm.NonceSize()决定(通常为 12 字节),必须每次加密唯一且不可预测 - 同一密钥下重复使用 nonce,攻击者可直接恢复明文甚至推导出密钥
- 安全做法:用
rand.Reader生成,或用计数器(需确保跨进程/重启不重复),禁止从时间戳或简单递增变量构造 - 密文结构为
gcm.Seal(nonce, nonce, plaintext, nil),解密时直接传入整个字节切片,gcm.Open会自动验证 tag
RSA 加密不能直接套大文本,得配合 AES 做混合加密
RSA 只能加密很短数据(比如 AES 密钥),直接加密长文本会失败或被 padding oracle 攻击。工程上必须用“RSA 封装 AES 密钥 + AES 加密正文”模式。
- 公钥加密只用于保护对称密钥:
rsa.EncryptOAEP(sha256.New(), rand.Reader, pub, aesKey, nil) - 务必选
EncryptOAEP而非EncryptPKCS1v15:后者已被证明易受 padding oracle 攻击 - 私钥解密后,必须校验返回的
aesKey长度是否符合预期(如 32 字节),防止伪造密钥导致后续 AES 解密崩溃 - 密钥对生成必须用
rsa.GenerateKey(rand.Reader, 2048),不能手写质数或复用测试密钥
最常被忽略的一点:所有密钥、IV、nonce 都不能硬编码,也不能从非密码学安全源(如 math/rand、时间戳、PID)生成;哪怕只是本地调试,也要用 crypto/rand.Reader 并显式检查 io.ReadFull 的返回错误——Go 不会帮你兜底,它只确保你每一步都清醒地做了安全选择。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











