rsa.SignPKCS1v15验签失败主因是哈希算法不一致或填充未对齐:签名与验签必须使用完全相同的crypto.Hash值、对同一原始数据做相同哈希,且公钥格式需为PKIX标准。

rsa.SignPKCS1v15 为什么总报 crypto/rsa: verification error
签名验签失败,八成是两端用的哈希不一致或填充方式没对齐。Go 的 rsa.SignPKCS1v15 和 rsa.VerifyPKCS1v15 要求签名前必须先哈希,且哈希算法必须完全相同——不是“都用 SHA256”就行,而是调用时传的 crypto.Hash 值(比如 crypto.SHA256)必须一致,且哈希后的字节数要匹配密钥长度减 11。
- 签名端:先用
hash.Hash.Write()写入原始数据,再把hash.Sum(nil)结果喂给rsa.SignPKCS1v15 - 验签端:必须对**同一原始数据**做完全相同的哈希运算,再把结果传给
rsa.VerifyPKCS1v15 - 别直接对明文签名——
rsa.SignPKCS1v15不接受原始字节,只接受哈希摘要;传错会 panic 或返回错误 - 私钥和公钥必须配对,且公钥从私钥导出时不能丢精度:
priv.Public()是安全的,但手动序列化再反解可能出问题
如何正确生成 RSA 密钥并安全保存
Go 里没有“一键生成并存文件”的封装,得自己控制 PEM 编码流程。密钥长度建议至少 2048 位,3072 更稳妥;小于 1024 已被主流环境拒绝。
- 生成:用
rsa.GenerateKey(rand.Reader, 2048),别用rand.New(rand.NewSource())——密码学场景必须用crypto/rand - 私钥保存:用
pem.Encode包裹x509.MarshalPKCS1PrivateKey,块类型必须是"RSA PRIVATE KEY"(不是"PRIVATE KEY",后者是 PKCS#8,rsa包不认) - 公钥保存:用
x509.MarshalPKIXPublicKey+"PUBLIC KEY"块类型;别用MarshalPKCS1PublicKey,它输出的是旧格式,某些语言验签会失败 - 文件权限:私钥文件务必设为
0600,Go 不帮你管 fs 权限
签名时要不要自己做哈希?怎么选 crypto.Hash
要,而且必须做。rsa.SignPKCS1v15 不处理哈希,只做数学签名;它信任你传进来的摘要确实是对应原文的、且没被篡改。
- 选
crypto.SHA256最常用:兼容性好,摘要长度 32 字节,2048 位密钥下留足填充空间 - 别用
crypto.MD5或crypto.SHA1:已被证明不安全,OpenSSL 等工具默认拒收 - 如果原文极大(如百 MB 文件),别把整个内容读进内存哈希——用
hash.Hash的流式接口:io.Copy(hash, file),再取hash.Sum(nil) - 注意:
rsa.VerifyPKCS1v15第三个参数是hash.Hash的Size()返回值,不是硬编码数字;写死32看似省事,换算法就崩
跨语言验签失败?重点检查这三处
Go 签名在 Python / Node.js / Java 里验不过,通常不是算法问题,而是二进制表示或编码细节没对齐。
立即学习“go语言免费学习笔记(深入)”;
- 公钥格式:确认对方用的是 PKIX(即
"PUBLIC KEY"PEM 块),不是 OpenSSL 默认的"RSA PUBLIC KEY"(那是 PKCS#1) - 签名结果是否 base64 或 hex 编码过:Go 的
SignPKCS1v15返回的是原始字节,有些语言 SDK 默认期待 base64 字符串,直接传字节会错位 - 原文编码:Go 里
[]byte("hello")是 UTF-8,但 Python 里b"hello"是 bytes,而"hello".encode('utf-8')才等价;空格、BOM、换行符差异都会导致哈希不一致










