← Knowledge base

Java (JSSE)

Is PQC enabled? — quick check

macOS / Linux / Windows

# 1) No-dependency check — identify this machine first.
uname -a 2>/dev/null || true

# 2) Dependency check — prompt before installing anything.
if ! command -v java >/dev/null 2>&1; then
  echo 'Java/JDK was not found.'
  printf 'Install or enable Java/JDK now? [y/N] '
  read answer
  case "$answer" in
    [Yy]*) echo 'Install JDK 24+ from your package manager or vendor distribution, then rerun this snippet.' ;;
    *) echo 'Skipping Java/JDK-based check.'; exit 1 ;;
  esac
fi

# 3) Capability — offline. JDK 24+ ships native ML-KEM in SunJSSE.
java -version 2>&1

# 4) Liveness — confirm JSSE can complete a TLS 1.3 handshake. There is no
#    public JSSE API for the negotiated TLS named group on any JDK, so the
#    authoritative answer comes from local openssl below.
jshell -s - <<EOF
import javax.net.ssl.*;
var f = (SSLSocketFactory) SSLSocketFactory.getDefault();
try (var s = (SSLSocket) f.createSocket("example.com", 443)) {
  s.startHandshake();
  var sess = s.getSession();
  System.out.println("proto=" + sess.getProtocol());
  System.out.println("cipher=" + sess.getCipherSuite());
}
/exit
EOF

# 5) Authoritative named-group readout — still local, via openssl CLI.
# 1) No-dependency check — identify this machine first.
uname -a 2>/dev/null || true

# 2) Dependency check — prompt before installing anything.
if ! command -v openssl >/dev/null 2>&1; then
  echo 'OpenSSL was not found. A local PQC proof needs OpenSSL 3.5+.'
  printf 'Install OpenSSL now? [y/N] '
  read answer
  case "$answer" in
    [Yy]*)
      if command -v brew >/dev/null 2>&1; then brew install openssl@3
      elif command -v apt-get >/dev/null 2>&1; then sudo apt-get update && sudo apt-get install -y openssl
      elif command -v dnf >/dev/null 2>&1; then sudo dnf install -y openssl
      elif command -v yum >/dev/null 2>&1; then sudo yum install -y openssl
      else echo 'No supported package manager found. Install OpenSSL 3.5+ and retry.'; exit 1
      fi ;;
    *) echo 'Install OpenSSL 3.5+ and retry for a local PQC proof.'; exit 1 ;;
  esac
fi

OPENSSL=openssl
if command -v brew >/dev/null 2>&1; then
  BREW_OPENSSL="$(brew --prefix openssl@3 2>/dev/null)/bin/openssl"
  [ -x "$BREW_OPENSSL" ] && OPENSSL="$BREW_OPENSSL"
fi

$OPENSSL version
if ! $OPENSSL list -tls-groups 2>/dev/null | grep -qiE 'X25519MLKEM768|MLKEM|Kyber'; then
  echo 'This OpenSSL does not advertise ML-KEM groups. Upgrade to OpenSSL 3.5+ or load oqsprovider, then retry.'
  exit 1
fi
$OPENSSL s_client -connect example.com:443 -tls1_3 -groups X25519MLKEM768   </dev/null 2>&1 | grep -E 'Negotiated TLS1\.3 group|Server Temp Key'

Expected when PQC is ON

openjdk 24 2025-03-18
proto=TLSv1.3
cipher=TLS_AES_256_GCM_SHA384
Negotiated TLS1.3 group: X25519MLKEM768

What you'll see when PQC is OFF

openjdk 21.0.4 2024-07-16
proto=TLSv1.3
cipher=TLS_AES_256_GCM_SHA384
# (no group line — JDK 21 cannot offer ML-KEM)

To force JDK 24+ to OFFER ML-KEM as a JSSE client: java -Djdk.tls.namedGroups=x25519_mlkem768,x25519,secp256r1 … . Earlier JDKs need BouncyCastle 1.79+ or upgrade.

JEP 496 (JDK 24, March 2025) added ML-KEM as a first-class JCA algorithm. JEP 497 (JDK 24) added ML-DSA for signatures. JSSE wires ML-KEM into TLS 1.3 as the X25519MLKEM768 hybrid group.

Pin named groups

java -Djdk.tls.namedGroups=X25519MLKEM768,X25519,secp256r1 -jar app.jar

You can also set this in java.security:

jdk.tls.namedGroups=X25519MLKEM768,X25519,secp256r1

Programmatic

SSLParameters params = ctx.getDefaultSSLParameters();
params.setNamedGroups(new String[]{"X25519MLKEM768","X25519","secp256r1"});

Older JDKs

BouncyCastle setup (pre-24)

Security.insertProviderAt(new BouncyCastlePQCProvider(), 1);
Security.insertProviderAt(new BouncyCastleJsseProvider(), 2);

Spring / Tomcat / Netty

Spring Boot's embedded Tomcat reads server.ssl.* and the JVM-level jdk.tls.namedGroups. Netty 4.2 picks up named groups from the JDK automatically; for the BoringSSL backend it inherits Chrome's behavior.

References

Run the check on your Java service →