Pular para o conteúdo
ecdsa.com
Java · Kotlin

Bouncy Castle (Java)

O provedor Java e o kit de ferramentas PKIX da Legion of the Bouncy Castle: todas as curvas que o JDK removeu, ECDSA determinístico quando solicitado, leitura de PEM e o maior conjunto de assinaturas pós-quânticas da JVM.
Licença
MIT
Versão verificada
1.86 ·
Código-fonte
github.com/bcgit/bc-java
Documentação
www.bouncycastle.org/documentation/documentation-java/

Visão geral da compatibilidade

ECDSAEdDSAPós-quântica

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Algoritmos pós-quânticos
ML-KEMML-DSASLH-DSAFalconLMSXMSS
Formatos de chave privada
pkcs8-dersec1-pem

O Bouncy Castle é duas coisas ao mesmo tempo: um provedor JCA (BC) que se integra a Signature, KeyPairGenerator e KEM, e APIs leves, como ECDSASigner, que funcionam sem a JCA. O bcpkix acrescenta certificados, CMS, OCSP e o PEMParser que ainda falta no JDK. A maior parte do código da JVM que lida com secp256k1, Brainpool, PGP ou certificados depende dele.

Os padrões de ECDSA coincidem com os do JDK: SHA256withECDSA produz DER com um nonce aleatório e deixa o s sem normalizar, e a verificação aceita as duas metades. As diferenças ficam em nomes de algoritmo que é preciso pedir explicitamente. SHA256withPLAIN-ECDSA produz o r‖s bruto de 64 bytes que o JWS espera; SHA256withECDDSA assina de forma determinística, e o ECDSASigner leve com um HMacDSAKCalculator reproduziu exatamente o vetor de teste da RFC 6979. Como no SunEC, uma assinatura bruta entregue ao algoritmo DER lança SignatureException em vez de retornar false.

A cobertura de curvas é o que traz a maioria das pessoas até aqui: da P-224 à P-521, a secp256k1 e as curvas Brainpool r1 passaram na assinatura e na verificação, assim como Ed25519 e Ed448. Registre o provedor e indique-o pelo nome — KeyPairGenerator.getInstance("EC", "BC") —, porque, caso contrário, o SunEC do JDK é consultado primeiro e rejeita a secp256k1, o que o exemplo evita.

A cobertura pós-quântica na versão 1.86 é a mais ampla entre as bibliotecas testadas: ML-KEM por meio de javax.crypto.KEM, ML-DSA, SLH-DSA e LMS no provedor BC, e Falcon e XMSS no BCPQC. Experimente os algoritmos no playground de assinaturas pós-quânticas e inspecione certificados híbridos com o inspetor de certificados pós-quânticos.

JcaPEMWriter grava chaves privadas EC como SEC1 -----BEGIN EC PRIVATE KEY-----, que o PEMParser lê de volta como um PEMKeyPair; ferramentas que esperam PKCS#8 precisam que a chave seja convertida antes. O conversor DER ⇄ bruto ajuda quando uma assinatura PLAIN e uma DER são confundidas.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/bouncy-castle-java/run.sh executou Bouncy Castle (Java) 1.86 na imagem de contêiner maven:3.9.16-eclipse-temurin-25 em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Codificação da assinatura ECDSA

    der

    SHA256withECDSA retorna DER; SHA256withPLAIN-ECDSA retorna 64 bytes de r‖s bruto.

    Registrado pela verificação
    fact ecdsa-signature-encoding = der
    evidence ecdsa-signature-encoding: SHA256withPLAIN-ECDSA output is 64 bytes, parses as DER: false
  • Nonce do ECDSA

    rfc6979-opt-in

    Aleatório por padrão; SHA256withECDDSA gerou assinaturas idênticas para a mesma entrada, e ECDSASigner com HMacDSAKCalculator reproduziu o apêndice A.2.5 da RFC 6979.

    Registrado pela verificação
    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 ao assinar

    either

    O s não é normalizado: valores high-S apareceram entre 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
  • High-S ao verificar

    accepts-high-s

    Uma assinatura DER com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    SHA256withECDSA calcula o hash; NONEwithECDSA assina um digest, e essa assinatura foi verificada contra a mensagem.

    Registrado pela verificação
    fact ecdsa-prehash = hashes-message-or-digest
  • Assinatura ECDSA malformada

    throws

    Uma assinatura r‖s bruta e um blob de 3 bytes passados a SHA256withECDSA lançam SignatureException.

    Registrado pela verificação
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: raw r||s to SHA256withECDSA: throws SignatureException; 3 bytes: throws SignatureException
  • Curvas ECDSA

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

    Assinatura e verificação funcionaram com P-224, P-256, P-384, P-521, secp256k1 e brainpoolP256r1/P384r1/P512r1.

    Registrado pela verificação
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 e Ed448.

    Registrado pela verificação
    fact eddsa = Ed25519, Ed448
  • Algoritmos pós-quânticos

    ML-KEMML-DSASLH-DSAFalconLMSXMSS

    ML-KEM (por meio de javax.crypto.KEM), ML-DSA, SLH-DSA e LMS no provedor BC, Falcon e XMSS no BCPQC, todos com o ciclo completo de ida e volta funcionando no teste.

    Registrado pela verificação
    fact pqc = ML-KEM, ML-DSA, SLH-DSA, Falcon, LMS, XMSS
  • Formatos de chave pública

    spki-pemspki-der

    Chaves públicas lidas de SPKI PEM por meio de PEMParser e de SPKI DER.

    Registrado pela verificação
    fact public-key-formats = spki-pem, spki-der
  • Formatos de chave privada

    pkcs8-dersec1-pem

    JcaPEMWriter grava chaves privadas EC como SEC1 PEM (EC PRIVATE KEY), que o PEMParser lê como um PEMKeyPair; o PKCS#8 DER é carregado pela JCA.

    Registrado pela verificação
    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

Uso

Gere uma chave secp256k1 no provedor BC, assine de forma determinística com SHA256withECDDSA e verifique.
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));
    }
}

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner maven:3.9.16-eclipse-temurin-25, e a execução falha se a saída não corresponder.

Configuração
mvn -q -B dependency:copy -Dartifact=org.bouncycastle:bcprov-jdk18on:1.86 -DoutputDirectory=lib >/dev/null
Comando
java -cp lib/bcprov-jdk18on-1.86.jar Usage.java
A saída deve corresponder a (expressão regular)
verified: true, DER starts with 0x30

Bibliotecas