エラーメッセージ
Invalid EC key. Point is not on the curve specified.
cryptography 50.0 で再現しました。EllipticCurvePublicKey.from_encoded_point() は、同じ失敗を「Invalid EC key.」という、より簡潔なメッセージで報告します。ほかのスタックでは表現が異なり、OpenSSL 1.1 のエラースタックは「point is not on curve」と出力し、Node は JWK を「Invalid JWK EC key」で拒否しますが、その背後にある数学はどれも同じです。
Python cryptography — ValueError from EllipticCurvePublicNumbers.public_key()
このエラーの意味
指定した (x, y) の組は、指定した曲線の方程式 y² = x³ + ax + b (mod p) を満たしていません。ライブラリはインポート時にこれを検証します。曲線上にない点を受け入れると、秘密鍵が漏洩しうる無効曲線攻撃が可能になるためです。つまり、この拒否は安全のためのチェックであり、杓子定規な対応ではありません。このチェックは曲線の方程式を 1 回計算するだけの軽い処理で、まともなライブラリはどれもこれを実行します。曲線上にない点はどこかで必ず拒否されるので、後段の処理で拒否されるよりも、ここではっきりと拒否されるほうが望ましいのです。
発生する理由
座標が別の曲線と組み合わされている
よくあるsecp256k1 の点(Bitcoin/Ethereum)が SECP256R1/P-256 として宣言されている、またはその逆です。どちらの曲線も座標は 32 バイトなので、ほかのチェックではこの取り違えを検出できず、検出できるのは曲線の方程式だけです。
座標のバイト列が破損している、または切り詰められている
よくある先頭のゼロが削除されている(31 バイトの座標)、16 進数のデコードが 1 文字ずれている、base64 と base64url を混同している、1 ビットだけ反転しているなど、x や y が少しでも変わると、点はほぼ確実に曲線から外れます。
バイト順が間違っている
ときどき標準的なエンコーディングでは、座標はどれもビッグエンディアンです。リトルエンディアンに反転したバイト列(ハードウェアとの連携や自作のコードでよく起こります)は、まったく別の点、つまり曲線上にない点を表します。
修正方法
- 1.
エンコードされた点から、正しい曲線を指定して鍵を構築する
from_encoded_point は長さと構造を一度に検証します。元のデータがどちらの曲線のものか曖昧な場合は、もう一方の候補の曲線も試してください。
python from cryptography.hazmat.primitives.asymmetric import ec point = b"\x04" + x_bytes + y_bytes # uncompressed, 65 bytes for P-256 key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256R1(), point) # if that raises, test: ec.SECP256K1() — the other 256-bit suspect - 2.
元になる値を確認する
鍵を構築する前に、長さが 32 バイトであることと、値が妥当であることを確認してください。削除されたゼロは、左側をゼロでパディングして復元してください。
python print(len(x_bytes), len(y_bytes)) # must be 32, 32 for P-256 x_bytes = x_bytes.rjust(32, b"\x00") # repair stripped leading zeros y_bytes = y_bytes.rjust(32, b"\x00")