Saltar al contenido
ecdsa.com
Java · Kotlin

Bouncy Castle (Java)

El proveedor Java y kit de herramientas PKIX de la Legion of the Bouncy Castle: todas las curvas que el JDK eliminó, ECDSA determinista a petición, análisis de PEM y el conjunto más amplio de firmas poscuánticas de la JVM.
Licencia
MIT
Versión comprobada
1.86 ·
Código fuente
github.com/bcgit/bc-java
Documentación
www.bouncycastle.org/documentation/documentation-java/

Compatibilidad de un vistazo

ECDSAEdDSACriptografía poscuántica

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Algoritmos poscuánticos
ML-KEMML-DSASLH-DSAFalconLMSXMSS
Formatos de clave privada
pkcs8-dersec1-pem

Bouncy Castle es dos cosas a la vez: un proveedor JCA (BC) que se integra en Signature, KeyPairGenerator y KEM, y API ligeras como ECDSASigner que funcionan sin la JCA. bcpkix añade certificados, CMS, OCSP y el PEMParser que el JDK sigue sin tener. La mayor parte del código de la JVM que trabaja con secp256k1, Brainpool, PGP o certificados depende de él.

Sus valores predeterminados de ECDSA coinciden con los del JDK: SHA256withECDSA produce DER con un nonce aleatorio y deja s sin normalizar, y la verificación acepta s en cualquiera de las dos mitades. Las diferencias están en nombres de algoritmo que hay que pedir expresamente. SHA256withPLAIN-ECDSA produce el r‖s en bruto de 64 bytes que espera JWS; SHA256withECDDSA firma de forma determinista, y el ECDSASigner ligero con un HMacDSAKCalculator reprodujo exactamente el vector de prueba del RFC 6979. Igual que con SunEC, una firma en bruto pasada al algoritmo DER lanza SignatureException en lugar de devolver false.

La cobertura de curvas es lo que trae aquí a la mayoría: con las curvas de P-224 a P-521, secp256k1 y las Brainpool r1 se firmó y se verificó, igual que con Ed25519 y Ed448. Registra el proveedor e indícalo por su nombre —KeyPairGenerator.getInstance("EC", "BC")—, porque, si no, se consulta primero el SunEC del JDK, que rechaza secp256k1, algo que el ejemplo evita.

La cobertura poscuántica de la 1.86 es la más amplia de las bibliotecas comprobadas: ML-KEM mediante javax.crypto.KEM, ML-DSA, SLH-DSA y LMS en el proveedor BC, y Falcon y XMSS en BCPQC. Prueba los algoritmos en la herramienta de firmas poscuánticas e inspecciona certificados híbridos con el inspector de certificados poscuánticos.

JcaPEMWriter escribe las claves privadas EC como SEC1 -----BEGIN EC PRIVATE KEY-----, que PEMParser vuelve a leer como un PEMKeyPair; las herramientas que esperan PKCS#8 necesitan que antes se convierta la clave. El conversor DER ⇄ en bruto ayuda cuando se confunden una firma PLAIN y una DER.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/bouncy-castle-java/run.sh ejecutó Bouncy Castle (Java) 1.86 en la imagen de contenedor maven:3.9.16-eclipse-temurin-25 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Codificación de la firma ECDSA

    der

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

    Registrado por la comprobación
    fact ecdsa-signature-encoding = der
    evidence ecdsa-signature-encoding: SHA256withPLAIN-ECDSA output is 64 bytes, parses as DER: false
  • Nonce de ECDSA

    rfc6979-opt-in

    Aleatorio por defecto; SHA256withECDDSA dio firmas idénticas para la misma entrada, y ECDSASigner con HMacDSAKCalculator reprodujo el apéndice A.2.5 del RFC 6979.

    Registrado por la comprobación
    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 al firmar

    either

    s no se normaliza: aparecieron valores high-S entre 64 firmas.

    Registrado por la comprobación
    fact ecdsa-low-s-sign = either
  • High-S al verificar

    accepts-high-s

    Se verificó una firma DER con s sustituido por n − s.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • De qué calcula el hash la llamada de firma

    hashes-message-or-digest

    SHA256withECDSA calcula el hash; NONEwithECDSA firma un resumen, y esa firma se verificó contra el mensaje.

    Registrado por la comprobación
    fact ecdsa-prehash = hashes-message-or-digest
  • Firma ECDSA mal formada

    throws

    Una firma r‖s en bruto y un blob de 3 bytes pasados a SHA256withECDSA lanzan SignatureException.

    Registrado por la comprobación
    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

    Con P-224, P-256, P-384, P-521, secp256k1 y brainpoolP256r1/P384r1/P512r1 se firmó y se verificó.

    Registrado por la comprobación
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 y Ed448.

    Registrado por la comprobación
    fact eddsa = Ed25519, Ed448
  • Algoritmos poscuánticos

    ML-KEMML-DSASLH-DSAFalconLMSXMSS

    ML-KEM (mediante javax.crypto.KEM), ML-DSA, SLH-DSA y LMS en el proveedor BC, y Falcon y XMSS en BCPQC; todos superaron la prueba de ida y vuelta en la comprobación.

    Registrado por la comprobación
    fact pqc = ML-KEM, ML-DSA, SLH-DSA, Falcon, LMS, XMSS
  • Formatos de clave pública

    spki-pemspki-der

    Las claves públicas se leyeron desde SPKI PEM mediante PEMParser y desde SPKI DER.

    Registrado por la comprobación
    fact public-key-formats = spki-pem, spki-der
  • Formatos de clave privada

    pkcs8-dersec1-pem

    JcaPEMWriter escribe las claves privadas EC como SEC1 PEM (EC PRIVATE KEY), que PEMParser lee como un PEMKeyPair; PKCS#8 DER se carga mediante la JCA.

    Registrado por la comprobación
    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

Genera una clave secp256k1 en el proveedor BC, firma de forma determinista con SHA256withECDDSA y verifica.
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));
    }
}

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor maven:3.9.16-eclipse-temurin-25, y la ejecución falla si la salida no coincide.

Configuración
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
La salida debe coincidir (expresión regular)
verified: true, DER starts with 0x30

Bibliotecas