エラーメッセージ
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())) はデータを自分でハッシュします。計算済みのダイジェストを utils.Prehashed なしで渡すと、そのダイジェストがもう一度ハッシュされます。これは、不可解な検証失敗の 3 大原因の 1 つです。
鍵またはハッシュの誤り
ときどきローテーションされた公開鍵や一致しない公開鍵を使っている、あるいは SHA-384 で署名されたものを SHA-256 で検証している場合です。ハッシュは署名方式の一部であり、自由に選べるパラメーターではありません。
修正方法
- 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 の正規化、曲線の誤りといった、よくある不一致の仮説を試し、どの仮説なら署名の検証が通るかを示します。