错误信息
cryptography.exceptions.InvalidSignature
这个异常抛出时不带任何消息文本——回溯信息的最后一行只有类名(在 cryptography 50.0 上复现)。
Python cryptography — raised by verify()
含义
public_key.verify(signature, data, ...) 判定:用这个密钥验证这段数据时,签名不通过。所有输入都解析成功了,只是验证没有通过。由于这个异常在设计上就不带任何详细信息,排查时只能逐一单独检验每个输入——密钥、数据字节、签名编码、哈希。
原因
在需要 DER 的地方用了原始 r‖s 签名
常见cryptography 的 ECDSA verify() 接受的是 ASN.1 DER。来自 WebCrypto、JWS 或其他库“raw”模式的 64 字节原始签名不会被报告为格式错误:verify() 抛出的仍是与伪造签名相同的、不带任何信息的 InvalidSignature,所以这种混用特别容易被忽略。
数据字节不一致
常见str 与 bytes 的编码差异、末尾多出的换行符、以不同的键顺序或空白重新序列化 JSON——签名和 verify() 处理的都是精确的字节,“语义相同”并不等于相同。
误用 Prehashed 导致重复哈希
常见verify(sig, data, ec.ECDSA(hashes.SHA256())) 会自己对 data 做哈希。如果传入已经算好的摘要却没有使用 utils.Prehashed,摘要会被再哈希一次——这是导致莫名其妙的验证失败的前 3 大原因之一。
密钥或哈希算法用错了
偶见公钥已轮换或不匹配,或者用 SHA-256 验证了基于 SHA-384 生成的签名。哈希算法是签名方案的一部分,不是可以随意选择的参数。
修复方法
- 1.
将原始签名转换为 DER
如果签名是 64 字节(P-256),先拆分为 r 和 s,重新编码为 DER,再验证。
python from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature r = int.from_bytes(raw_sig[:32], "big") s = int.from_bytes(raw_sig[32:], "big") der_sig = encode_dss_signature(r, s) public_key.verify(der_sig, message, ec.ECDSA(hashes.SHA256())) - 2.
对预先计算的摘要使用 Prehashed
如果你手里的是摘要而不是消息,就要明确声明——否则库会对你的哈希值再做一次哈希。
python from cryptography.hazmat.primitives.asymmetric import utils public_key.verify( der_sig, digest_bytes, # already SHA-256(message) ec.ECDSA(utils.Prehashed(hashes.SHA256())), ) - 3.
找出出错的是哪个输入
把密钥、消息和签名粘贴到下方的错误解读工具中:它会逐一检验常见的不匹配假设——哈希算法不对、原始格式与 DER 混用、重复哈希、high-S 规范化、曲线不对——并指出能让你的签名验证通过的那一个。