エラーメッセージ
java.security.InvalidAlgorithmParameterException: Curve not supported: secp256k1 (1.3.132.0.10)
別の表記
- Legacy SunEC curve disabled
曲線が最初に現れる箇所に応じて、`KeyPairGenerator.initialize`、`KeyFactory.generatePublic/Private`(`InvalidKeySpecException` でラップされます)、`Signature.initVerify` からスローされます。JDK 15 ではすでにこれらの曲線がデフォルトで無効化されており(JDK-8237219)、メッセージは「Legacy SunEC curve disabled」で、`-Djdk.sunec.disableNative=false` で再び有効にできました。JDK 14 以前はこれらの曲線をサポートしていました。
Java (JDK 16+) — KeyPairGenerator / KeyFactory / Signature with the SunEC provider
このエラーの意味
JDK は secp256k1(Bitcoin と Ethereum で使われている曲線)という名前を知っており、OID 1.3.132.0.10 に解決することさえできますが、組み込みの SunEC プロバイダーはもはやこの曲線を実装していません。JDK 16 は、ネイティブの EC ライブラリとともにレガシーな曲線を削除し(リリースノート「Removal of Legacy Elliptic Curves」、JDK-8235710)、最新の実装を持つ曲線だけを残しました。P-256、P-384、P-521、X25519/X448、Ed25519/Ed448 です。Java 8 や 11 で動作していたコードは、アップグレード後にここで失敗します。
発生する理由
JDK 16 以降で実行している
よくあるJDK 16 で SunEC の曲線リストが縮小されました。最もよくあるきっかけは、ブロックチェーン関連サービスのランタイムのアップグレードです。
ES256K の JWT や secp256k1 の JWK
ときどきJCA に処理を委ねる JOSE ライブラリ(Nimbus など)で `ES256K`(RFC 8812)を使うには、secp256k1 に対応したプロバイダーが必要です。
2021 年より前のサンプルからコピーしたコード
ときどきデフォルトのプロバイダーで `ECGenParameterSpec("secp256k1")` を使うサンプルは、曲線が削除される前に書かれたものです。
修正方法
- 1.
Bouncy Castle プロバイダーを使う
Bouncy Castle は secp256k1 を純粋な Java で実装しています。プロバイダーとして登録し、`"BC"` を指定して明示的に要求してください。
java import java.security.*; import java.security.spec.ECGenParameterSpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; Security.addProvider(new BouncyCastleProvider()); KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "BC"); generator.initialize(new ECGenParameterSpec("secp256k1")); KeyPair pair = generator.generateKeyPair(); Signature signature = Signature.getInstance("SHA256withECDSA", "BC"); - 2.
Ethereum 関連の処理には専用のライブラリを使う
web3j などのライブラリは、secp256k1、リカバリ ID、EIP-191 のプレフィックスを自動的に処理します。素の JCA にはこうした機能はありません。
java // Ethereum signing/recovery: web3j implements secp256k1 itself (on top of Bouncy Castle). org.web3j.crypto.ECKeyPair keyPair = org.web3j.crypto.Keys.createEcKeyPair(); org.web3j.crypto.Sign.SignatureData sig = org.web3j.crypto.Sign.signPrefixedMessage(message, keyPair);