← Blog
7 min read

Post-Quantum Crypto for Sysadmins: What You Actually Update

PQC is not a new TLS version. It is new key-exchange and signature algorithms inside your TLS stacks. Here is the mental model, what to inventory, and how to verify OpenSSL, Nginx, Java, and friends.

securitypost-quantum-cryptographycryptographytlsopensslsystems

In September 2026, post-quantum cryptography stopped being a research headline and became an operations problem.

NIST has already standardized ML-KEM (FIPS 203), ML-DSA (FIPS 204), and SLH-DSA (FIPS 205). The interesting question is no longer only "when will a cryptographically relevant quantum computer exist?" It is: how do you migrate real systems before that happens?

I already wrote about PQC from the research side - my 2018 thesis, McEliece, and how the NIST race aged. This post is the other half: what a systems engineer actually has to update, and why "just turn on PQC" is the wrong mental model.

Why this matters now

A few forces are converging at once:

  • Harvest now, decrypt later - traffic captured today can be decrypted later if the key exchange was classical and the secret stays valuable long enough.
  • Hybrid cryptography - combine classical ECDH/X25519 with ML-KEM during the transition, so you do not bet everything on one family.
  • Crypto-agility - design systems that can change algorithms quickly when something breaks.
  • Lattice monoculture risk - ML-KEM and ML-DSA both sit on Module-LWE related problems. Relying almost entirely on lattices is a research concern; NIST also selected HQC, a code-based KEM, as additional diversity.
  • Implementation attacks - side channels against PQC implementations, and AI-assisted cryptanalysis. NIST reported that HAWK, a signature candidate under consideration, was withdrawn after a vulnerability found with AI assistance. Finalized NIST PQC standards were not affected.

And outside the standards track: classical advances against RSA still matter for urgency, European financial regulators have warned about blockchain cryptography risk, and networks like Tezos have spun up quantum-resistant testnets. The operational message is the same: migration timelines are compressing.

If you want a TFM/TFG angle, side-channel attacks against ML-KEM or crypto-agility and PQC migration still beat pure theory. They let you run experiments.

The wrong question: "which TLS version is PQC?"

There is no "TLS 1.4 PQC."

TLS is a protocol. It defines how client and server negotiate, authenticate, derive keys, and encrypt application data.

OpenSSL, BoringSSL, JSSE, Go's crypto/tls are implementations of that protocol.

X25519, ML-KEM, ECDSA, ML-DSA, AES-GCM are cryptographic mechanisms those implementations plug into different slots of the handshake.

So you can keep speaking TLS 1.3 while the algorithms underneath change:

Rendering diagram…

PQC shows up first as new key exchange groups, not as a brand-new protocol version.

TLS 1.3 is modular - and that is the whole point

In TLS 1.2, a cipher suite packed several jobs into one name:

Rendering diagram…

In TLS 1.3 those jobs were separated:

SlotQuestion it answersExamples
Cipher suiteHow do we encrypt the bytes?TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256
Key exchange / groupHow do we agree on a shared secret?X25519, X25519MLKEM768, MLKEM768
Signature algorithmsHow do we prove who the server is?ECDSA, RSA-PSS, later ML-DSA

That separation is why ML-KEM can land without inventing a cipher suite named TLS_MLKEM_... and without inventing TLS 1.4.

A perfectly valid hybrid connection looks like this:

Rendering diagram…

You do not need a PQC certificate to demonstrate a PQC key exchange. Certificates and KEMs solve different problems.

The map: protocol → implementation → algorithms

Rendering diagram…

Two OpenSSL installs can both "speak TLS 1.3" and have very different crypto capabilities. Asking "do we have TLS 1.3?" is not enough for a PQC inventory.

Thinking of PQC as "plugins" is a useful metaphor - with one caveat. You usually cannot drop ml-kem.so into a folder and be done. The TLS stack must know how to put that algorithm into supported_groups / key_share, how to encapsulate and decapsulate, and how to derive session keys through HKDF afterward.

What a sysadmin actually updates

PQC is not "install quantum crypto on the server." It is a migration of cryptographic infrastructure, component by component.

1. Inventory where crypto lives

TLS/HTTPS, VPN, SSH, PKI certificates, encrypted email, backups, appliances, load balancers, HSMs, and any app that embeds its own TLS library.

Look especially for RSA, ECDSA, ECDH, and classic Diffie-Hellman.

2. Do not confuse ML-KEM and ML-DSA

AlgorithmJobWhere it shows up
ML-KEMKey encapsulation / shared secretTLS key exchange groups
ML-DSA / SLH-DSADigital signaturesCertificates, code signing, auth

You can migrate key exchange first and leave certificate signatures classical for a while. That is normal during transition.

3. Prefer hybrid first, PQC-only later

Conceptual hybrid key agreement:

Rendering diagram…

If you configure the server PQC-only (or hybrid-only with no classical fallback), clients that only speak X25519 / P-256 will fail the handshake. That is expected policy, not a mysterious bug - but it is also why production migrations keep classical groups until client coverage is known.

Also: X25519MLKEM768 is a hybrid group (classical + PQC), not pure PQC. OpenSSL 3.5 also exposes pure ML-KEM groups (MLKEM512, MLKEM768, MLKEM1024).

4. Update each TLS stack, not "the OS crypto"

This is the part people get wrong.

Rendering diagram…

Updating /usr/bin/openssl does nothing for a Java process still on an old JDK. Updating OpenSSL behind Nginx does nothing for a Fortinet IPsec tunnel that has no PQC/hybrid IKE yet.

5. Do not forget PKI, HSMs, and clients

You can have PQC key exchange and still authenticate with an RSA/ECDSA certificate. Full migration also asks: does the CA, HSM, load balancer, middlebox TLS inspection, and every client OS/browser/agent understand the new algorithms? MTU, handshake size, CPU, and old proxies all become operational concerns because PQC structures are larger.

OpenSSL: what "updating" really means

OpenSSL 1.1.1 brought TLS 1.3. Years later, OpenSSL 3.5 added ML-KEM and hybrid TLS groups such as X25519MLKEM768, SecP256r1MLKEM768, and SecP384r1MLKEM1024. Same protocol version; more cryptographic capabilities.

So yes: for stacks that use OpenSSL, moving to a build that includes those algorithms is the fundamental step to have PQC capability.

But three states are different:

  1. OpenSSL supports PQC - algorithms present in the library.
  2. This service can negotiate PQC - the app is linked to that library and groups are not configured away.
  3. This connection used PQC - client and server actually selected a PQC/hybrid group.

Updating both ends to OpenSSL 3.5+ gets you (1), and often (2) because X25519MLKEM768 is in OpenSSL 3.5's default TLS group list. You still verify (3) with a real handshake.

And watch for config traps: if an app hardcodes classical groups, upgrading OpenSSL alone will not negotiate ML-KEM.

How to verify (commands)

Distinguish "the library knows PQC" from "this connection used PQC."

OpenSSL CLI

openssl version -a

openssl list -kem-algorithms | grep -i ML-KEM
# expect ML-KEM-512 / 768 / 1024 on OpenSSL 3.5+

openssl list -tls1_3 -tls-groups | grep -i MLKEM
# expect MLKEM* and X25519MLKEM768, SecP256r1MLKEM768, ...

Handshake probe:

openssl s_client -connect server.example.com:443 -tls1_3 -groups X25519MLKEM768 -state -msg

Seeing TLS_AES_256_GCM_SHA384 only proves the cipher suite. It does not prove the key exchange was PQC. Force the group and inspect messages (or Wireshark) for the negotiated group / key_share.

Nginx

openssl version is the CLI's OpenSSL. Nginx may be linked elsewhere:

nginx -V 2>&1
# look for built with / running with OpenSSL 3.5.x

Then probe the live listener with openssl s_client as above.

Apache

apache2 -v   # or httpd -v
apachectl -M | grep ssl
ldd /usr/sbin/apache2 | grep -E 'ssl|crypto'   # path may vary

Again: finish with a real handshake against :443.

Java / JSSE

Do not look at system OpenSSL first. Look at the JDK:

java -version
java -XshowSettings:properties -version 2>&1 | grep -E 'java.version|java.vendor'

Modern JDKs expose hybrid groups through JSSE; X25519MLKEM768 can be preferred when the peer supports it, and jdk.tls.namedGroups can control the list. A tiny program printing SSLParameters.getNamedGroups(), or -Djavax.net.debug=ssl,handshake, will show whether the JVM knows the hybrid group.

Python

python3 -c "import ssl; print(ssl.OPENSSL_VERSION); print(ssl.OPENSSL_VERSION_INFO)"

That reports the OpenSSL Python is linked against - often more useful than the system openssl binary.

Go

Go typically uses its own crypto/tls. System OpenSSL upgrades do not magically upgrade Go apps. Check go version / the binary's build info and the TLS APIs that version actually exposes.

Universal proof: packet capture

sudo tcpdump -i lo -w tls.pcap port 4433
# run classic handshake, then hybrid handshake, compare ClientHello sizes and groups

In Wireshark: tls or tcp.port == 4433. Compare classic vs hybrid ClientHello / ServerHello size and selected group.

SystemProtocolAlgorithm todayTLS stackVersionPQC capable?Internet-facing?Long-lived secrets?
web01HTTPSECDHEOpenSSL??yesyes
app01TLSECDHEJava JSSE??nomaybe
vpn01IPsecECDHvendor??yesyes
ca01PKIRSAHSM??noyes

The one-sentence takeaway

Updating OpenSSL (or the JDK, or Go, or the VPN firmware) to a version that implements ML-KEM gives you the ability to speak those words. Making sure every application uses that stack, that config does not strip the new groups, and that clients can answer.

Comments