Go crypto标准库需手动组合密码学原语:AES密钥必须为16/24/32字节,IV须16字节随机且每次加密唯一并明文传输,PKCS#7填充需手动实现,哈希比较须用bytes.Equal,RSA加解密填充方式必须严格一致,密钥派生与随机数生成须用crypto/rand。

Go 的 crypto 标准库不是“拿来即用”的加密工具箱,而是密码学原语的组合套件——你得自己搭电路,不能直接插电开机。
为什么 aes.NewCipher 总 panic “invalid key size”?
因为 aes.NewCipher 只认三种密钥长度:16、24 或 32 字节,分别对应 AES-128 / AES-192 / AES-256。传 12 字节、20 字节、甚至 32 个字符的字符串(UTF-8 下可能超 32 字节),都会直接 panic。
- 常见错误:从配置读
env.CRYPTO_KEY="my-secret-key"就直接丢给aes.NewCipher([]byte(key)),没校验长度 - 正确做法:用
sha256.Sum256([]byte(key)).Sum(nil)[:32]固定截出 32 字节(AES-256),或用scrypt.Key派生——别把原始密码当密钥 - 注意:
len("abc") == 3,但len("密码") == 6(UTF-8 编码),别被字符串长度骗了
cipher.NewCBCEncrypter 报错或解密乱码,八成是 IV 问题
CBC 模式下,iv 不是可选参数,而是每次加密都必须换的新鲜随机值;且长度必须严格等于 aes.BlockSize(16 字节),不能为 nil,也不能复用。
- 常见错误:用固定字节数组如
[]byte{0,0,0,...}当 IV;或加密一次后缓存 IV 多次复用 - 正确做法:用
crypto/rand.Read(iv)生成,再用io.ReadFull(rand.Reader, iv)确保读满 16 字节 - 传输规则:IV 不需要保密,但必须随密文一起发出去——标准做法是把 IV 明文前置在密文开头
- 别跳过填充:CBC 要求明文长度是 16 的整数倍,得手动实现 PKCS#7 填充,
crypto/cipher不提供自动填充
用 md5.Sum 或 sha256.Sum256 比较哈希值,千万别用 ==
md5.Sum(data) 返回的是一个带数组字段的结构体,不是切片;直接 == 比较的是整个结构体(含未初始化内存),必然失败。
立即学习“go语言免费学习笔记(深入)”;
- 常见错误:
if md5.Sum(data) == md5.Sum(other) { ... }—— 这个分支永远进不去 - 正确做法:调用
.Sum(nil)得到[]byte,再用bytes.Equal(a, b)比较 - 或者转字符串:
fmt.Sprintf("%x", sum)(注意是%x,不是%X,大小写不一致会导致比较失败) - 提醒:MD5 和 SHA-1 已不安全,仅限校验场景(如文件下载完整性),绝不能用于密码存储
rsa.Decrypt 报 “decryption error”,几乎全是填充方式对不上
RSA 加解密必须两端使用完全相同的填充方案:加密用了 rsa.EncryptPKCS1v15,解密就必须用 rsa.DecryptPKCS1v15;换成 rsa.DecryptOAEP 就会炸。
- 常见错误:加密端用 PKCS#1 v1.5,解密端误用 OAEP;或反过来
- 私钥长度要够:至少 2048 位,推荐 4096;用
ssh-keygen -t rsa -b 4096生成的私钥,标准库不支持,得用golang.org/x/crypto/ssh解析 - 解密返回的明文可能带前导零字节(尤其 PKCS#1 v1.5),别直接
string(res)打印,先bytes.TrimLeft(res, "\x00") - 别用
math/rand生成密钥或 IV——它不是密码学安全的,等于把锁换成纸糊的
最麻烦的点不在 API 多难记,而在于每个环节都要求你主动做决定:密钥怎么派生、IV 怎么传、填充怎么填、错误怎么判、标签怎么验。漏掉任意一环,表面跑通,实则埋雷。


















