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

Bouncy Castle (Java)

Провайдер Java и набор инструментов PKIX от The Legion of the Bouncy Castle: все кривые, удалённые из JDK, детерминированный ECDSA по запросу, разбор PEM и самый широкий на JVM набор постквантовых подписей.
Лицензия
MIT
Проверенная версия
1.86 ·
Исходный код
github.com/bcgit/bc-java
Документация
www.bouncycastle.org/documentation/documentation-java/

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

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

Кривые ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Постквантовые алгоритмы
ML-KEMML-DSASLH-DSAFalconLMSXMSS

Bouncy Castle — это сразу две вещи: провайдер JCA (BC), который подключается к Signature, KeyPairGenerator и KEM, и легковесные API, например ECDSASigner, работающие без JCA. bcpkix добавляет сертификаты, CMS, OCSP и PEMParser, которого до сих пор нет в JDK. Большая часть кода на JVM, работающего с secp256k1, Brainpool, PGP или сертификатами, зависит от Bouncy Castle.

Параметры ECDSA по умолчанию совпадают с JDK: SHA256withECDSA выдаёт DER со случайным nonce и не нормализует s, а проверка принимает s из обеих половин диапазона. Отличия включаются явно, через имена алгоритмов. SHA256withPLAIN-ECDSA выдаёт 64-байтовую подпись raw r‖s, которую ожидает JWS; SHA256withECDDSA подписывает детерминированно, а легковесный ECDSASigner с HMacDSAKCalculator в точности воспроизвёл тестовый вектор RFC 6979. Как и в SunEC, подпись raw, переданная алгоритму для DER, приводит к исключению SignatureException, а не к возврату false.

Большинство приходит сюда ради поддержки кривых: на кривых от P-224 до P-521, secp256k1 и Brainpool r1, а также с Ed25519 и Ed448 подпись и проверка прошли успешно. Зарегистрируйте провайдер и укажите его по имени (KeyPairGenerator.getInstance("EC", "BC")): иначе первым будет опрошен SunEC из JDK, который отклоняет secp256k1, а пример именно так этого и избегает.

Поддержка постквантовых алгоритмов в версии 1.86 — самая широкая среди проверенных библиотек: ML-KEM через javax.crypto.KEM, ML-DSA, SLH-DSA и LMS в провайдере BC, а также Falcon и XMSS в BCPQC. Попробуйте алгоритмы в песочнице постквантовых подписей, а гибридные сертификаты разберите инспектором постквантовых сертификатов.

JcaPEMWriter записывает закрытые ключи EC в формате SEC1 -----BEGIN EC PRIVATE KEY-----, а PEMParser читает их обратно как PEMKeyPair; для инструментов, которые ожидают PKCS#8, ключ сначала нужно преобразовать. Если перепутаны подписи PLAIN и DER, поможет конвертер DER ⇄ raw.

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

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/bouncy-castle-java/run.sh запустила Bouncy Castle (Java) 1.86 в образе контейнера maven:3.9.16-eclipse-temurin-25; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

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

    der

    SHA256withECDSA возвращает DER; SHA256withPLAIN-ECDSA возвращает 64 байта в формате raw r‖s.

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

    rfc6979-opt-in

    По умолчанию случайный; SHA256withECDDSA дал одинаковые подписи для одних и тех же входных данных, а ECDSASigner с HMacDSAKCalculator воспроизвёл вектор из приложения A.2.5 RFC 6979.

    Что записала проверка
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: SHA256withECDSA repeats: false; ECDSASigner with HMacDSAKCalculator reproduces RFC 6979 A.2.5: true; SHA256withECDDSA repeats: true
  • 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 подписывает дайджест, и такая подпись прошла проверку по сообщению.

    Что записала проверка
    fact ecdsa-prehash = hashes-message-or-digest
  • Некорректная подпись ECDSA

    throws

    Подпись raw r‖s и 3-байтовый фрагмент данных, переданные в SHA256withECDSA, вызывают исключение SignatureException.

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

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-224, P-256, P-384, P-521, secp256k1 и brainpoolP256r1/P384r1/P512r1: подпись и проверка прошли успешно.

    Что записала проверка
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 и Ed448.

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

    ML-KEMML-DSASLH-DSAFalconLMSXMSS

    ML-KEM (через javax.crypto.KEM), ML-DSA, SLH-DSA и LMS в провайдере BC, Falcon и XMSS в BCPQC; в ходе проверки все они прошли полный цикл.

    Что записала проверка
    fact pqc = ML-KEM, ML-DSA, SLH-DSA, Falcon, LMS, XMSS
  • Форматы открытых ключей

    spki-pemspki-der

    Открытые ключи читаются из SPKI PEM через PEMParser и из SPKI DER.

    Что записала проверка
    fact public-key-formats = spki-pem, spki-der
  • Форматы закрытых ключей

    pkcs8-dersec1-pem

    JcaPEMWriter записывает закрытые ключи EC в формате SEC1 PEM (EC PRIVATE KEY), а PEMParser читает их как PEMKeyPair; PKCS#8 DER загружается через JCA.

    Что записала проверка
    fact private-key-formats = pkcs8-der, sec1-pem
    evidence private-key-formats: JcaPEMWriter writes an EC private key as -----BEGIN EC PRIVATE KEY-----; PEMParser returns PEMKeyPair

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

Сгенерируйте ключ secp256k1 в провайдере BC, детерминированно подпишите с SHA256withECDDSA и проверьте подпись.
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.Signature;
import java.security.spec.ECGenParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class Usage {
    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        // secp256k1 is not in the JDK's SunEC; it is in BC.
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "BC");
        generator.initialize(new ECGenParameterSpec("secp256k1"));
        KeyPair pair = generator.generateKeyPair();
        byte[] message = "order #1042 approved".getBytes(StandardCharsets.UTF_8);

        // ECDDSA = deterministic ECDSA (RFC 6979); PLAIN-ECDSA = raw r||s instead of DER.
        Signature signer = Signature.getInstance("SHA256withECDDSA", "BC");
        signer.initSign(pair.getPrivate());
        signer.update(message);
        byte[] der = signer.sign();

        Signature verifier = Signature.getInstance("SHA256withECDSA", "BC");
        verifier.initVerify(pair.getPublic());
        verifier.update(message);
        System.out.println("verified: " + verifier.verify(der) + ", DER starts with 0x" + Integer.toHexString(der[0] & 0xff));
    }
}

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

Подготовка
mvn -q -B dependency:copy -Dartifact=org.bouncycastle:bcprov-jdk18on:1.86 -DoutputDirectory=lib >/dev/null
Команда
java -cp lib/bcprov-jdk18on-1.86.jar Usage.java
Вывод должен совпадать (регулярное выражение)
verified: true, DER starts with 0x30

Библиотеки