Сообщение об ошибке
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 сократился. Чаще всего ошибку вызывает обновление среды выполнения у сервиса, связанного с блокчейном.
JWT с ES256K или JWK на secp256k1
иногдаБиблиотекам JOSE, которые передают криптографию в JCA (например, 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, идентификаторами восстановления и префиксами 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);