The question reaches a bank's Java team from its security office: which of our systems still exchange keys or sign with RSA or elliptic curves, and when does that have to stop?

A PQC migration for a Java system starts with that inventory and with upgrades: find the cryptography, move to a JDK that ships the new algorithms, and make the algorithm a setting. Swapping algorithms is the short part.

Harvest now, decrypt later: which data in a financial system is exposed today

Public-key algorithms such as RSA, ECDH and ECDSA rest on problems a large enough quantum computer could solve. The NSA calls such a machine a cryptanalytically relevant quantum computer, and nobody can show one today. The risk for a bank starts earlier, because traffic and backups recorded now can be decrypted once such a machine exists. That is harvest now, decrypt later.

The EU coordinated roadmap turns it into a test you can apply to each system. A use case is high-risk "if compromising confidentiality after 10 years or more would still cause significant damage", and data that has to stay confidential that long "should be protected from quantum computer attacks starting no later than by the end of 2030".

In a financial system that covers more than people expect: account and transaction history, KYC files, loan agreements, card data kept for disputes, the internal traffic between services that carries all of it. The FastPost ledger we built for Legerity has been in production since 2013, which is longer than the ten years in that test.

Signatures are a different case. A forged signature needs the quantum computer to exist at the moment of forgery, so recorded traffic is not at risk. What is at risk is a signature that has to stay verifiable for decades, such as a signed loan agreement or an audit trail.

Symmetric encryption stays. NIST's transition plan keeps AES-256 in its highest security category, so the data itself stays encrypted with AES. The migration concerns how the AES keys are exchanged, wrapped and signed.

PQC migration deadlines: NIST, the EU roadmap and the UK NCSC

NIST published the first post-quantum standards on 13 August 2024: FIPS 203 for key encapsulation (ML-KEM) and FIPS 204 for signatures (ML-DSA), with FIPS 205 as a hash-based signature alternative. The dates for retiring the old algorithms are in NIST IR 8547, still an initial public draft from November 2024: RSA and elliptic-curve algorithms at the 112-bit security level, which covers 2048-bit RSA keys, are deprecated after 2030, and all of them are disallowed after 2035.

European banks and insurers will be asked about the EU roadmap of June 2025 first. Member states should start the transition by the end of 2026, finish high-risk use cases by the end of 2030, and medium-risk ones by the end of 2035. The UK National Cyber Security Centre sets the same end date, with discovery and a plan by 2028 and the priority migrations by 2031.

Post-quantum migration deadlines from 2025 to 2035. NIST, in the draft IR 8547: RSA and elliptic-curve algorithms at the 112-bit security level deprecated after 2030, and all of them disallowed after 2035. The EU coordinated roadmap of June 2025: the transition starts by the end of 2026, high-risk use cases are migrated by the end of 2030 and medium-risk use cases by the end of 2035. The UK NCSC: discovery and a plan by 2028, priority migrations by 2031, and all systems migrated by 2035.20252026202720282029203020312032203320342035NISTIR 8547 draft112-bit RSA, ECCdeprecatedRSA, ECCdisallowedEU roadmapJune 2025TransitionstartsHigh-riskuse cases doneMedium-riskuse cases doneUK NCSCMarch 2025Discoveryand planPrioritymigrationsAll systemsmigrated
The three timelines a European financial institution is most likely to be asked about. NIST's dates are from a draft, and the EU and UK dates are guidance to member states and organizations.

The NSA's CNSA 2.0, announced in September 2022, sets the algorithms for US national security systems. A commercial bank is outside its scope, so it appears here only because suppliers to both markets quote it.

For budgeting, the date that matters is the end of 2030. Any system that keeps data confidential for ten years or more is high-risk under the EU test, and four years is not long for a bank to change how every service exchanges keys.

Building a cryptographic inventory of a Java system

A cryptographic inventory lists every place a system encrypts, signs or exchanges keys, with the algorithm, the key size, the library and the data it protects. For a Java application that means five places:

  • Code that calls the Java Cryptography Architecture directly: Cipher, Signature, KeyPairGenerator, KeyAgreement.
  • Libraries that bring their own cryptography: Bouncy Castle, JWT libraries, SAML and XML signature stacks, database drivers with their own TLS.
  • Keystores and certificates, including the ones a partner gave you years ago.
  • TLS endpoints in both directions: the ones you serve and the outbound calls to payment networks, credit bureaus and other partners.
  • Stored data whose keys are wrapped or signed with RSA or elliptic curves: field-level encryption, archived documents, signed records.

CycloneDX, the SBOM standard, defines a Cryptography Bill of Materials (CBOM) for this: "algorithms, keys, certificates, and their relationships to software components". A CBOM is worth keeping for the same reason as an SBOM. The inventory goes out of date with every release, and a machine-readable one can be regenerated in the build.

Commands for a first pass

These four commands find most of it in a Maven project. They give a first draft of the inventory: a provider configured in a properties file or an algorithm read from a database will not show up in a grep.

# 1. Where the code calls the JCA with a quantum-vulnerable algorithm
grep -rnE 'getInstance\("(RSA|EC|ECDSA|ECDH|DH|DSA|Ed25519|EdDSA|X25519|SHA(256|384|512)with(RSA|ECDSA))' \
     --include=*.java src/

# 2. Libraries that bring their own cryptography
mvn dependency:tree -Dincludes=org.bouncycastle,com.nimbusds,io.jsonwebtoken

# 3. Keys and certificates in a keystore
keytool -list -v -keystore keystore.p12 | grep -E 'Alias name|Signature algorithm|Subject Public Key Algorithm'

# 4. The key exchange each TLS connection negotiates
java -Djavax.net.debug=ssl:handshake -jar app.jar 2>&1 | grep -A1 '"server_share"'

The keystore listing shows both kinds of key side by side once you start migrating. On Java 25, a keystore holding a 2048-bit RSA key and an ML-DSA key reports Signature algorithm name: SHA384withRSA with Subject Public Key Algorithm: 2048-bit RSA key for the first, and ML-DSA-65 for the second.

What the JDK gives you: ML-KEM, ML-DSA and X25519MLKEM768

ReleaseWhat it addsWhat it means in practice
JDK 24 (March 2025)ML-KEM (JEP 496) and ML-DSA (JEP 497) in the standard providersPost-quantum key encapsulation and signatures with no extra library; not used by TLS
Java 25 (LTS, September 2025)The same algorithmsThe first long-term support release that has them
JDK 27 (15 September 2026)Hybrid key exchange for TLS 1.3 (JEP 527): X25519MLKEM768, enabled by default and first in the listEvery javax.net.ssl connection to a server that supports it becomes quantum-safe for key exchange, with no code change
Java 25.0.5 (planned for 20 October 2026)JEP 527 backportedHybrid TLS on the current LTS release
Oracle JDK 21.0.14 and 17.0.22 (planned for 19 January 2027)JEP 527 backported to Oracle's buildsHybrid TLS for teams on Oracle's paid updates for 17 or 21

X25519MLKEM768 is a hybrid: it combines classical X25519 with ML-KEM-768, so the connection stays as secure as today's even if a flaw turns up in the newer algorithm. We checked the difference on 11 October 2026 with a plain SSLSocket to www.cloudflare.com. On JDK 27 the handshake negotiated X25519MLKEM768; on Temurin 25.0.4, which does not have the backport yet, it negotiated classical x25519.

The JDK only matters where Java terminates TLS. If a load balancer, an API gateway or a service mesh sidecar terminates it in front of your application, that component needs the upgrade, and the inventory has to say which one it is.

Systems that cannot leave Java 17 or 21 yet, and do not run Oracle's builds, can use Bouncy Castle (release 1.86 on Maven Central) for ML-KEM and ML-DSA in application code. Use the FIPS names and implementations: older libraries and many examples still say Kyber and Dilithium, the names from the competition, which may refer to versions before the final standards.

Crypto agility in Spring Boot: the four InfoQ patterns

Pankaj Sharma's InfoQ article (August 2026) describes four places a Spring Boot application in banking can add post-quantum cryptography in application code:

  1. Request bodies between internal services encrypted with an ML-KEM-derived AES-256-GCM key, as a second layer under TLS.
  2. PII and KYC fields encrypted before they are written through JPA.
  3. Loan agreements and audit trails signed at the moment they are created.
  4. Service JWTs signed with a post-quantum algorithm in place of RS256.

The article is also blunt about what comes first: "Get AWS KMS or HashiCorp Vault in place for Kyber key management. Nothing else is production-safe without this change." A post-quantum private key in a properties file is no safer than an RSA key in one.

The code below uses the JDK's own providers on Java 25 and runs as shown. The key encapsulation produces an AES key; the signature algorithm is read from configuration, which is what crypto agility means in practice: changing algorithms takes a configuration change and a deployment.

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.util.Arrays;
import javax.crypto.KEM;
import javax.crypto.SecretKey;

public class PqcDemo {

    public static void main(String[] args) throws Exception {
        // Key encapsulation (FIPS 203). The receiver publishes an ML-KEM public key.
        KeyPair receiver = KeyPairGenerator.getInstance("ML-KEM").generateKeyPair();   // ML-KEM-768

        KEM.Encapsulated sent = KEM.getInstance("ML-KEM")
                .newEncapsulator(receiver.getPublic())
                .encapsulate();
        SecretKey senderKey = sent.key();             // a 256-bit secret, used as an AES-GCM key
        byte[] encapsulation = sent.encapsulation();  // sent to the receiver

        SecretKey receiverKey = KEM.getInstance("ML-KEM")
                .newDecapsulator(receiver.getPrivate())
                .decapsulate(encapsulation);
        System.out.printf("ML-KEM: encapsulation %d bytes, keys match: %b%n", encapsulation.length,
                Arrays.equals(senderKey.getEncoded(), receiverKey.getEncoded()));

        // Signatures (FIPS 204). The algorithm comes from configuration, not from the code.
        String keyAlg = System.getProperty("signing.key-algorithm", "ML-DSA");
        String sigAlg = System.getProperty("signing.signature-algorithm", "ML-DSA");
        KeyPair signer = KeyPairGenerator.getInstance(keyAlg).generateKeyPair();
        byte[] document = "Loan agreement 2026-0412".getBytes();

        Signature sign = Signature.getInstance(sigAlg);
        sign.initSign(signer.getPrivate());
        sign.update(document);
        byte[] signature = sign.sign();

        Signature verify = Signature.getInstance(sigAlg);
        verify.initVerify(signer.getPublic());
        verify.update(document);
        System.out.printf("%s: signature %d bytes, valid: %b%n", sigAlg, signature.length, verify.verify(signature));
    }
}
$ java PqcDemo
ML-KEM: encapsulation 1088 bytes, keys match: true
ML-DSA: signature 3309 bytes, valid: true

$ java -Dsigning.key-algorithm=RSA -Dsigning.signature-algorithm=SHA256withRSA PqcDemo
ML-KEM: encapsulation 1088 bytes, keys match: true
SHA256withRSA: signature 384 bytes, valid: true

The sizes are the part that breaks things. An ML-DSA signature is 3,309 bytes against 384 for the RSA signature from the same program, and a JWT carries it in Base64. Check every header size limit between the token issuer and the service that verifies it, and every database column sized for an RSA signature, before the fourth pattern goes live.

Agility also needs a record of which algorithm produced each stored ciphertext or signature. Without one, the next change of algorithm means guessing what was used for data written ten years earlier.

PQC migration checklist: the order of work

This is the order we would follow for a Java platform in banking or insurance. Each step depends on the one before it.

  1. Build the cryptographic inventory and generate a CBOM in the build.
  2. Classify each use case by how long its data must stay confidential, using the EU's ten-year test.
  3. Upgrade to a JDK that has ML-KEM and ML-DSA: Java 25 now, 25.0.5 or later for hybrid TLS.
  4. Turn on hybrid TLS wherever Java terminates connections, and list the load balancers and gateways that need their own upgrade.
  5. Move keys into a KMS or an HSM before introducing any new key type.
  6. Add an algorithm identifier to every stored ciphertext and signature, and read algorithm names from configuration.
  7. Re-encrypt long-lived data so its AES keys are protected with ML-KEM, starting with the high-risk use cases.
  8. Add ML-DSA signatures next to the existing ones on documents that must stay verifiable for decades.
  9. Change tokens last, once every service that verifies them accepts the new algorithm and the larger size.
  10. Load-test each step: larger handshakes, keys and signatures change latency and storage.

How we would run a PQC migration on your platform

We treat a PQC migration like any other upgrade of a long-lived Java system: measured first, then done in tested steps. It starts with a paid baseline review that produces the inventory, the risk class of each use case and a remediation plan, and the upgrade phases are scoped and priced separately. For the general hardening that should come with it, see our guide to securing Java applications.

If your security office has started asking which systems still depend on RSA, our Java vulnerability remediation service begins with that baseline review, scoped on a thirty-minute call.