Trying Out Post-Quantum Key Exchange with the Now Generally Available Amazon Corretto 27
This page has been translated by machine translation. View original
Introduction
Amazon Corretto 27 was released on September 17, 2026.
OpenJDK 27 reached GA on September 15, 2026, so this release came two days later.
In this article, I used JDK debug logs on local Docker to confirm that Corretto 27 selects post-quantum hybrid key exchange in TLS 1.3.
Main Features of Corretto 27
The announcement lists the following as the main features.
- Making G1 the default GC in all environments (JEP 523)
- Post-quantum hybrid key exchange for TLS 1.3 (JEP 527)
- Compact object headers enabled by default (JEP 534)
- In-process data masking for JFR (JEP 536)
- Continued previews of extended pattern matching, structured concurrency, and lazy constants (JEP 532, JEP 533, JEP 531)
- Continued incubation of the Vector API (JEP 537)
Test Environment
The host used for testing has the aarch64 architecture. Both the JDK and OpenSSL are contained within the containers, so nothing needs to be installed on the host. The measurements this time were performed on an aarch64 host.
The client is an image in which the latest version of Corretto 27 is downloaded and extracted on top of debian:bookworm-slim. The version as of the retrieval date (September 17, 2026) was as follows.
openjdk version "27" 2026-09-15
OpenJDK Runtime Environment Corretto-27.0.0.35.1 (build 27+35-FR)
OpenJDK 64-Bit Server VM Corretto-27.0.0.35.1 (build 27+35-FR, mixed mode, sharing)
The server is OpenSSL 3.5.7 on debian:trixie-slim. The official OpenSSL 3.5.0 changelog states that TLS hybrid key exchange schemes, including X25519MLKEM768, were added.
Verification Procedure
Place compose.yml, Dockerfile.server, Dockerfile.client, and PqTlsCheck.java in the same directory and run them (they are listed below in this order).
Overall Configuration (compose.yml)
services:
tls-server:
build:
context: .
dockerfile: Dockerfile.server
container_name: pq-tls-server
networks:
- pq-net
logging:
driver: "json-file"
tls-client:
build:
context: .
dockerfile: Dockerfile.client
container_name: pq-tls-client
depends_on:
- tls-server
networks:
- pq-net
# Run after waiting for the server to start, with JDK handshake debugging
entrypoint:
- sh
- -c
- |
sleep 2
# A setting recommended to keep enabled in production as well
exec java \
-Djavax.net.debug=ssl:handshake \
-Dcom.sun.jndi.ldap.object.trustURLCodebase=false \
-cp /app \
PqTlsCheck tls-server 4433
logging:
driver: "json-file"
networks:
pq-net:
driver: bridge
Starting the OpenSSL Server
On the server side, a self-signed certificate is generated when the image is built, and openssl s_server is started with TLS 1.3 only and with -trace.
Dockerfile.server
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /certs
# Generate a self-signed server certificate (no CA; a single certificate is enough)
RUN openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
-keyout server.key -out server.crt -days 1 -nodes \
-subj "/CN=tls-server" \
-addext "subjectAltName=DNS:tls-server,IP:127.0.0.1"
EXPOSE 4433
# -trace: output handshake details (including named group) to stderr
CMD ["openssl", "s_server", \
"-key", "/certs/server.key", \
"-cert", "/certs/server.crt", \
"-accept", "4433", \
"-tls1_3", \
"-trace", \
"-www"]
Running the Corretto 27 Client
The client is an image that downloads Corretto 27 and runs javac at build time.
Dockerfile.client
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Corretto 27 - automatic architecture detection
ARG TARGETARCH
RUN case "${TARGETARCH}" in \
amd64) JDK_URL="https://corretto.aws/downloads/latest/amazon-corretto-27-x64-linux-jdk.tar.gz" ;; \
arm64) JDK_URL="https://corretto.aws/downloads/latest/amazon-corretto-27-aarch64-linux-jdk.tar.gz" ;; \
*) echo "Unsupported arch: ${TARGETARCH}" && exit 1 ;; \
esac \
&& curl -fsSL "${JDK_URL}" | tar -xz -C /opt \
&& ln -s /opt/amazon-corretto-27* /opt/java
ENV JAVA_HOME=/opt/java
ENV PATH="${JAVA_HOME}/bin:${PATH}"
WORKDIR /app
COPY PqTlsCheck.java .
RUN javac PqTlsCheck.java
# Output the full handshake log with -Djavax.net.debug=ssl:handshake
# Server certificate verification is disabled (because it is self-signed)
CMD ["java", \
"-Djavax.net.debug=ssl:handshake", \
"-Dcom.sun.jndi.ldap.object.trustURLCodebase=false", \
"PqTlsCheck", "tls-server", "4433"]
The client application is a short program that establishes a TLS 1.3 connection with SSLSocket and prints the session information.
PqTlsCheck.java
This is an implementation for verification purposes, intended for a self-signed certificate. Certificate verification is disabled, so do not use it in production.
import javax.net.ssl.*;
import java.io.*;
import java.security.cert.X509Certificate;
/**
* Corretto 27 の PQ TLS ハンドシェイク確認ツール。
* 接続先サーバーと TLS 1.3 ネゴシエーションを行い、
* 選択された cipher suite / named group をログに出力する。
*
* 証明書検証を無効にした TrustManager を使用 (自己署名証明書対応)。
* 本番環境ではこの TrustManager を使わず、JDK 既定の TrustManager(証明書検証あり)とホスト名検証を有効にすること。
*/
public class PqTlsCheck {
public static void main(String[] args) throws Exception {
String host = args.length > 0 ? args[0] : "localhost";
int port = args.length > 1 ? Integer.parseInt(args[1]) : 4433;
System.out.println("=== Corretto 27 PQ TLS Check ===");
System.out.println("Target: " + host + ":" + port);
System.out.println("Java version: " + System.getProperty("java.version"));
System.out.println("Java vendor: " + System.getProperty("java.vendor"));
System.out.println();
// 自己署名証明書を許容する TrustManager(検証用のみ)
TrustManager[] trustAll = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] c, String a) {}
public void checkServerTrusted(X509Certificate[] c, String a) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAll, null);
SSLSocketFactory factory = ctx.getSocketFactory();
try (SSLSocket socket = (SSLSocket) factory.createSocket(host, port)) {
socket.setEnabledProtocols(new String[]{"TLSv1.3"});
// 明示設定していないため null が返る(JDK デフォルトが適用される)
SSLParameters params = socket.getSSLParameters();
String[] namedGroups = params.getNamedGroups();
System.out.println("--- Named Groups (client preference order) ---");
if (namedGroups != null) {
for (int i = 0; i < namedGroups.length; i++) {
System.out.printf(" [%d] %s%n", i, namedGroups[i]);
}
} else {
System.out.println(" (not set explicitly — JDK default applies)");
}
System.out.println();
// ハンドシェイク実行(ssl:handshake デバッグログは JVM が出力)
socket.startHandshake();
SSLSession session = socket.getSession();
System.out.println("--- TLS Session ---");
System.out.println("Protocol: " + session.getProtocol());
System.out.println("CipherSuite: " + session.getCipherSuite());
System.out.println("PeerHost: " + session.getPeerHost());
System.out.println();
System.out.println("SUCCESS: TLS handshake completed.");
}
}
}
Start the server, and then run the client.
docker compose up -d tls-server && docker compose run --rm tls-client
When you are done, also delete the locally built images.
docker compose down --rmi local
Checking the Handshake
Because -Djavax.net.debug=ssl:handshake is specified, the contents of the ClientHello and ServerHello are output to the client's standard error. First, let's check the supported_groups extension of the ClientHello.
"supported_groups (10)": {
"named groups": [X25519MLKEM768, x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096]
},
Since PqTlsCheck.java does not configure named groups at all, this is the default order of preference in Corretto 27. We were able to confirm, without changing any code, the behavior described in JEP 527 of placing X25519MLKEM768 first.
Next is the key_share of the ServerHello.
"key_share (51)": {
"server_share": {
"named group": X25519MLKEM768
...
The OpenSSL 3.5.7 server selected X25519MLKEM768, and the handshake was established with hybrid key exchange. The JDK debug log records the completion of negotiation with TLS 1.3.
Negotiated protocol version: TLSv1.3
The application's standard output is as follows.
--- TLS Session ---
Protocol: TLSv1.3
CipherSuite: TLS_AES_256_GCM_SHA384
PeerHost: tls-server
SUCCESS: TLS handshake completed.
Summary
To coincide with the general availability of Amazon Corretto 27, I confirmed in a local Docker environment that the TLS 1.3 post-quantum hybrid key exchange X25519MLKEM768 is selected.
Major AWS services such as Amazon S3 and Elastic Load Balancing (ALB/NLB) have supported PQC since 2025.
In addition, Aurora MySQL 8.4.8, released in September 2026, also supports PQC.
It has been pointed out that, as quantum computers improve in performance, there is a risk that widely used public-key cryptography such as RSA and elliptic curve cryptography (ECC) could be broken by the mid-2030s (NISC interim summary, November 20, 2025).
As preparation for the so-called "2035 problem," please try evaluating Corretto 27, a PQC-capable JDK.
However, Corretto 27 is a Feature Release, and support lasts only until April 2027. Please use it on the assumption that you will move to future updates or an LTS version.
