← Back to Blog

Java BouncyCastle: Post-Quantum Algorithm Coverage

Java BouncyCastle: Post-Quantum Algorithm Coverage - QNSQY post-quantum encryption guide

BouncyCastle is the cryptography library that fills in everything the Java standard library leaves out. The Java Cryptography Architecture (JCA) ships with a fairly conservative algorithm set: AES, RSA, DSA, ECDSA, EC key agreement, SHA-2, and a few extras. BouncyCastle is the third-party Java provider that has shipped, on time and ahead of the JDK, every cryptographic algorithm that Java developers actually need. Hash trees? Yes. ChaCha20? Yes. ARIA, Camellia, GOST, Kuznyechik? Yes. Post-quantum algorithms? Yes, and broadly so.

The Legion of the Bouncy Castle, the volunteer organization at bouncycastle.org, has a reputation for adopting algorithms before they appear in the JDK. Post-quantum cryptography is no exception. BouncyCastle has post-quantum support that exceeds anything yet shipped in the OpenJDK standard library.

This article walks through what BouncyCastle covers for post-quantum, how to use it from Java code, and how the BouncyCastle approach compares to the in-progress JEP-based work for OpenJDK.

What BouncyCastle Is

BouncyCastle Java is two things:

  • A lightweight cryptography API that lives in org.bouncycastle.crypto.* packages
  • A JCA Provider that hooks into Java's standard cryptography framework

Most applications use the JCA Provider path. They call standard Java APIs like KeyPairGenerator.getInstance("ML-KEM-768", "BC") and BouncyCastle handles the rest. This means existing Java code can adopt post-quantum algorithms with minimal restructuring, just by adding the BouncyCastle provider to the JVM.

BouncyCastle also ships a TLS implementation called BCTLS (org.bouncycastle.tls.*) that some applications use as a TLS stack, separate from JSSE. BCTLS gets post-quantum support faster than JSSE because it ships with each BouncyCastle release.

For broader context on Java cryptography, see openssl PQC status 2026.

Where BouncyCastle Stands on Post-Quantum

BouncyCastle's post-quantum coverage is more comprehensive than any other major Java library. Here is the snapshot.

AlgorithmStatus in BouncyCastle
ML-KEM (FIPS 203)All parameter sets: 512, 768, 1024
ML-DSA (FIPS 204)All parameter sets: 44, 65, 87
SLH-DSA (FIPS 205)All parameter sets: 128s, 128f, 192s, 192f, 256s, 256f
FN-DSA / FalconFalcon-512 and Falcon-1024
LMS / HSSYes, per NIST SP 800-208
XMSS / XMSS^MTYes
HQCImplementation present
BIKEImplementation present
FrodoKEMImplementation present
PicnicImplementation present (legacy)
NTRU and NTRU PrimeImplementation present (legacy)

This breadth is partly historical. BouncyCastle implemented many algorithms during the NIST PQC competition rounds. After NIST selected its standards, BouncyCastle kept the older algorithms available for users who needed them while adding the standardized variants. The result is a library that works across the entire NIST PQC menu plus stateful hash-based signatures.

How to Use ML-KEM in Java

A typical Java code path with BouncyCastle:

  1. Register the BouncyCastle provider with Security.addProvider(new BouncyCastlePQCProvider())
  2. Generate keys via KeyPairGenerator.getInstance("ML-KEM-768", "BCPQC")
  3. Use the org.bouncycastle.pqc.jcajce.spec.MLKEMParameterSpec to configure parameters
  4. Encapsulate with KeyGenerator.getInstance("ML-KEM-768") for receiver-side wrapping
  5. Decapsulate with Cipher or KeyAgreement APIs depending on use case

The exact API surface has evolved over BouncyCastle releases. The 1.78 release line consolidated the post-quantum APIs around the NIST-standard algorithms. The 1.80 line continues the consolidation with API stability promises for ML-KEM, ML-DSA, and SLH-DSA.

For background on the algorithms themselves, see ML-KEM explained.

How to Use ML-DSA in Java

ML-DSA is similar:

  1. Generate keys via KeyPairGenerator.getInstance("ML-DSA-65", "BCPQC")
  2. Sign via Signature.getInstance("ML-DSA-65", "BCPQC") with initSign(privateKey) and update(data)
  3. Verify via the same Signature class with initVerify(publicKey)

Because BouncyCastle plugs into JCA, these Signature objects can be passed to existing Java code that expects a java.security.Signature. Existing code paths for digital signing (X.509 certificates, JWT signatures, code signing) work with ML-DSA without rewriting.

The JCA provider system also lets you use ML-DSA from Java's KeyStore APIs, including PKCS#12 keystores. This means existing keystore-based deployments can rotate from RSA or ECDSA keys to ML-DSA keys with the same Java code that has always handled keystore operations.

TLS Through BCTLS

BouncyCastle ships its own TLS stack, BCTLS. BCTLS supports post-quantum TLS groups including:

  • X25519MLKEM768 hybrid (matching the IETF draft used by Chrome, Firefox, OpenSSL)
  • ML-KEM-only groups in experimental forms
  • Hybrid signature schemes for TLS 1.3 client authentication

Because BCTLS ships with BouncyCastle releases, it gets PQC algorithms before JSSE. Applications that need post-quantum TLS in Java today and cannot wait for OpenJDK's JSSE updates often switch to BCTLS.

The cost of BCTLS is that you give up some integration with the standard JSSE world. Most Java HTTP clients (Apache HttpClient, OkHttp) integrate with JSSE; using BCTLS requires adapter code. For server-side use cases like custom servers or microservices, BCTLS works well.

For comparison with native Java TLS, see the openssl PQC reference noted above.

Stateful Hash-Based Signatures: BouncyCastle Strengths

BouncyCastle has long supported LMS, HSS, XMSS, and XMSS^MT. These are stateful hash-based signature schemes covered by NIST SP 800-208. They are not part of FIPS 204 or 205, but they are approved for specific use cases like firmware signing.

The BouncyCastle implementations have been used in firmware signing pipelines for years. The state-management problem (each LMS signature consumes a one-time key, so signing twice from the same state breaks security) is solved by integrating BouncyCastle with HSMs that handle state durably. AWS CloudHSM and YubiHSM both provide LMS support that pairs with BouncyCastle clients.

For algorithm comparison, see ML-DSA vs SLH-DSA.

Comparing BouncyCastle to OpenJDK

OpenJDK has a JEP (JDK Enhancement Proposal) process for adding post-quantum cryptography to the standard library. Several JEPs have been proposed and accepted. The pacing is slower than BouncyCastle because:

  • OpenJDK changes go through extensive review across multiple stewards
  • API stability commitments are stronger
  • FIPS validation pathways for OpenJDK builds (Adoptium, Red Hat, Azul) require specific module structuring
  • Backwards compatibility constraints are tighter

The result is that OpenJDK ML-KEM and ML-DSA support is appearing in JDK 24 and 25 preview features, with stabilization in JDK 26 and beyond. BouncyCastle has had production-ready support for these algorithms for longer.

In practice, many Java teams use BouncyCastle today and plan to migrate to JDK-native APIs once those stabilize. The migration is straightforward because BouncyCastle's JCA provider exposes the same Java standard APIs the OpenJDK eventually exposes.

Performance and FIPS

BouncyCastle has separate FIPS-validated and non-FIPS distributions. The FIPS distribution (BCFIPS) goes through CMVP validation periodically. FIPS-validated builds with post-quantum algorithms are progressing through CMVP through 2026. The non-FIPS general distribution gets new algorithms first.

Performance is generally adequate for non-hot-path use. Java performance for cryptography depends heavily on the JIT and on whether native acceleration is available. BouncyCastle's PQC implementations are pure Java, so they do not benefit from CPU-specific assembly the way C libraries do. For most application workloads (signing JWTs, validating certificates, encrypting files), the JVM-level performance is fine. For TLS termination at extreme scale, BCTLS may not match a tuned C library.

For broader migration considerations, see AWS KMS quantum migration.

What's Not Covered

BouncyCastle is comprehensive, but a few items are still not present:

  • Hybrid certificates carrying classical and PQ signatures: Awaiting IETF stabilization
  • Specific HSM integration paths: PKCS#11 PQC mechanism numbers are still settling at OASIS
  • Quantum-safe TLS 1.2 cipher suites: TLS 1.3 is the focus

BouncyCastle's API Surface for PQC

The BouncyCastle PQC API surface mirrors the JCA surface developers already know. For ML-KEM:

  • KeyPairGenerator with parameter sets like ML-KEM-512, ML-KEM-768, ML-KEM-1024
  • Cipher for encapsulation operations, when using the KEM as a key transport mechanism
  • KeyAgreement for combining KEM-derived material with other key agreement output
  • KeyFactory for converting between encoded and structured key representations

For ML-DSA:

  • KeyPairGenerator for ML-DSA-44, ML-DSA-65, ML-DSA-87
  • Signature for sign and verify, integrated with java.security.Signature
  • KeyFactory and CertificateFactory for parsing X.509 certificates with ML-DSA keys

For SLH-DSA:

  • Similar pattern with parameter sets like SLH-DSA-128s, 128f, 192s, 192f, 256s, 256f
  • Larger signature sizes are exposed; applications should account for storage

These APIs work uniformly through Java's standard cryptography framework. Existing code that accepts a Signature or KeyPairGenerator instance will accept BouncyCastle PQC instances without modification.

ASN.1 and Certificate Encoding

X.509 certificates carrying ML-DSA public keys use specific ASN.1 OIDs:

  • 2.16.840.1.101.3.4.3.17 for ML-DSA-44 signatures
  • 2.16.840.1.101.3.4.3.18 for ML-DSA-65 signatures
  • 2.16.840.1.101.3.4.3.19 for ML-DSA-87 signatures

These OIDs are NIST-assigned and used in the FIPS 204 specification context. BouncyCastle's certificate parsing recognizes these OIDs and extracts the algorithm parameters correctly.

For ML-KEM, similar OID assignments exist for use in PKCS#8 private-key encoding and SubjectPublicKeyInfo public-key encoding. BouncyCastle's PKCS#8 import/export routines handle these.

Code Signing With ML-DSA in Java

A practical use case for BouncyCastle PQC is signing JAR files and other artifacts with ML-DSA. The flow:

  1. Generate an ML-DSA-65 keypair using BouncyCastle
  2. Wrap the public key in an X.509 certificate signed by a trusted authority (which itself uses ML-DSA or hybrid signatures)
  3. Sign the JAR using jarsigner or programmatic signing with ML-DSA as the algorithm
  4. Verify on the consumer side using the same certificate path

This pattern is being adopted in enterprise Java environments where firmware and code signing pipelines need post-quantum protection. The state-management problem of LMS (each signature consumes one-time material) does not apply to ML-DSA, making it more practical for general code signing.

For broader signing context, see hybrid encryption.

Best Practices for Java Developers

For Java applications adopting post-quantum cryptography:

  1. Add the BouncyCastle provider to your JVM and update your dependency to current BouncyCastle (1.78 series or later)
  2. Use Java standard APIs (KeyPairGenerator, Signature, Cipher) and pass the provider name "BCPQC" or "BC"
  3. Test interoperability with peer implementations (OpenSSL, Go, Rust) by validating wire formats
  4. For TLS, evaluate whether BCTLS or JSSE meets your needs; BCTLS for cutting-edge PQC, JSSE for ecosystem fit
  5. Plan for rotation: using JCA APIs makes algorithm migration mostly mechanical

For library authors, expose BouncyCastle as one possible provider and let users plug in alternatives.

Frequently Asked Questions

Is BouncyCastle production-ready for post-quantum?

Yes for non-FIPS deployments. BouncyCastle's ML-KEM, ML-DSA, and SLH-DSA implementations are mature and have been deployed by various organizations. For FIPS-validated production, the BCFIPS distribution is the answer, with PQC validation progressing.

Can I use BouncyCastle and JCA together?

Yes. BouncyCastle is a JCA provider. You register it once at JVM startup, then use standard Java cryptography APIs.

Should I use BCTLS or JSSE for TLS?

BCTLS gives you the latest PQC algorithms today. JSSE has broader Java ecosystem integration. For new Java services that need PQC, evaluate both. For existing services, BCTLS may require adapter code.

Is BouncyCastle slow?

Pure Java cryptography is slower than tuned C with assembly, but for most application workloads BouncyCastle is fast enough. Profile your specific use case before optimizing.

Where can I find the source?

The repository is github.com/bcgit/bc-java. The post-quantum modules live in org.bouncycastle.pqc.crypto. and org.bouncycastle.pqc.jcajce..

How does BouncyCastle handle stateful hash-based signature state?

For LMS and HSS, BouncyCastle expects the integrating application to manage state. The library provides primitives for signing and verifying; the application is responsible for storing the LMS state and not reusing it. In practice, signing pipelines route LMS operations through HSMs that handle state durably.

Can BouncyCastle and JSSE coexist?

Yes. BouncyCastle is a JCA provider; JSSE is the standard Java SSL/TLS framework. They handle different layers and can coexist. BouncyCastle can also provide the JSSE provider via its BCJSSE module if you want BouncyCastle-implemented TLS.

Is BouncyCastle's ML-KEM faster or slower than the JDK native APIs?

JDK native APIs (when available) typically use HotSpot intrinsics or platform-specific libraries that benefit from optimization. BouncyCastle is pure Java and slightly slower in benchmarks. For most applications the difference is negligible.

Sources

  1. BouncyCastle repository, github.com/bcgit/bc-java
  2. BouncyCastle project, bouncycastle.org
  3. NIST FIPS 203, 204, 205 (post-quantum standards), 2024
  4. NIST SP 800-208 (Stateful Hash-Based Signatures)
  5. JDK Enhancement Proposals for PQC, openjdk.org/jeps
  6. IETF draft-kwiatkowski-tls-ecdhe-mlkem

Related Articles

Protect Your Data Before Q-Day Arrives

QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.

Try QNSQY