Go crypto标准库是密码学原语集合,需手动组合AES块、IV、PKCS#7填充等;易错点包括密钥长度不符、IV复用、未填充、GCM nonce重用;推荐用scrypt派生密钥、GCM替代CBC、RSA仅用于加密AES密钥。
go 标准库的 crypto 系列包不是“开箱即用”的工具库,而是密码学原语集合——你得自己组合 aes 块、iv、填充、认证模式,才能跑通一次加解密。所谓“通用工具库”,本质是把重复踩坑的逻辑封装成可复用、难误用的接口。
为什么直接用 cipher.NewCBCEncrypter 容易出错
它不检查 IV 长度,不拒绝复用 IV,不处理填充,也不校验密钥字节长度。常见崩溃点:
-
aes.NewCipher传入 20 字节密钥 → panic: "invalid key size" - 用
[]byte{0,0,0,...}当 IV → 相同明文永远生成相同密文,等同于裸奔 - 加密后直接写密文,没存 IV → 解密时无从初始化
cipher.BlockMode - 明文长度不是 16 倍数,又没手动 PKCS#7 填充 → 解密后末尾乱码或 panic
AES-GCM 模式比 CBC 更适合做通用封装
GCM 是 AEAD(Authenticated Encryption with Associated Data)模式,一次调用同时完成加密 + 认证,无需手动填充,且能检测密文篡改。关键优势:
- 不需要额外实现填充逻辑:GCM 对输入长度无整除要求
- 自动绑定 nonce(相当于 IV):nonce 复用直接导致密钥泄露,
cipher.NewGCM强制要求 nonce 长度为gcm.NonceSize()(通常 12 字节),避免传错 - 解密失败会返回 error:比如 nonce 错、密文被改、tag 不匹配,不会静默输出垃圾数据
- 标准库已内置,无需第三方依赖
示例关键片段:
block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) nonce := make([]byte, gcm.NonceSize()) _, _ = rand.Read(nonce) // 必须每次唯一 ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
如何安全地从用户密码派生 AES 密钥
绝不能把 "my-pass-123" 直接转 []byte 当密钥用——熵太低,暴力破解秒破。必须用 KDF(密钥派生函数):
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 别用
sha256.Sum256:它是快速哈希,GPU 可每秒试几亿次 - 用
golang.org/x/crypto/scrypt:内存硬抗型,拖慢暴力速度 - 盐值(salt)必须随机且每次加密都不同:和 nonce 一样,需随密文持久化(如前置在文件/消息开头)
- 参数建议:
N=32768, r=8, p=1,输出长度固定为 32(适配 AES-256)
错误示范:key := []byte(os.Getenv("SECRET"))
正确做法:key, _ := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
为什么不要自己封装 RSA 加解密为“通用算法”
RSA 不是拿来加密大块数据的。它的明文长度上限极低(比如 2048 位密钥最多加密 245 字节),且填充方式(PKCS#1 v1.5 或 OAEP)必须两端严格一致,稍有偏差就 decryption error。真实场景中:
- 用 RSA 加密一个随机生成的 AES 密钥(即“信封加密”)
- 用该 AES 密钥加密实际数据
- 把加密后的 AES 密钥 + AES 密文 + IV/nonce 一起打包传输
强行把 RSA 当通用加解密入口,只会让使用者掉进“加密成功但解密必炸”的坑里。
真正难的不是写对一行 aes.NewCipher,而是确保每次加密都生成新 nonce、每次派生都用新 salt、每次传输都带齐元数据、每次解密都校验 tag——这些细节漏掉任意一环,安全就归零。通用库的价值,正在于把“必须做对”的事变成“不做错很难”。

















