PyJWT、Authlib、paramiko、certbot、AWS や Google の SDK など、ほとんどの Python コードは実際の暗号処理に cryptography を使っています。hazmat 層では鍵と署名を直接扱えます。wheel には独自の OpenSSL が同梱されているため、動作は OS ではなくパッケージのバージョンで決まります(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 テストベクトルを、s が high-S の値である点まで含めて正確に再現しました。パッケージには s を正規化する処理がなく、検証は s が n の上半分と下半分のどちらにあっても受け入れるためです。libsecp256k1 や @noble/curves のように low-S を要求する検証者に渡す署名は、先に s を n − s に置き換える必要があります。sign() と verify() はデータを自分でハッシュ化します。すでにダイジェストを持っている場合は Prehashed(hashes.SHA256()) を渡してください。そうしないと二重にハッシュ化されます。
50.0.2 の対応曲線は幅広く(P-224 から P-521 まで、secp256k1、Brainpool の r1 曲線)、さらに 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 に公開鍵を渡した場合、暗号化された鍵をパスワードなしで読み込んだ場合、指定した名前付き曲線上にない点を渡した場合などです。別のスタックで作られた署名がどうしても検証を通らないときは、署名検証ツールとライブラリ相互運用マトリクスが役立ちます。