3、常见失败原因总览
签名校验失败,说白了就是「验签没通过」。我排查过不下百次这类问题,发现翻来覆去就那么几个坑。今天我把它们全摆出来,你对照着看,能省不少时间。
核心结论:90% 的签名校验失败,都逃不出下面这五类原因。记住它们,排查时心里就有底了。
3.1 密钥不匹配
这是最常踩的坑,没有之一。签名用的是私钥,验签用的是公钥。这两把钥匙必须是一对,否则怎么验都是失败。
我记得有一次,同事折腾了一下午,最后发现他把测试环境的公钥配到了生产环境。你说冤不冤?
| 场景 | 典型表现 | 排查方向 |
|---|---|---|
| 公钥与私钥不配对 | 每次验签都失败,错误码固定 | 检查密钥对的生成时间、指纹 |
| 公钥配置错误 | 部分环境正常,部分失败 | 对比各环境的公钥文件 |
| 密钥格式不对 | 解析密钥时报错 | 确认 PEM/DER 格式是否匹配 |
我的习惯:每次部署前,我会用 openssl 命令验证一下公钥和私钥是否匹配。就一行命令的事,能省掉后面几小时的排查时间。
# 验证私钥和公钥是否匹配
openssl pkey -in private.pem -pubout | openssl md5
openssl x509 -in public.pem -pubkey | openssl md5
# 对比两个 MD5 值,一致就没问题
3.2 数据被篡改
签名校验的本质,就是验证「数据」和「签名」是否对应。如果有人改了数据内容,但没重新生成签名,那验签必然失败。
为什么会这样?你想想看,签名是对原始数据的哈希值做的加密。数据一变,哈希值就变了,用公钥解出来的哈希值对不上,自然就失败了。
- 传输中被篡改:中间人攻击、代理服务器修改了请求体
- 拼接顺序错误:签名时参数的顺序和验签时不一致
- 编码问题:URL 编码、Base64 编码前后不一致
我曾经踩过的坑:有一次排查了半天,最后发现是客户端把参数做了 URL 解码,但服务端验签时用的是原始编码后的字符串。两边数据不一样,签名怎么可能通过?
3.3 算法不一致
签名算法有很多种:RSA、ECDSA、HMAC……每种又有不同的哈希算法搭配。签名方和验签方用的算法必须完全一致。
我见过最离谱的一次,服务端用的是 SHA256withRSA,客户端用的是 SHA1withRSA。两边都觉得自己没错,但就是验不过。
| 常见算法组合 | 签名方 | 验签方 |
|---|---|---|
| RSA + SHA256 | SHA256withRSA | SHA256withRSA |
| ECDSA + SHA256 | SHA256withECDSA | SHA256withECDSA |
| HMAC-SHA256 | HmacSHA256 | HmacSHA256 |
关键点:算法名称的字符串也要完全一致。比如 Java 里的 "SHA256withRSA" 和 "SHA256WithRSA"(大小写不同),在某些库中会被认为是不同的算法。
3.4 时间戳问题
很多签名机制会加入时间戳,防止重放攻击。如果客户端和服务端的时间不一致,签名就会失效。
说白了,就是你的签名里带了一个「当前时间」,服务端收到后一看——这时间不对啊,要么是未来,要么是过去太久,直接拒掉。
- 时钟偏差:服务器和客户端时间差超过允许范围(通常是 5 分钟)
- 时区问题:一个用 UTC,一个用 GMT+8,差了好几个小时
- 时间格式:时间戳是秒还是毫秒?这个搞错了也会出问题
我建议:所有服务统一使用 UTC 时间,时间戳用毫秒级。客户端和服务端都配置 NTP 自动同步,能省掉 90% 的时间相关问题。
3.5 证书过期
证书是有有效期的。过期了,签名校验自然就失败了。这个问题其实很好排查,但往往被忽略。
我记得有一次线上告警,查了半天,最后发现是证书刚好在凌晨 0 点过期。嗯,就是这么巧。
| 证书状态 | 验签结果 | 处理方式 |
|---|---|---|
| 有效期内 | 正常通过 | 无需处理 |
| 已过期 | 失败,返回证书过期错误 | 更新证书,重新部署 |
| 即将过期(30天内) | 正常通过,但应预警 | 提前申请新证书,安排轮换 |
注意:证书过期不是瞬间切换的。旧证书过期后,新证书还没部署上去,中间会有个空窗期。我建议提前两周就开始准备证书轮换,别等到最后一刻。