大多数 Python 代码——PyJWT、Authlib、paramiko、certbot,以及 AWS 和 Google 的 SDK——在执行真正的密码学运算时都使用 cryptography。它的 hazmat 层直接暴露密钥和签名;wheel 包自带 OpenSSL,因此行为取决于包的版本,而不是操作系统(50.0.2 自带 OpenSSL 4.0.3)。
ECDSA 签名输出为 ASN.1 DER,verify() 也按 DER 解析。WebCrypto、JWS 和大多数硬件令牌使用 64 字节的原始 r‖s 形式,verify() 会拒绝它,抛出的 InvalidSignature 与伪造签名的相同。decode_dss_signature 和 encode_dss_signature 在 DER 与整数对之间转换;定宽填充需要你自己补上,做法与示例完全一样,也可以用 DER ⇄ 原始格式转换器手动检查。
除非你要求使用 RFC 6979,否则 nonce 是随机的:ec.ECDSA(hashes.SHA256(), deterministic_signing=True) 完全复现了该 RFC 的 P-256 测试向量——包括其中的 high-S 值,因为这个包不会对 s 做任何规范化,验证时两个半区的 s 都接受。要交给 libsecp256k1 或 @noble/curves 等要求 low-S 的验证器的签名,需要先把 s 替换为 n − s。sign() 和 verify() 会自行对数据计算哈希;如果你手上已经是摘要,要传入 Prehashed(hashes.SHA256()),否则会计算两次哈希。
曲线覆盖面很广——P-224 到 P-521、secp256k1 和 Brainpool r1 曲线——50.0.2 还支持 Ed25519、Ed448、ML-KEM-768/1024 和 ML-DSA-44/65/87。密钥可从 PEM 和 DER(SPKI、PKCS#8、加密的 PKCS#8、SEC1)、OpenSSH 以及裸 SEC1 点加载;它没有 JWK 读取器,因此 JWK 需要借助 PyJWT、Authlib 或 jwcrypto,或者 JWK 转换器来处理。
人们搜索的大多数错误来自密钥加载,而不是签名:把公钥传给了 load_pem_private_key、加密的密钥没有提供密码,或者点不在指定的命名曲线上。当来自其他技术栈的签名无法验证通过时,签名验证工具和库互操作矩阵可以帮上忙。