Go标准库crypto/rsa签名必须先哈希再签名,SignPKCS1v15不自动哈希,需手动传入SHA256等摘要字节;私钥解析须区分PKCS#1与PKCS#8格式;VerifyPKCS1v15失败常见原因包括公钥类型错误、摘要长度不匹配及签名解码后长度异常。

Go 标准库的 crypto/rsa 签名接口不直接处理原始数据,必须先哈希再签名;跳过哈希步骤或用错哈希类型会导致 VerifyPKCS1v15 永远失败。
rsa.SignPKCS1v15 要求你手动传入哈希摘要,不是原始数据
这是最常踩的坑:误以为 SignPKCS1v15 会自动对输入做 SHA256 计算。它不会——它只加密你传进去的字节。如果你直接传 []byte("hello"),签名和验证必然不匹配。
- 必须先用
crypto.SHA256.New()创建哈希器,Write()原始数据,再调用Sum(nil)得到摘要字节 - 传给
SignPKCS1v15的第四个参数是这个摘要,不是原文 - 哈希算法标识(如
crypto.SHA256)必须和验证时用的完全一致,否则VerifyPKCS1v15直接返回crypto.ErrVerification
私钥加载必须区分 PKCS#1 和 PKCS#8 格式
PEM 块类型决定解析方式:"RSA PRIVATE KEY" 对应 x509.ParsePKCS1PrivateKey,而 "PRIVATE KEY" 是 PKCS#8,得用 x509.ParsePKCS8PrivateKey。混用会 panic 或返回 nil。
- 用
pem.Decode()先解出 block,检查block.Type再选解析函数 - OpenSSL 默认生成的是 PKCS#8(
openssl genpkey),而ssh-keygen -t rsa默认输出的是 PKCS#1 - 如果解析失败且 error 是
"x509: unknown key type",大概率是格式不匹配
VerifyPKCS1v15 失败时不要只看 error 文本
VerifyPKCS1v15 返回的 error 类型很有限,常见但易被忽略的原因有三个:
立即学习“go语言免费学习笔记(深入)”;
- 公钥类型错误:传了
*rsa.PrivateKey而不是*rsa.PublicKey,或用了privateKey.PublicKey但没取地址(&privateKey.PublicKey) - 摘要长度不匹配:SHA256 摘要固定 32 字节,若传入其他长度(比如自己截断或拼接),验证直接失败
- 签名被 base64 解码后长度异常:2048 位密钥的 PKCS#1 v1.5 签名长度固定为 256 字节;若解码后不是 256,说明传输损坏或编码错误
生产环境别硬编码 crypto/rand.Reader
虽然 rand.Reader 在绝大多数场景下够用,但它依赖系统熵池。在容器或嵌入式环境中,首次调用可能阻塞数秒甚至超时失败。
- 可提前用
rand.Read([]byte)测试是否可读,或加超时包装 - 签名过程本身不耗熵,但
SignPKCS1v15内部会读取少量随机字节用于防侧信道攻击;不能替换成伪随机源 - 如果服务启动阶段频繁生成密钥对,建议用
rsa.GenerateMultiPrimeKey替代GenerateKey,减少单次调用熵需求
真正难的不是调通签名,而是确保每次哈希、每次解析、每次传参都严格对齐——少一个 &,错一个 Type,漏一次 Sum(nil),验证就静默失败。这些点不在文档首页,但在日志里永远只显示 “verification failed”。


















