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.
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:
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:
In TLS 1.3 those jobs were separated:
| Slot | Question it answers | Examples |
|---|---|---|
| Cipher suite | How do we encrypt the bytes? | TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256 |
| Key exchange / group | How do we agree on a shared secret? | X25519, X25519MLKEM768, MLKEM768 |
| Signature algorithms | How 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:
You do not need a PQC certificate to demonstrate a PQC key exchange. Certificates and KEMs solve different problems.
The map: protocol → implementation → algorithms
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
| Algorithm | Job | Where it shows up |
|---|---|---|
| ML-KEM | Key encapsulation / shared secret | TLS key exchange groups |
| ML-DSA / SLH-DSA | Digital signatures | Certificates, 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:
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.
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:
- OpenSSL supports PQC - algorithms present in the library.
- This service can negotiate PQC - the app is linked to that library and groups are not configured away.
- 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.
| System | Protocol | Algorithm today | TLS stack | Version | PQC capable? | Internet-facing? | Long-lived secrets? |
|---|---|---|---|---|---|---|---|
| web01 | HTTPS | ECDHE | OpenSSL | ? | ? | yes | yes |
| app01 | TLS | ECDHE | Java JSSE | ? | ? | no | maybe |
| vpn01 | IPsec | ECDH | vendor | ? | ? | yes | yes |
| ca01 | PKI | RSA | HSM | ? | ? | no | yes |
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.