rsa.generatekey panic 因密钥长度非2的幂或

rsa.GenerateKey 生成密钥时为什么总 panic: "crypto/rsa: invalid prime"?
因为 rsa.GenerateKey 的第二个参数(密钥长度)必须是 2 的幂且 ≥ 2048,常见错误是传了 1024 或非整数位长(比如 2056)。Go 的 RSA 实现严格校验 p、q 是否为安全素数,若位长太小或内部素数生成失败,会直接 panic 而不是返回 error。
- 必须用
2048、3072或4096—— 别碰1024,它已被认为不安全,且 Go 1.20+ 在部分环境下会拒绝生成 - 别自己算位长:传
2048,不是2048 / 2或math.Pow(2,11)(后者是 float64,类型不匹配) - 调用前确保
rand.Reader可用:rsa.GenerateKey(rand.Reader, 2048)——rand.Reader是加密安全的,别用rand.New(...)自定义 seed
公钥怎么从 *rsa.PrivateKey 里正确提取出来?
很多人以为 priv.PublicKey 就是能直接序列化的公钥,其实它只是结构体副本,缺少 ASN.1 编码所需的完整信息。真正要导出 PEM,得用 x509.MarshalPKIXPublicKey 再套一层 PEM 编码。
- 错误写法:
priv.PublicKey直接json.Marshal或写入 PEM —— 会丢失 OID 和编码格式,OpenSSL 无法识别 - 正确链路:
x509.MarshalPKIXPublicKey(&priv.PublicKey)→ 得到 DER 字节 → 用pem.Encode包进"PUBLIC KEY"类型块 - 注意:不要用
x509.MarshalPKCS1PublicKey,那是旧标准,OpenSSL 默认不认,除非你明确需要 PKCS#1 格式(如某些嵌入式场景)
私钥保存要不要加密?EncryptPEMBlock 和 MarshalPKCS8PrivateKey 怎么选?
不加密私钥 = 磁盘上明文存敏感材料,等于没设防;但加密方式选错,会导致其他语言(如 Python、Node.js)读不了。
- 如果只在 Go 内部用,且需密码保护:用
pkcs8.EncryptPEMBlock(来自golang.org/x/crypto/pkcs8),它生成的是带 PBKDF2 + AES 的 PKCS#8 加密块,兼容性好 - 如果目标系统是 OpenSSL 传统流程(比如 Nginx、curl),优先用
x509.MarshalPKCS1PrivateKey+pem.Encode,再用 OpenSSL 命令行加密:openssl pkcs8 -topk8 -v2 aes-256-cbc -in key.pem -out key-enc.pem - 别用
rsa.EncryptOAEP或手写 AES 加密私钥文件——密钥管理不是加个 cipher 就完事,PKCS#8 的 salt、iter、iv 都得规范处理
为什么用 rsa.SignPKCS1v15 签名后,Python 的 verify 报 “hash mismatch”?
根本原因不是签名错,而是 Go 和 Python 对“原始哈希输入”的理解不一致:Go 的 SignPKCS1v15 要求你先算好 hash,再把 hash.Sum(nil) 传进去;而 Python 的 verify 默认会自己再哈希一遍,导致二次哈希。
- Go 端必须显式指定 hash 类型,并传入原始摘要字节:
rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, hash.Sum(nil)) - Python 端要用
verify(data, signature, public_key, 'SHA256')并确保data是原始消息(不是 hash),否则就和 Go 对不上 - 更稳的做法:统一用
SignPSS(PSS 填充),它把 hash 和 salt 绑定进签名逻辑,跨语言一致性更好,只要双方都用crypto.SHA256和相同 salt 长度
密钥生命周期里最易被跳过的环节,是私钥导出后的权限控制和公钥的 ASN.1 结构验证 —— 写完文件记得 chmod 600,加载公钥前用 x509.ParsePKIXPublicKey 而不是硬转类型断言。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











