crypto.ErrVerification仅表示哈希比对失败,根本原因必在哈希值不一致、公钥类型错误或签名长度异常三者之一;需严格确保验签时的hashed []byte和hash crypto.Hash与签名时完全一致,且公钥经正确类型断言。

rsa.VerifyPKCS1v15 返回 crypto.ErrVerification 不代表验签逻辑失败,只说明输入不匹配——问题几乎总出在哈希、公钥或签名三者之一。
rsa.VerifyPKCS1v15 为什么总返回 crypto.ErrVerification
这个错误最常被误读为“签名无效”,但它实际含义是:验签流程走完了,但 ASN.1 解包 + 哈希比对这一步没通过。真正原因藏在输入源头:
-
hashed []byte必须是和签名时完全一致的哈希输出(比如sha256.Sum256(data).[:]),不能是原始数据、不能是 hex 字符串、不能少一个字节 -
hash crypto.Hash参数必须和签名时传给rsa.SignPKCS1v15的值严格相等,crypto.SHA256≠sha256.New(),也不等于字符串"sha256" - 签名长度必须匹配密钥位数:2048 位私钥产生的签名一定是 256 字节;若你拿到的是 base64 解码后 344 字节,说明解码前不是纯二进制签名(比如混了 PEM 头尾)
从 PEM 或证书里取公钥,类型断言容易 panic
Go 的 *rsa.PublicKey 类型来自 crypto/rsa 包,而 x509.ParsePKIXPublicKey 返回的是 interface{},直接传给 rsa.VerifyPKCS1v15 会 panic:“interface conversion: interface {} is *rsa.PublicKey, not *rsa.PublicKey”——看着一样,实则是不同包路径导致的类型不兼容。
- 从 PEM 公钥文件解析:用
pem.Decode→x509.ParsePKIXPublicKey→ 显式断言pubKey.(*rsa.PublicKey) - 从
*x509.Certificate取:先确认cert.SignatureAlgorithm == x509.SHA256WithRSA,再写cert.PublicKey.(*rsa.PublicKey) - 断言失败时别硬转,说明公钥不是 RSA 类型(可能是 ECDSA),得换用
ecdsa.VerifyASN1
哈希计算方式错,验签必崩,但不报错
rsa.VerifyPKCS1v15 不检查你传的 hashed 是怎么来的,只按 hash 参数指定的算法去解包并比对。哈希算错,它照常执行,结果必然 crypto.ErrVerification。
- 正确做法:用
h := sha256.New(); h.Write(data); sum := h.Sum(nil),然后传sum和crypto.SHA256 - 错误写法:用
sha256.Sum256(data)得到结构体,却传crypto.SHA1;或用md5.Sum却传crypto.SHA256——函数不拒绝,但验签永远失败 - 大文件别全读内存:用
io.Copy(h, file)流式哈希,避免 OOM;h.Sum(nil)才是你要传给验签函数的切片
PKCS#1 v1.5 vs PSS:选哪个、怎么配
除非对接遗留系统,否则优先用 rsa.SignPSS + rsa.VerifyPSS。PSS 填充含随机性,抗选择明文攻击,且 Go 标准库对它的参数校验更严格,能提前暴露配置错误。
立即学习“go语言免费学习笔记(深入)”;
- v1.5 签名输出长度固定(= 密钥字节数),PSS 输出长度相同但每次不同;两者都不能混用
- PSS 必须配
&rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.SHA256},SaltLength别硬写数字,Hash必须和哈希实例一致 - 验签失败时,PSS 的错误信息更明确(比如
crypto/rsa: verification error: hash mismatch),v1.5 只统一返回crypto.ErrVerification
fmt.Printf("%x", hash) 对比十六进制)、公钥是否真的来自同一对密钥、签名是否被截断或编码污染。这些点漏掉任何一个,crypto.ErrVerification 就会一直出现。


















