Перейти к содержимому
ecdsa.com
Java · Kotlin

Java JCA (SunEC, JDK 25)

Java Cryptography Architecture с провайдером SunEC из JDK, как в JDK 25: по умолчанию DER, есть вариант в формате P1363, случайные nonce, только три кривые NIST, а также Ed25519, Ed448, ML-KEM и ML-DSA.
Лицензия
GPL-2.0-only WITH Classpath-exception-2.0
Проверенная версия
25.0.4.1 ·
Исходный код
github.com/openjdk/jdk
Документация
docs.oracle.com/en/java/javase/25/security/java-cryptography-architecture-jca-reference-guide.html

Совместимость вкратце

ECDSAEdDSAПостквантовая криптография

Кривые ECDSA
P-256P-384P-521
EdDSA
Ed25519Ed448

Код на Java подписывает данные через java.security.Signature, KeyPairGenerator и KeyFactory, а работу выполняют собственные провайдеры JDK: SunEC для эллиптических кривых и EdDSA, а также более новые провайдеры ML-KEM (javax.crypto.KEM, JDK 24) и ML-DSA. Android, Kotlin и все фреймворки для JVM построены на том же API, поэтому его значения по умолчанию проявляются в Spring, Keycloak и бесчисленных SDK.

Кодировку определяет имя алгоритма. SHA256withECDSA выдаёт и ожидает ASN.1 DER; SHA256withECDSAinP1363Format использует 64-байтовый формат raw r‖s, который нужен JWS и WebCrypto, как в примере. Если их перепутать, поведение несимметрично: подпись raw, переданная в SHA256withECDSA, приводит к исключению SignatureException, а подпись DER, переданная варианту P1363, просто даёт false. NONEwithECDSA подписывает вычисленный вами дайджест (такая подпись прошла проверку через SHA256withECDSA по исходному сообщению), а имя хеша не зависит от кривой, поэтому SHA256withECDSA с ключом P-384 допустим, но не проходит проверку у сторон, которые ожидают SHA-384.

SunEC в JDK 25 предлагает для ECDSA только secp256r1, secp384r1 и secp521r1: P-224, secp256k1 и кривые Brainpool вызвали ошибки, поэтому код для Bitcoin и Ethereum на JVM использует Bouncy Castle или web3j. Nonce случайные, s не нормализуется, а проверка принимает значения из обеих половин диапазона, поэтому для сторон, строго требующих low-S, в подписях нужно заменить s на n − s.

Ключи загружаются из X.509 SPKI DER (X509EncodedKeySpec), PKCS#8 DER (PKCS8EncodedKeySpec) или из отдельных компонентов в формате raw (ECPrivateKeySpec). Поддержка PEM появилась в JDK 25 в виде предварительного API (preview): java.security.PEMDecoder, JEP 470. В ходе проверки он декодировал открытый ключ только с --enable-preview, а в рабочей среде код по-прежнему сам убирает строки BEGIN и END и декодирует base64. Постквантовые алгоритмы действительно поддерживаются: инкапсуляция ML-KEM-512/768/1024, а также подпись и проверка ML-DSA-44/65/87 работали; SLH-DSA в этом JDK нет.

Сравнивайте кодировки подписей в конвертере DER ⇄ raw, а подписанные JAR-файлы разбирайте в инспекторе подписей JAR.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/java-jca/run.sh запустила Java JCA (SunEC, JDK 25) 25.0.4.1 в среде Eclipse Adoptium 25.0.4.1+1-LTS в образе контейнера maven:3.9.16-eclipse-temurin-25; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Кодировка подписи ECDSA

    der

    SHA256withECDSA возвращает ASN.1 DER; SHA256withECDSAinP1363Format возвращает 64 байта в формате raw r‖s.

    Что записала проверка
    fact ecdsa-signature-encoding = der
    evidence ecdsa-signature-encoding: SHA256withECDSAinP1363Format output is 64 bytes
  • Nonce в ECDSA

    random

    Случайный nonce: две подписи одного и того же сообщения тестовым ключом из RFC 6979 различались.

    Что записала проверка
    fact ecdsa-nonce = random
  • Low-S при подписи

    either

    s не нормализуется: среди 64 подписей встретились значения high-S.

    Что записала проверка
    fact ecdsa-low-s-sign = either
  • High-S при проверке

    accepts-high-s

    Подпись в DER, в которой s заменили на n − s, прошла проверку.

    Что записала проверка
    fact ecdsa-low-s-verify = accepts-high-s
  • Что хеширует вызов подписи

    hashes-message-or-digest

    SHA256withECDSA хеширует данные; NONEwithECDSA подписывает заранее вычисленный дайджест, и такая подпись прошла проверку через SHA256withECDSA по сообщению.

    Что записала проверка
    fact ecdsa-prehash = hashes-message-or-digest
  • Хеш-функция, привязанная к кривой

    none

    Хеш в имени алгоритма не привязан к кривой: с ключом P-384 SHA256withECDSA успешно подписал данные и проверил подпись.

    Что записала проверка
    fact ecdsa-hash-curve-binding = none
  • Некорректная подпись ECDSA

    throws

    Подпись raw, переданная в SHA256withECDSA, и 3-байтовый фрагмент данных вызывают исключение SignatureException; подпись DER, переданная варианту P1363, даёт false.

    Что записала проверка
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: raw r||s to SHA256withECDSA: throws SignatureException; DER to the P1363 algorithm: false; 3 bytes: throws SignatureException
  • Кривые ECDSA

    P-256P-384P-521

    secp256r1, secp384r1 и secp521r1; secp224r1, secp256k1 и brainpoolP256r1 недоступны.

    Что записала проверка
    fact ecdsa-curves = P-256, P-384, P-521
    evidence ecdsa-curves: P-224 (secp224r1) not available
    evidence ecdsa-curves: secp256k1 (secp256k1) not available
    evidence ecdsa-curves: brainpoolP256r1 (brainpoolP256r1) not available
  • EdDSA

    Ed25519Ed448

    Ed25519 и Ed448 через KeyPairGenerator и Signature с этими именами.

    Что записала проверка
    fact eddsa = Ed25519, Ed448
  • Постквантовые алгоритмы

    ML-KEMML-DSA

    ML-KEM-512/768/1024 через javax.crypto.KEM и ML-DSA-44/65/87 через Signature работали; KeyPairGenerator для SLH-DSA нет.

    Что записала проверка
    fact pqc = ML-KEM, ML-DSA
    evidence pqc: SLH-DSA KeyPairGenerator available: false
  • Форматы открытых ключей

    spki-der

    Открытые ключи загружаются из X.509 SPKI DER; getEncoded() возвращает их в той же форме.

    Что записала проверка
    fact public-key-formats = spki-der
    evidence public-key-formats: getEncoded() of 256-bit key returns X.509 SPKI DER; format X.509
  • Форматы закрытых ключей

    pkcs8-derraw-scalar

    Закрытые ключи загружаются из PKCS#8 DER и из скаляра в формате raw через ECPrivateKeySpec.

    Что записала проверка
    fact private-key-formats = pkcs8-der, raw-scalar
  • API для PEM

    preview

    java.security.PEMDecoder существует как предварительный API (preview, JEP 470) и декодировал открытый ключ PEM только с --enable-preview.

    Что записала проверка
    fact pem-api = preview
    evidence pem-api: java.security.PEMDecoder present: true (JEP 470, preview in JDK 25; needs --enable-preview)
    evidence pem-api: PEMDecoder.of().decode(PEM text) with --enable-preview: true

Использование

Подпишите данные и проверьте подпись в формате raw r‖s, который используют JWS и WebCrypto, выбрав вариант имени алгоритма для формата P1363.
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;

public class Usage {
    public static void main(String[] args) throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
        generator.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair pair = generator.generateKeyPair();
        byte[] message = "order #1042 approved".getBytes(StandardCharsets.UTF_8);

        // The algorithm name picks the hash AND the encoding:
        // SHA256withECDSA = DER, SHA256withECDSAinP1363Format = raw r||s (JWS, WebCrypto).
        Signature signer = Signature.getInstance("SHA256withECDSAinP1363Format");
        signer.initSign(pair.getPrivate());
        signer.update(message);
        byte[] raw = signer.sign();

        Signature verifier = Signature.getInstance("SHA256withECDSAinP1363Format");
        verifier.initVerify(pair.getPublic());
        verifier.update(message);
        System.out.println("raw bytes: " + raw.length + ", verified: " + verifier.verify(raw));
    }
}

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера maven:3.9.16-eclipse-temurin-25, и запуск завершается ошибкой, если вывод не совпадает.

Команда
java Usage.java
Вывод должен совпадать (регулярное выражение)
raw bytes: 64, verified: true

Библиотеки