rsa.GenerateKey panic 因公钥指数 e 未用 *big.Int 类型传入,须用 new(big.Int).SetInt64(65537);签名须先哈希再调用 SignPKCS1v15,验证时哈希类型与数据必须严格一致,PEM block type 要匹配标准格式。

rsa.GenerateKey 生成密钥对时为什么总 panic: crypto/rsa: invalid public exponent
因为 rsa.GenerateKey 要求传入的 e(公钥指数)必须是奇数且大于 1,常见但错误的做法是直接传 3 或 65537 的整数值,却忘了它必须是 *big.Int 类型。
- 正确做法:用
new(big.Int).SetInt64(65537)构造,65537是工业标准,兼顾安全与性能;3虽合法但不推荐用于生产 - 常见错误:传
65537(int64)或big.NewInt(65537)(看似对,实则可能被误写成big.NewInt(3)导致低安全性) - 密钥长度建议选
2048或4096;1024已被 NIST 弃用,Go 1.20+ 对其警告增强
签名时 crypto/rsa.SignPKCS1v15 返回 “crypto/rsa: message too long”
RSA 签名不是“把任意数据塞进去”,而是对摘要(hash)签名。错误在于直接对原始字节调用签名函数,忽略了最大输入限制 —— 它取决于密钥长度和填充方案。
- 必须先用哈希函数处理原文,例如
sha256.Sum256(data).Sum(nil),再将哈希结果传给SignPKCS1v15 -
SignPKCS1v15第二个参数是crypto.Hash类型,不是字符串;必须匹配哈希对象类型,比如用crypto.SHA256,否则签名验证必失败 - 若原文极小(如 JSON token),也别跳过哈希步骤 —— 协议层面要求如此,绕过等于放弃防篡改能力
用 rsa.PublicKey.Verify 验证失败,但私钥签名逻辑看起来没问题
验证失败八成不是算法问题,而是签名/验签两端的哈希方式、填充方案或数据预处理不一致。
- 确保签名和验证都用同一哈希:签名时传
crypto.SHA256,验证时也必须传crypto.SHA256,不能一个用SHA256一个用SHA256.New()实例 - 验证函数
VerifyPKCS1v15的第三个参数是哈希后的字节([]byte),不是原文;别把原文直接喂进去 - 注意编码:私钥签名输出是原始字节,若存为 base64 字符串传输,验证前必须完整 decode,任何截断或换行符都会导致失败
如何安全地序列化和加载 RSA 私钥(避免 PEM 乱码或解析失败)
Go 的 x509 和 pem 包配合使用是标准路径,但容易在 block type 或 ASN.1 编码上出错。
立即学习“go语言免费学习笔记(深入)”;
- 私钥序列化:用
x509.MarshalPKCS1PrivateKey(仅限 RSA)→pem.Encode,block type 必须是"RSA PRIVATE KEY";用"PRIVATE KEY"会生成 PKCS#8,需改用x509.MarshalPKCS8PrivateKey - 公钥序列化:用
x509.MarshalPKIXPublicKey→pem.Encode,block type 固定为"PUBLIC KEY";别写成"RSA PUBLIC KEY"(那是旧格式,ParsePKIXPublicKey不认) - 加载私钥时,如果 PEM 内容带多余空格或 Windows 换行符(
\r\n),pem.Decode仍能处理;但若整个 PEM 被二次 URL 编码或 JSON 字符串转义未还原,就会静默失败










