JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3 in Java 27

  • Last Updated: September 23, 2026
  • By: javahandson
  • Series
img

JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3 in Java 27

Java 27 brings post-quantum hybrid key exchange to TLS 1.3 through JEP 527. The name sounds heavy, but the idea is quite simple. Java now mixes a trusted classic algorithm with a new quantum-resistant one when two systems agree on a secret key.

Every Java backend talks to something over HTTPS. It may call a payment gateway, a partner bank, a cloud API or another microservice. All of this traffic depends on TLS. And TLS depends on key exchange algorithms that today’s computers cannot break.

The worry is about tomorrow. A large enough quantum computer could break some of these algorithms in the future. So JEP 527 adds a second layer of protection right now, while the risk is still far away.

The good part? Most of us do not need to change a single line of code. Java 27 turns on the hybrid scheme X25519MLKEM768 by default. It also keeps it at the top of the preference list.

In this article, we will go step by step. First we will look at the problem. Then we will see how ML-KEM works, what “hybrid” really means, how to configure it and how to verify it. We will also cover the traps that can quietly switch the feature off.

1. JEP 527 at a Glance

Before going deep, here is a quick summary of the JEP. Keep this table handy when you explain the feature to your team.

ItemDetails
JEP527: Post-Quantum Hybrid Key Exchange for TLS 1.3
Released inJDK 27 (GA on 15 September 2026)
Componentsecurity-libs / javax.net.ssl
ProviderSunJSSE (the JDK’s built-in TLS implementation)
New named groupsX25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024
DefaultX25519MLKEM768 enabled and most preferred
Code change neededNo, for apps that use the default named groups
TLS versionsTLS 1.3 only

2. A Quick Refresher on the TLS 1.3 Handshake

Let’s say your Spring Boot service calls a payment API over HTTPS. Before any JSON moves, the client and the server do a TLS handshake. Think of it as a short meeting where both sides agree on the rules.

Client                                   Server
  |                                        |
  | ---- ClientHello (groups, key shares) ->|
  |                                        |
  | <--- ServerHello (chosen key share) ----|
  |                                        |
  |   Both sides now compute the SAME      |
  |   shared secret without sending it     |
  |                                        |
  | <====== Encrypted application data ===>|

2.1 Key Exchange Is the Heart of the Handshake

Inside this meeting, the most important step is key exchange. Both sides want the same secret key. However, they must never send that key over the network in plain form.

TLS 1.3 solves this with Diffie-Hellman style math. The client sends a public value. The server sends its own public value. Each side then mixes its private value with the other side’s public value. Both land on the same secret, and a listener in the middle cannot work it out.

In TLS terms, these key exchange schemes are called “named groups”. Common ones are x25519, secp256r1 and secp384r1. Most modern TLS 1.3 traffic today uses x25519.

💡 Interview Insight
In TLS 1.3, the client usually sends its key share inside the very first message (ClientHello). That is why TLS 1.3 needs only one round trip for the handshake. Interviewers love to ask how TLS 1.3 became faster than TLS 1.2, and this is the main answer.

3. Why Quantum Computers Worry Security Engineers

A normal computer works with bits. Each bit is either 0 or 1. A quantum computer works with qubits, and it can run some special algorithms that normal computers cannot run well.

3.1 The Math Behind Today’s Key Exchange

RSA depends on how hard it is to factor very large numbers. Elliptic-curve Diffie-Hellman (ECDH) depends on another hard problem called the discrete logarithm. For a classical computer, both problems would take an impossible amount of time.

In 1994, Peter Shor published a quantum algorithm that can solve both problems fast. On a big and stable quantum computer, Shor’s algorithm could break RSA and ECDH. That includes x25519, which protects most TLS traffic today.

Let me be clear here. Such a machine does not exist today. Nobody can break your HTTPS traffic with a quantum computer right now. So why is Java acting today?

3.2 Harvest Now, Decrypt Later

The answer is an attack style called “harvest now, decrypt later”. An attacker records your encrypted traffic today. They cannot read it yet, so they just store it. Storage is cheap, and they can wait for years.

Today (2026)
 
Client ===== encrypted TLS traffic =====> Server
                    |
                 Attacker copies and stores it
 
Some year in the future
 
Stored traffic --> Large quantum computer --> Breaks old key exchange --> Reads data

Now think about data that must stay secret for 10 or 20 years. Bank records, card data, health records, government files and trade secrets all fall in this group. If someone captures that traffic in 2026 and breaks it in 2036, the damage is still real.

This is exactly why the JEP says the move to quantum-resistant algorithms is urgent. The threat comes later, but the recording happens now.

💡 Interview Insight
If an interviewer asks “Why adopt post-quantum crypto when quantum computers are not ready?”, answer with harvest now, decrypt later. Key exchange protects confidentiality, so it has to move first. Signatures can wait a little, because a future attacker cannot go back in time and fake a handshake that already happened.

4. What Is ML-KEM?

Post-quantum cryptography (PQC) means algorithms that stay safe even against large quantum computers. They run on normal hardware. They just use math problems that Shor’s algorithm cannot solve.

ML-KEM stands for Module-Lattice-Based Key Encapsulation Mechanism. NIST standardised it in August 2024 as FIPS 203. You may also know its earlier name, CRYSTALS-Kyber. Its security comes from hard problems on lattices, which are grids of points in many dimensions.

4.1 KEM vs Diffie-Hellman

ML-KEM does not work exactly like Diffie-Hellman. It is a KEM, and the flow has three simple steps:

  • The receiver generates a key pair and shares the public key.
  • The sender uses that public key to create a fresh secret. It also produces a “ciphertext” (an encapsulation) that wraps this secret.
  • The receiver opens the ciphertext with its private key and gets the same secret.

In TLS, the client plays the receiver and the server plays the sender. The client sends an ML-KEM public key in ClientHello. Then the server sends back the ciphertext in ServerHello. At the end, both hold the same shared secret.

ML-KEM comes in three sizes: ML-KEM-512, ML-KEM-768 and ML-KEM-1024. A bigger number gives more security and bigger keys. JEP 527 uses ML-KEM-768 and ML-KEM-1024.

4.2 How Java Got Here: The Building Blocks

JEP 527 did not appear from nowhere. Java added the pieces over a few releases:

JDKJEPWhat it added
21JEP 452The KEM API (javax.crypto.KEM)
24JEP 496ML-KEM algorithm in the JDK
24JEP 497ML-DSA, a quantum-resistant signature algorithm
27JEP 527Hybrid ML-KEM key exchange inside TLS 1.3

Since JDK 24, you can already use ML-KEM directly with the KEM API. Here is a small example. You will not need this for TLS, but it shows what happens under the hood.

import javax.crypto.KEM;
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.NamedParameterSpec;
 
public class MlKemDemo {
    public static void main(String[] args) throws Exception {
        // Receiver (TLS client) creates an ML-KEM-768 key pair
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM");
        kpg.initialize(NamedParameterSpec.ML_KEM_768);
        KeyPair receiverKeys = kpg.generateKeyPair();
 
        KEM kem = KEM.getInstance("ML-KEM");
 
        // Sender (TLS server) encapsulates a fresh secret
        KEM.Encapsulator enc = kem.newEncapsulator(receiverKeys.getPublic());
        KEM.Encapsulated result = enc.encapsulate();
        SecretKey senderSecret = result.key();
        byte[] ciphertext = result.encapsulation();
 
        // Receiver decapsulates and gets the same secret
        KEM.Decapsulator dec = kem.newDecapsulator(receiverKeys.getPrivate());
        SecretKey receiverSecret = dec.decapsulate(ciphertext);
 
        System.out.println("Ciphertext size: " + ciphertext.length + " bytes");
        System.out.println("Secrets match: " + java.util.Arrays.equals(
                senderSecret.getEncoded(), receiverSecret.getEncoded()));
    }
}

Run it on JDK 24 or later. You should see a ciphertext of 1088 bytes and “Secrets match: true”. JEP 527 simply does this same dance inside the TLS handshake for you.

5. What Hybrid Key Exchange Really Means

This is the most important idea in JEP 527. Java does not throw away x25519 and replace it with ML-KEM. Instead, it runs both at the same time and joins the results.

Traditional ECDHE (X25519)   -->  secret A
Post-quantum KEM (ML-KEM-768) -->  secret B
 
Hybrid secret = B + A  (joined together)
        |
        v
TLS 1.3 key schedule --> session keys

5.1 Why Not Use ML-KEM Alone?

Good question. ML-KEM is new. Experts have studied x25519 for many years, and it has held up well. ML-KEM has not faced the same amount of real-world attack and analysis yet.

So the hybrid approach plays it safe from both sides:

  • If someone finds a flaw in ML-KEM, x25519 still protects you from classical attackers.
  • If a quantum computer breaks x25519 one day, ML-KEM still protects you.
  • An attacker must break both algorithms to recover the session keys.

In simple words, the connection stays secure as long as at least one of the two algorithms holds. That is a solid way to move to new crypto without betting everything on it.

5.2 How the Two Secrets Get Combined

For X25519MLKEM768, the client sends one combined key share. This share holds an ML-KEM-768 public key and an X25519 public key, placed one after the other. The server replies with an ML-KEM ciphertext plus its own X25519 public key.

Each side then computes two secrets and joins them into one 64-byte value. After that, the normal TLS 1.3 key schedule takes over. Nothing else in TLS changes. Certificates, cipher suites and record encryption all stay the same.

💡 Interview Insight
A crisp one-liner for interviews: “Hybrid key exchange is secure if either component is secure.” Then add why the industry chose hybrid first. The new PQC algorithms are young, so we keep the proven classical one as a safety net.

6. The Three Hybrid Named Groups in Java 27

JEP 527 adds three new named groups to the SunJSSE provider. Their names match the official IANA names, so they work with other TLS stacks too.

Named groupClassical partPost-quantum partEnabled by default
X25519MLKEM768X25519 (ECDHE)ML-KEM-768Yes (first choice)
SecP256r1MLKEM768secp256r1 / P-256 (ECDHE)ML-KEM-768No
SecP384r1MLKEM1024secp384r1 / P-384 (ECDHE)ML-KEM-1024No

6.1 Why X25519MLKEM768 Is the Default

X25519MLKEM768 has become the common choice across the industry. Chrome, Firefox, Cloudflare and OpenSSL 3.5 all support it. So a Java 27 client has the best chance of a hybrid match with this group.

The other two groups exist mainly for compliance. Some regulated setups must use NIST curves like P-256 or P-384. For example, SecP384r1MLKEM1024 fits teams that want the highest security level on both sides.

7. How the Handshake Works With Java 27

Now let’s walk through a real handshake. We will take two cases, one with a modern server and one with an older server.

7.1 Case 1: Java 27 Client and a Modern Server

By default, a Java 27 client sends two key shares in ClientHello. One is the hybrid X25519MLKEM768 share, and the other is a plain x25519 share. It also lists all the groups it supports, in order of preference.

Java 27 Client
  supported_groups: X25519MLKEM768, x25519, secp256r1, ...
  key_share:        X25519MLKEM768, x25519
 
Modern Server (supports hybrid)
  picks: X25519MLKEM768
 
Result: hybrid post-quantum key exchange, one round trip

7.2 Case 2: Java 27 Client and an Older Server

An older server does not know X25519MLKEM768. It simply ignores that share and picks the plain x25519 share instead. The handshake still finishes in one round trip, just without post-quantum protection.

That is why the client sends both shares. If it sent only the hybrid share, older servers would reply with a HelloRetryRequest. That costs one extra round trip on every new connection.

7.3 The Default Named Groups List

Here is the full default order in JDK 27, straight from the JEP:

X25519MLKEM768, x25519, secp256r1, secp384r1, secp521r1,
x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192

Notice that only one hybrid group sits in the default list. If you need SecP256r1MLKEM768 or SecP384r1MLKEM1024, you must enable it yourself. We will see how in a moment.

💡 Interview Insight
In TLS 1.3, the client suggests and the server decides. The client lists groups in order of preference, but the server makes the final choice. So a Java 27 client alone cannot force a hybrid connection. Both sides need support for the same hybrid group.

8. Do You Need to Change Your Code?

For most applications, the answer is no. However, there are a few cases where you will not get the feature automatically. Let’s look at both sides.

8.1 When You Get It for Free

You get hybrid key exchange by default when all of these are true:

  • Your app runs on JDK 27 or later.
  • It uses the JDK’s TLS stack (javax.net.ssl / SunJSSE). This covers SSLSocket, SSLEngine, HttpsURLConnection and java.net.http.HttpClient.
  • The connection uses TLS 1.3.
  • Nobody has overridden the named groups in code or JVM flags.
  • The other side also supports X25519MLKEM768.

8.2 When You Do Not Get It Automatically

These are the traps I would check first in any real project:

  • Custom named groups: if a startup script sets -Djdk.tls.namedGroups=x25519,secp256r1, the hybrid group drops out. The same happens with an old setNamedGroups() call in your code.
  • TLS 1.2 connections: JEP 527 covers TLS 1.3 only. Many legacy bank and partner endpoints still speak only TLS 1.2.
  • Different TLS engine: Netty with netty-tcnative or BoringSSL, and Tomcat with the OpenSSL connector, do not use SunJSSE. Their post-quantum support depends on that native library.
  • Third-party JSSE providers: for example, Bouncy Castle JSSE has its own list of groups.
  • Older JDK: JDK 21, 24 or 25 do not have this feature in TLS.
💡 Interview Insight
A common production surprise: the team upgrades to JDK 27 but hardened JVM flags from years ago still pin jdk.tls.namedGroups. The app works fine, but it never uses hybrid key exchange. Always grep your startup scripts, Dockerfiles and Helm charts for this property.

9. Configuring Named Groups in Java 27

Most teams should stay with the defaults. Still, you may need control for compliance, testing or a partner that needs a specific group. Java gives you two ways.

9.1 Option 1: The jdk.tls.namedGroups System Property

This property sets the groups for the whole JVM. The order you give is the order of preference.

# Prefer hybrid groups, keep classic ones as fallback
java -Djdk.tls.namedGroups=X25519MLKEM768,SecP256r1MLKEM768,x25519,secp256r1 \
     -jar payment-service.jar

Be careful here. Whatever you list replaces the default list fully. If you forget a classical group, older servers may fail the handshake with you.

9.2 Option 2: SSLParameters on an SSLSocket

For finer control, set the groups on one connection with SSLParameters.setNamedGroups(). This method has existed since JDK 20, and now it also accepts the hybrid names.

import javax.net.ssl.*;
 
SSLSocket socket = (SSLSocket) SSLContext.getDefault()
        .getSocketFactory()
        .createSocket("api.partnerbank.com", 443);
 
SSLParameters params = socket.getSSLParameters();
params.setProtocols(new String[] { "TLSv1.3" });
params.setNamedGroups(new String[] {
        "SecP256r1MLKEM768",   // hybrid, NIST curve
        "X25519MLKEM768",      // hybrid, most common
        "secp256r1",           // classic fallback
        "x25519"               // classic fallback
});
socket.setSSLParameters(params);
 
socket.startHandshake();

9.3 Option 3: SSLParameters With java.net.http.HttpClient

Most modern code uses HttpClient rather than raw sockets. You can pass the same SSLParameters to its builder:

import java.net.URI;
import java.net.http.*;
import javax.net.ssl.SSLParameters;
 
SSLParameters params = new SSLParameters();
params.setProtocols(new String[] { "TLSv1.3" });
params.setNamedGroups(new String[] {
        "X25519MLKEM768", "x25519", "secp256r1"
});
 
HttpClient client = HttpClient.newBuilder()
        .sslParameters(params)
        .build();
 
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://pq.cloudflareresearch.com"))
        .build();
 
HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());

Honestly, you do not need this code just to get the default behaviour. Use it only when you really want to change the order or add the other two hybrid groups.

10. How to Verify Hybrid Key Exchange Is Working

Enabling a feature is one thing. Proving that it works in your environment is another. Here are two simple ways to check.

10.1 Turn On JSSE Debug Logs

The JDK can print handshake details. Run your app with the debug flag and filter for the ML-KEM group names:

java -Djavax.net.debug=ssl:handshake -jar payment-service.jar 2>&1 \
     | grep -i "mlkem"

Look at the ClientHello and ServerHello sections. In ClientHello, X25519MLKEM768 should appear under “supported_groups” and “key_share”. In ServerHello, the chosen key share tells you what the server picked. If it shows x25519, the server does not support hybrid yet.

Debug logs are very noisy. Use them in a test environment, not in production.

10.2 Test Against a Local OpenSSL 3.5 Server

OpenSSL 3.5 and later support ML-KEM hybrid groups. You can start a test server that accepts only the hybrid group:

openssl s_server -accept 8443 -tls1_3 \
        -cert server.pem -key server-key.pem \
        -groups X25519MLKEM768 -www

Now point a Java 27 client at https://localhost:8443. If the handshake passes, you know hybrid key exchange works end to end. Then try the same thing on JDK 25. The handshake fails, because the old JDK has no hybrid group to offer.

11. What About Spring Boot Applications?

There is no new Spring property or annotation for JEP 527. It lives fully inside the JDK. If your Spring Boot app calls APIs through a client that uses JSSE, it benefits the moment it runs on JDK 27.

Your @Service code
        |
RestClient / WebClient / RestTemplate
        |
HTTP client library (JDK HttpClient, Apache HttpClient, ...)
        |
javax.net.ssl (SunJSSE)
        |
TLS 1.3 with X25519MLKEM768
        |
TCP

The same idea applies on the server side. An embedded Tomcat, Jetty or Undertow using JSSE on JDK 27 will accept hybrid key exchange from modern clients. Still, check two things in real projects:

  • Does your HTTP client library use JSSE, or a native TLS engine?
  • Does TLS end at a load balancer or API gateway before your app? If yes, that box decides the key exchange with the outside world, not your JDK.

In many companies, TLS from the internet ends at a cloud load balancer. So JEP 527 matters most for internal service-to-service calls and outgoing calls to partners.

12. Performance and Network Considerations

Hybrid key exchange is not free. The math is fast, but the keys are much bigger. Let’s look at the numbers.

12.1 Bigger Key Shares

Named groupClient key shareServer key share
x2551932 bytes32 bytes
X25519MLKEM7681,216 bytes1,120 bytes
SecP256r1MLKEM7681,249 bytes1,153 bytes
SecP384r1MLKEM10241,665 bytes1,665 bytes

Because a Java 27 client sends both a hybrid share and an x25519 share, the ClientHello grows by more than 1 KB. It can now go beyond a single network packet of about 1,500 bytes. On the CPU side, ML-KEM is fast, often faster than the elliptic-curve part. The real cost is size, not speed.

12.2 Watch Out for Middleboxes

Some old firewalls, proxies and TLS inspection boxes expect a small ClientHello that fits in one packet. When the message splits across two packets, a few of them misbehave. They may drop the connection or time out.

Browsers faced this same issue when they rolled out hybrid key exchange in 2024. Most vendors have fixed it since then. Even so, enterprise networks often run old devices for years.

12.3 A Practical Rollout Checklist

  • Search all JVM flags and config for jdk.tls.namedGroups and setNamedGroups().
  • List every outgoing TLS partner and check if it supports TLS 1.3.
  • Test the upgrade through the real path: proxy, firewall, load balancer and TLS terminator.
  • Watch handshake failures and timeouts after rollout, especially to older partners.
  • Keep a quick rollback ready. For example, set jdk.tls.namedGroups without the hybrid group for one specific service.
💡 Interview Insight
If a partner connection breaks after the JDK 27 upgrade, do not disable TLS 1.3 in panic. First remove X25519MLKEM768 from jdk.tls.namedGroups for that one service. If the handshake works again, you have found a middlebox size problem. Raise it with the network team.

13. What JEP 527 Does Not Do

It is easy to oversell this feature. So let’s be precise about its limits.

  • Java is not fully quantum-safe after this JEP. The change protects only the key exchange step.
  • Certificates and signatures stay the same. Your server certificate is still RSA or ECDSA. A future quantum computer could fake those, but it cannot use that to decrypt old recorded traffic.
  • Only TLS 1.3 benefits. TLS 1.2 gets nothing new.
  • It covers only javax.net.ssl. Other APIs do not get hybrid key exchange from this JEP.
  • It does not add pure ML-KEM groups (ML-KEM without the classical part). The JEP says this may come in future work.

A fair summary sounds like this: Java 27 adds post-quantum protection to TLS 1.3 key exchange. It is one important step in a long migration, not the whole journey.

14. Traditional TLS vs Java 27 Hybrid TLS

Here is a quick side-by-side view to help you remember the difference:

AspectBefore (JDK 26 and older)Java 27 with JEP 527
Default first groupx25519X25519MLKEM768
Safe against classical attacksYesYes
Safe against future quantum attacksNoYes, when both sides support hybrid
Harvest now, decrypt laterExposedProtected
ClientHello sizeSmallAbout 1.2 KB larger
Code changesNot applicableNone for default setups

15. Key Takeaways

  • JEP 527 brings post-quantum hybrid key exchange to TLS 1.3 in Java 27.
  • It protects today’s traffic against harvest now, decrypt later attacks.
  • Hybrid means classical ECDHE plus ML-KEM. The connection stays safe if either one holds.
  • Java 27 adds three groups: X25519MLKEM768, SecP256r1MLKEM768 and SecP384r1MLKEM1024.
  • X25519MLKEM768 is on by default and sits first in the preference list.
  • Apps on javax.net.ssl need no code change, unless they override the named groups.
  • Expect a bigger ClientHello, and test your network path before production.

16. Conclusion

JEP 527 is a small change from the outside but a big step for the Java platform. Quantum computers that can break x25519 do not exist yet. Still, attackers can record traffic today and wait. That makes key exchange the first place where post-quantum protection matters.

Java handles this in a careful way. It does not throw away proven elliptic-curve crypto. Instead, it pairs x25519 with ML-KEM-768 and joins both secrets. As long as one of them holds, your TLS session stays safe.

For most teams, the work is simple. Move to JDK 27 and check that no old flag overrides the named groups. Then test the full network path, including proxies and load balancers. After that, the JDK negotiates hybrid key exchange on its own whenever the other side supports it.

For a Java developer, this is the best kind of security upgrade. The heavy crypto lives deep inside the JDK. Your business code stays the same, and your data stays protected for many years to come.

17. Further Reading

Leave a Comment