Disclosure: Some links on this site are affiliate links. If you purchase through them, we earn a small commission at no extra cost to you. Learn more

·Quantum TLS
The Enterprise PQC Migration Checklist: Discovery to Deployment

The Enterprise PQC Migration Checklist: Discovery to Deployment

TL;DR

A PQC migration has four non-negotiable phases in strict order: (1) cryptographic discovery — build a Cryptographic Bill of Materials (CBOM) across TLS endpoints, code dependencies, certificates, and hardware, since you cannot migrate an algorithm you haven't found; (2) risk-based prioritization — rank discovered assets by "harvest now, decrypt later" exposure, not by ease of migration; (3) dependency and library remediation — upgrade to OpenSSL 3.5+, BoringSSL, or an oqs-provider bridge, and regenerate SBOMs to confirm transitive dependencies also moved; (4) phased reverse-proxy rollout — enable hybrid key exchange (e.g., X25519MLKEM768) as an additive, backward-compatible option via canary/percentage rollout, never a flag-day cutover, because hybrid groups are opportunistic and non-supporting clients silently fall back to classical groups without any outage. The single highest-leverage rule for avoiding outages: hybrid PQC key exchange is negotiated per-connection and degrades gracefully — the actual outage risk comes from CPU capacity under-provisioning (ML-KEM keygen/encaps cost) and middlebox ClientHello-size intolerance, not from the cryptography itself.

Why Sequencing Matters

Enterprises that treat PQC migration as a single "turn on the new TLS setting" change consistently discover, mid-rollout, that they don't actually know where all their cryptography lives — vendor SaaS integrations, embedded device fleets, code-signing pipelines, and internal service-to-service mTLS are all cryptographic surface area that a reverse-proxy-only rollout never touches. The four phases below are ordered because each is a hard prerequisite for the next: you cannot prioritize risk on an incomplete inventory, you cannot upgrade a dependency you haven't mapped, and you cannot safely roll out a proxy change without knowing which clients terminate there.

Phase 1: Cryptographic Discovery

The objective of this phase is a Cryptographic Bill of Materials (CBOM) — an inventory of every algorithm, key size, and certificate in use, mapped to the system that uses it. The emerging CycloneDX CBOM extension is the closest thing to a standard machine-readable format for this:

json
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "components": [
    {
      "type": "cryptographic-asset",
      "name": "edge-lb-tls-cert",
      "cryptoProperties": {
        "assetType": "certificate",
        "algorithmProperties": {
          "primitive": "signature",
          "parameterSetIdentifier": "P-256",
          "executionEnvironment": "software"
        }
      }
    }
  ]
}

Discovery Checklist

Phase 2: Risk-Based Prioritization

Not every discovered asset migrates in the same order. The dominant risk model for PQC timing is "harvest now, decrypt later" (HNDL): an adversary recording today's encrypted traffic can decrypt it retroactively once a cryptographically relevant quantum computer exists, which only threatens confidentiality, not authentication — so the correct prioritization axis is how long the data must remain confidential, not how easy the system is to migrate.

Priority TierCriteriaExample SystemsTarget Migration Window
Tier 0 — CriticalData confidentiality requirement exceeds 10 years; regulated (health, financial, national security)VPN concentrators, document-retention archives, long-lived session dataImmediate — before any HNDL-relevant timeline
Tier 1 — HighExternally-facing TLS with moderate-sensitivity dataPublic web/API edge, customer-facing authAligned to browser/client PQC support maturity
Tier 2 — ModerateInternal service-to-service traffic, short data lifetimeInternal mTLS mesh, ephemeral job queuesStandard infrastructure refresh cycle
Tier 3 — LowSignature-only use (integrity, not confidentiality) or already crypto-agileCode signing (can wait for ML-DSA tooling maturity), already-abstracted KMS-backed servicesOpportunistic, tracked but not urgent

Government-adjacent organizations should anchor Tier 0/1 windows to published external mandates rather than inventing their own — NSA's CNSA 2.0 timeline and the US federal NSM-10 directives are the most commonly cited external forcing functions, and contractual/regulatory obligations frequently reference them even for non-government enterprises in the supply chain.

Prioritization Checklist

Phase 3: Software Dependency Mapping and Library Remediation

Hybrid PQC support did not arrive uniformly across the TLS library ecosystem, and "update OpenSSL" is not a single well-defined action across an enterprise's actual dependency graph.

Library Landscape

LibraryPQC (ML-KEM) StatusIntegration Path
OpenSSL 3.5+Native X25519MLKEM768 and ML-KEM provider supportIn-tree as of 3.5; earlier 3.x needs the oqs-provider bridge
OpenSSL < 3.2No native supportRequires oqs-provider (built on liboqs) loaded via OPENSSL_CONF provider directives, or a full library upgrade
BoringSSLHybrid X25519Kyber768/X25519MLKEM768 shipped and used in Chrome by defaultGoogle-controlled fork; consumers get it via Chromium/gRPC/Envoy updates, not a standalone package upgrade
Go crypto/tlsNative X25519MLKEM768 support in recent stdlib releasesRequires a Go toolchain upgrade, not just a go.mod dependency bump
Java (JSSE + Bouncy Castle)ML-KEM/ML-DSA available via Bouncy Castle provider registrationRequires explicit Security.addProvider(new BouncyCastleProvider()) plus JSSE algorithm-constraint updates
Python cryptography/sslTracks the linked OpenSSL versionMigration is entirely a function of the underlying libssl the interpreter was built against
bash
# Verify what a given OpenSSL build actually supports before assuming coverage:
openssl list -providers
openssl list -kem-algorithms | grep -i mlkem
ini
# openssl.cnf — loading oqs-provider alongside the default provider
# when the base OpenSSL build predates native 3.5+ ML-KEM support.
[provider_sect]
default = default_sect
oqsprovider = oqsprovider_sect

[default_sect]
activate = 1

[oqsprovider_sect]
activate = 1
module = /usr/lib/ossl-modules/oqsprovider.so

Transitive Dependency Risk

The most commonly missed step is confirming that statically-linked or vendored copies of a TLS library inside container images, language runtimes, or third-party binaries actually picked up the upgrade — bumping a package manager's top-level entry does not guarantee every transitively vendored libssl.so was rebuilt against it. Regenerating an SBOM (via syft, cyclonedx-cli, or equivalent) after the upgrade and diffing it against the pre-upgrade SBOM is the only reliable way to confirm this, rather than trusting the top-level manifest declaration alone.

Dependency Checklist

Phase 4: Phased Reverse-Proxy Rollout

This is the phase with direct outage risk, and the phase this checklist spends the most operational detail on. The critical property that makes a safe rollout possible: hybrid key exchange groups are negotiated, not mandated — listing X25519MLKEM768 in a server's group preference alongside classical X25519/secp256r1 never breaks a client that doesn't support it; the TLS 1.3 handshake simply negotiates down to a mutually supported group. Outages during PQC rollouts are therefore almost always caused by one of two operational failures, not by the protocol negotiation itself: CPU capacity exhaustion and middlebox intolerance of larger ClientHello messages.

Step 1 — Canary Configuration

nginx
# nginx.conf — hybrid group listed alongside classical groups.
# Non-supporting clients negotiate down to x25519 automatically; no client-side outage.
ssl_protocols TLSv1.3;
ssl_conf_command Groups X25519MLKEM768:x25519:secp256r1;
ssl_ecdh_curve X25519MLKEM768:x25519:secp256r1;
nginx
# Canary split via weighted upstream: route a small percentage of
# terminating traffic to a pool with hybrid groups enabled first.
upstream edge_pool {
    server 10.0.1.10:443 weight=95;   # classical-only, unchanged baseline
    server 10.0.1.20:443 weight=5;    # hybrid-enabled canary
}

Step 2 — Capacity Validation Before Widening the Canary

ML-KEM key generation and encapsulation are computationally cheap in absolute terms but are additional CPU work layered onto every handshake that previously did only an X25519 scalar multiplication — at high connection-establishment rates (a busy public edge doing tens of thousands of new handshakes per second), this shifts aggregate CPU headroom in a way that matters for autoscaling thresholds and capacity planning, even though no single handshake becomes perceptibly slower. Load-test the hybrid-enabled pool at realistic peak handshake rates, not just functional correctness, before increasing canary weight.

Step 3 — Watch for ClientHello Fragmentation Fallout

Hybrid ClientHello messages carrying an ML-KEM-768 public key grow past 1,200+ bytes of key-share data alone, which has historically pushed some ClientHello messages past the single-TCP-segment/single-UDP-datagram boundary that a nontrivial population of legacy middleboxes silently assumed would never happen — some older TLS-inspecting proxies and load balancers were found to drop or mishandle oversized ClientHellos during the initial industry-wide Kyber/X25519Kyber768 rollout. Monitor negotiated-group and handshake-failure metrics specifically for the canary pool, and be prepared to fall back a client-facing pool to classical-only groups quickly if failure rates spike — this is the actual outage vector, not the cryptography.

Step 4 — Staged Widening

Avoiding Outages: The Core Operational Rules

  1. Never flag-day cut over. Hybrid groups are additive and backward-compatible by design — there is no technical requirement to remove classical group support simultaneously with adding PQC support, and doing so voluntarily reintroduces exactly the outage risk hybrid negotiation was designed to avoid.
  2. Capacity-test before widening, not after. ML-KEM's CPU cost is a fleet-capacity question, not a per-connection latency question — the failure mode is autoscaler lag or CPU saturation under load, which only appears at realistic traffic volume.
  3. Treat middlebox/ClientHello-size intolerance as the primary compatibility risk, not client TLS-stack support — modern browsers and TLS libraries handle group negotiation correctly; the risk sits in the network path between client and server, not at either endpoint.
  4. Keep rollback a configuration change, not a deployment. Group preference lists and canary weights should live in infrastructure config (feature flags, load balancer weights) with sub-minute revert capability, decoupled from application release cycles.
  5. Segment rollout by traffic pattern, not just percentage. A 5% canary that happens to route only same-region, well-behaved corporate-network traffic will not surface a consumer-ISP middlebox issue — ensure canary traffic is representative, not merely small.

Summary Checklist