如何在Golang中实现RSA密钥对生成与签名 Go语言crypto/rsa非对称加密

1次阅读

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

如何在 Golang 中实现 RSA 密钥对生成与签名 Go 语言 crypto/rsa 非对称加密

rsa.GenerateKey 生成密钥对时为什么总 panic: crypto/rsa: invalid public exponent

因为 rsa.GenerateKey 要求传入的 e(公钥指数)必须是奇数且大于 1,常见但错误的做法是直接传 365537 的整数值,却忘了它必须是 *big.Int 类型。

  • 正确做法:用 new(big.Int).SetInt64(65537) 构造,65537 是工业标准,兼顾安全与性能;3 虽合法但不推荐用于生产
  • 常见错误:传 65537(int64)或 big.NewInt(65537)(看似对,实则可能被误写成 big.NewInt(3) 导致低安全性)
  • 密钥长度建议选 204840961024 已被 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 的 x509pem 包配合使用是标准路径,但容易在 block type 或 ASN.1 编码上出错。

立即学习 go 语言免费学习笔记(深入)”;

  • 私钥序列化:用 x509.MarshalPKCS1PrivateKey(仅限 RSA)→ pem.Encode,block type 必须是 "RSA PRIVATE KEY";用 "PRIVATE KEY" 会生成 PKCS#8,需改用 x509.MarshalPKCS8PrivateKey
  • 公钥序列化:用 x509.MarshalPKIXPublicKeypem.Encode,block type 固定为 "PUBLIC KEY";别写成 "RSA PUBLIC KEY"(那是旧格式,ParsePKIXPublicKey 不认)
  • 加载私钥时,如果 PEM 内容带多余空格或 Windows 换行符(rn),pem.Decode 仍能处理;但若整个 PEM 被二次 URL 编码或 JSON 字符串转义未还原,就会静默失败
密钥导出格式、哈希选择、PEM block type 这三处最常被当成“细节”跳过,结果卡在验证环节查半天——其实问题不在加密逻辑,而在协议约定是否对齐。

text=ZqhQzanResources