Enable post-quantum cryptography

Post-quantum cryptography (PQC) protects TLS connections against future quantum computers. A quantum computer powerful enough to break current public-key algorithms like RSA and Elliptic Curve Cryptography (ECC) could decrypt traffic captured today. Security teams call this threat harvest now, decrypt later. F5 NGINXaaS for Google Cloud addresses this with two modes:

  • Hybrid mode: Combines classical elliptic-curve key exchange with the Module-Lattice-Based Key-Encapsulation Mechanism (ML-KEM) for data encryption. This protects against quantum attacks while staying compatible with clients that don’t yet support PQC.
  • Full PQC mode: Uses Module-Lattice-Based Digital Signature Algorithm (ML-DSA) certificates and keys that you provide. This gives you a fully post-quantum TLS stack when both client and server support it.

Before you begin

Before you begin, ensure you have:

  • An existing NGINXaaS for Google Cloud deployment: See Create a deployment if you need to create one.
  • TLS 1.3 in your NGINX configuration: NGINX includes TLSv1.3 in its default ssl_protocols value alongside TLSv1.2. ML-KEM hybrid key exchange only applies to TLS 1.3 connections. To prevent clients from downgrading to TLS 1.2, restrict ssl_protocols to TLSv1.3 only - this is recommended for maximum security but drops support for older clients.

Enable hybrid mode (ML-KEM key exchange)

Hybrid mode is active by default. NGINX includes TLSv1.3 in its default ssl_protocols value, and ML-KEM hybrid groups are part of the default key exchange group list. No configuration changes are required in the NGINXaaS console for hybrid mode to work.

Clients that support ML-KEM will negotiate it automatically on TLS 1.3 connections. Clients that don’t support ML-KEM fall back to classical key exchange.

Because hybrid ML-KEM key exchange applies only to TLS 1.3 connections, allowing TLS 1.2 means some clients can still negotiate a purely classical handshake. To prevent a downgrade, restrict ssl_protocols to TLSv1.3 only:

  1. Select Configurations in the left menu.

  2. Select the ellipsis (three dots) next to your configuration and select Edit.

  3. Add ssl_protocols TLSv1.3; to your server block:

    nginx
    server {
        listen 443 ssl;
        ssl_protocols TLSv1.3;
        ssl_certificate     /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;
        # ...
    }
  4. Select Next and then Save to apply the change.

  5. Deploy configuration to relevant deployments.

Restricting to TLSv1.3 drops support for clients that only support TLS 1.2. If you need to support older clients, keep the default ssl_protocols value and accept that those connections won’t use ML-KEM.

Optional: Explicitly configure ML-KEM hybrid groups

If you want to enforce a specific group order or exclude classical-only groups, set ssl_ecdh_curve in your server block. The following snippet enables ML-KEM hybrid (X25519MLKEM768) first, with X25519 as a classical fallback:

nginx
server {
    listen 443 ssl;
    ssl_protocols TLSv1.3;
    ssl_ecdh_curve X25519MLKEM768:X25519;
    ssl_certificate     /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;
    # ...
}
Including X25519 after X25519MLKEM768 lets clients that don’t support ML-KEM fall back to classical key exchange. Remove X25519 only if you want to restrict connections to ML-KEM-capable clients.

Enable full PQC mode (ML-DSA certificates)

Full PQC mode requires you to upload an ML-DSA certificate and private key. NGINXaaS for Google Cloud accepts ML-DSA keys in PEM format with the following constraints:

  • NGINXaaS Console: Seed-only key format only.
  • Google Secret Manager: Seed-only and seed-priv formats are both supported.

Choose the method that matches your key format.

Upload an ML-DSA certificate using the Console

Use this method if your ML-DSA private key is in seed-only format.

  1. Follow the steps in Add certificates using the Console to upload your ML-DSA certificate and key.

  2. In your NGINX configuration, reference the certificate and key with ssl_certificate and ssl_certificate_key, and set ssl_protocols TLSv1.3;:

    nginx
    server {
        listen 443 ssl;
        ssl_protocols TLSv1.3;
        ssl_certificate     /etc/nginx/certs/mldsa.crt;
        ssl_certificate_key /etc/nginx/certs/mldsa.key;
        # ...
    }
  3. Select Next and then Save to apply the change.

  4. Deploy configuration to relevant deployments.

Store an ML-DSA certificate in Google Secret Manager

Use this method if your ML-DSA private key is in seed-priv format, or if you want to keep your keys within Google Cloud.

  1. Add your ML-DSA certificate and key to Google Secret Manager. Follow the steps in Add an SSL/TLS certificate to Secret Manager.
  2. Reference the secret in your NGINX configuration as described in Use a Secret Manager certificate in an NGINX configuration.
  3. Add ssl_protocols TLSv1.3; to your server block.

Verify post-quantum negotiation

To confirm that a client is negotiating a post-quantum key exchange group with your deployment, inspect the TLS handshake from the client side using OpenSSL:

openssl s_client -connect <YOUR_DEPLOYMENT_ENDPOINT>:443 -groups X25519MLKEM768

In the output, look for the Negotiated TLS1.3 group line. A successful hybrid negotiation shows:

Negotiated TLS1.3 group: X25519MLKEM768

If you see X25519 or another classical group instead, confirm that ssl_protocols TLSv1.3; is set, and that no ssl_ecdh_curve directive is overriding the defaults with classical-only groups.

Track ML-KEM adoption across real clients

OpenSSL spot-checks confirm your server is ready, but they don’t show which of your actual clients negotiate ML-KEM. Use the $ssl_curve NGINX variable to log the key exchange group negotiated for each connection, then analyze the logs to measure adoption over time.

Add a custom log_format and an access_log directive to your NGINX configuration:

nginx
http {
    log_format pqc_tracking '$remote_addr - $ssl_protocol $ssl_curve';
    access_log /var/log/nginx/ssl-details.log pqc_tracking;
    # ...
}

In the logs, connections that negotiated ML-KEM hybrid key exchange appear with X25519MLKEM768 in a value of the $ssl_curve variable. Classical TLS 1.3 connections appear with X25519 or another classical group name, and TLS 1.2 connections return an empty value.

To forward logs to Cloud Logging, see Enable NGINX logs.

What’s next