验证RSA签名失败最常见的原因是公钥格式不匹配:cryptography库要求PEM格式且含正确头尾标记(如-----BEGIN PUBLIC KEY-----),DER或SSH格式会报ValueError;同时verify()的padding和algorithm参数必须与签名生成时完全一致,原始数据还需严格标准化(如UTF-8编码、JSON规范序列化),否则验签静默失败。

验证RSA签名时,公钥格式不匹配会导致ValueError: No PEM start marker
最常见的失败不是算法逻辑错,而是公钥没加载成功。Python的cryptography库要求公钥必须是PEM格式且带正确头尾标记(如-----BEGIN PUBLIC KEY-----),直接用OpenSSL生成的DER或SSH格式公钥会直接报这个错。
实操建议:
- 用
openssl rsa -pubin -in pubkey.pem -text -noout确认文件含PEM头尾;若只有十六进制或base64乱码段,说明是DER——需转成PEM:openssl rsa -pubin -inform DER -in pubkey.der -outform PEM -out pubkey.pem - 如果公钥来自JWT或JWK,别直接传字符串,先用
jwcrypto.jwk.JWK.from_json()解析再导出PEM - 加载时别用
load_pem_public_key()硬刚,加一层try-except捕获ValueError并打印原始字节前100位,快速定位格式问题
cryptography中verify()调用失败的三个关键参数组合
签名验证不是“把数据、签名、公钥塞进去就完事”,padding和algorithm必须与签名生成时完全一致,否则一律抛InvalidSignature。
常见匹配关系:
立即学习“Python免费学习笔记(深入)”;
- 签名用
PKCS1v15+SHA256→ 验证时必须用padding.PKCS1v15()+hashes.SHA256() - 签名用
PSS(更安全)→ 验证时padding必须是padding.PSS(...),且mgf(掩码生成函数)和salt_length要一模一样 - 如果签名是用
pycryptodome的PKCS1_signature模块生成的,默认是SHA1,但cryptography默认用SHA256——不显式指定就会静默失败
验证前必须对原始数据做严格预处理
很多人把JSON字符串、HTTP请求体、数据库记录原样喂给verify(),结果失败。RSA签名验证对输入字节流极其敏感:换行符、空格、编码、BOM、字段顺序,差一点就验不过。
务必检查:
- 原始数据是否已用
.encode('utf-8')转为bytes?传str会直接报TypeError - 如果是JSON,是否用
json.dumps(obj, separators=(',', ':'), sort_keys=True)标准化?避免因空格或键序不同导致哈希值变化 - HTTP签名场景下,是否只取
body而忽略了Content-Type头或Signature头本身?RFC 8785明确要求canonicalization
测试用例里最容易漏掉的边界情况
本地跑通不代表线上安全。以下情况不覆盖,上线后可能被绕过:
- 用私钥签名后,故意篡改签名最后1字节,验证必须返回False——否则说明没校验完整签名长度
- 传入空bytes、None、超长字符串(>64KB)作为待验数据,确认不崩溃且返回合理异常
- 公钥是自签名证书里的,但证书已过期或被吊销——
cryptography不校验证书链,只认公钥本身,这点常被误认为“验证通过=可信”
真正难的不是调通API,是确保你验证的“原始数据”和签名方签署的“原始数据”在每一个字节上都等价。差一个换行,验签就失效,而且不会报错,只会默默返回False。


















