In short

Serve TLS 1.3, plus TLS 1.2 only with ECDHE and AEAD suites, offer the hybrid X25519MLKEM768 group, and keep certificate verification on in every client. Automate renewal with ACME, because public certificate lifetimes fall to 200 days from March 2026, 100 days in 2027 and 47 days in 2029. Add HSTS and CAA, use a private CA for client certificates, and check each endpoint with a fresh handshake after every renewal.

  • A trusted chain is not enough: clients must also check the hostname and validity dates, and never disable verification.
  • Baseline: TLS 1.2 and 1.3, ECDHE with AES-GCM or ChaCha20-Poly1305, groups X25519MLKEM768, X25519, P-256 and P-384.
  • Maximum public certificate validity: 200 days from 15 March 2026, 100 days from 15 March 2027, 47 days from 15 March 2029.
  • Automate issuance, deployment and reload with ACME and ARI; a renewed file on disk proves nothing about what a listener serves.
  • Let’s Encrypt ended OCSP in 2025 and client-authentication certificates in 2026; issue mTLS client certificates from a private CA.
  • Add HSTS, CAA records and Certificate Transparency monitoring.

Verify it

  1. Run the command-line checks against every termination point and record protocol, key-exchange group, chain and expiry.
  2. Connect with a wrong hostname and with an untrusted root, and confirm both fail in every client that requires verification.
  3. Run testssl.sh --protocols --vulnerable host:443 and confirm SSL, TLS 1.0 and TLS 1.1 are not offered.
  4. Confirm responses carry Strict-Transport-Security and that dig CAA example.com +short returns only your intended CAs.
  5. Confirm that monitoring alerts when a served certificate has less than 30 days left.
Applies to
  • CA/Browser Forum Baseline Requirements 2.3.1
  • Mozilla Server Side TLS guidelines 6.0
  • OpenSSL 3.5 or later for X25519MLKEM768

TLS servers, clients and certificate renewal. Browser, native and private-PKI trust behavior differs, so test each client family.

Part of
Learning path: Cryptography without shortcuts (step 7 of 12). Workflow: Fix a finding

Fix it in your stack

Tested fixes: the vulnerable code, the fix, a plausible fix that still fails, and the regression tests that tell them apart.

Learning goals and prerequisitesAbout 33 minutes: 8 to read and 25 for the exercise

What you will learn

  • Separate certificate authentication from traffic-key encryption.
  • Configure a current TLS profile, including hybrid post-quantum key exchange.
  • Plan automated renewal and CA migration across actual termination points.
  • Verify failures for wrong identity, wrong trust and near expiry.

Before you start

Engineers who configure TLS endpoints or run certificate renewal, and reviewers who check what a handshake proves.

  • Distinguish certificate identity, chain validation, private keys and trust stores.
Go to the guided practice

The certificate authenticates a key in a protocol exchange

TLS negotiates protocol parameters, establishes traffic secrets, authenticates the peer as required, and protects application records. In a typical HTTPS connection, the server presents a certificate chain and proves possession of the corresponding private key within the handshake. The client validates both the chain and the intended service identity.

A valid chain to a trusted root is insufficient when the certificate is for the wrong hostname. Likewise, a correct hostname does not excuse an expired certificate or an untrusted path. Keep expected identity from the client’s configured destination; do not replace it with whatever name the peer presents.

The certificate key and traffic keys have different lifecycles. In an ephemeral exchange, the certificate key authenticates handshake material while derived traffic keys encrypt records. Renewal of a certificate does not automatically replace keys in already established connections, and a large RSA certificate key does not describe the negotiated symmetric cipher.

Make the client and server enforce one intended profile

Use a maintained TLS implementation and a documented profile. RFC 9325 recommends TLS 1.3 and restricts legacy protocols and suites. Mozilla’s Server Side TLS guidelines (version 6.0) give a tested intermediate profile: TLS 1.2 and 1.3 only, ECDHE key exchange with AES-GCM or ChaCha20-Poly1305 suites, the groups X25519MLKEM768, X25519, P-256 and P-384, and an ECDSA P-256 or RSA 2048-bit certificate; its modern profile allows TLS 1.3 only. Treat client compatibility as measured input, not an indefinite reason to retain every historic protocol.

Keep certificate verification enabled in internal clients as well as browsers; the fix card at /fix/tls-certificate-validation/ shows how in each stack. A private network does not make a peer-provided certificate self-authenticating. Configure private trust anchors deliberately and scope their distribution. Adding an internal root to every machine gives it authority over every context those machines allow that root to authenticate.

SNI helps the server choose a certificate; it is not itself proof of the server’s identity. The client still checks the intended hostname. For mutual TLS, client certificate validation identifies a caller, but the application must map that identity to allowed operations and tenants. A certificate accepted by a shared CA is not universal administrator authority. Issue client certificates from a private CA: Let’s Encrypt removed the client authentication usage from its default certificates on 11 February 2026 and stopped issuing it on 8 July 2026, following the Chrome Root Program requirement to separate server and client PKIs.

TLS 1.3 early data can be replayed under relevant conditions. Enable it only for operations whose protocol and application semantics tolerate that behavior. A request looking like a GET is not sufficient evidence if the handler performs a purchase or changes account state.

Turn on hybrid post-quantum key exchange

The hybrid group X25519MLKEM768 combines X25519 with ML-KEM-768 from FIPS 203, so recorded traffic stays confidential unless both are broken. It needs no new certificate. It is on by default in OpenSSL 3.5 and later, Go 1.24 and later and JDK 27, and offered by Chrome and Edge 131 and later, Firefox 132 and later on desktop, and Safari 26. A server built on such a library usually negotiates it already; the fourth command below confirms it. The migration guide at /domains/identity-crypto/key-management-pki/crypto-inventory-pqc/ covers the rest of the post-quantum work.

The key share adds about 1.2 KB to the ClientHello, so test load balancers, firewalls and other intermediaries that mishandle large handshakes, and keep X25519 as a fallback group. Go’s GODEBUG=tlsmlkem=0 and JDK’s jdk.tls.namedGroups setting can turn the group off while a broken intermediary is fixed.

Renewal includes deployment and independent observation

Inventory each termination point: external load balancer, reverse proxy, service mesh sidecar, direct service listener, and fallback site. Record certificate names, issuer, expiration, key identity, owner, renewal method, and how a running process reloads the new credential. A renewed file on disk is not evidence that a listener serves it.

Renew before expiry with enough time for failed issuance, DNS or challenge changes, deployment, and rollback. With ACME, let the CA’s renewal information (ARI, RFC 9773) choose the renewal window; without ARI, renew at about two-thirds of the certificate’s lifetime rather than after a fixed number of days. Validate that the private key matches the leaf certificate, that the names cover intended clients, and that the served chain contains the required intermediates. Clients normally carry trust anchors; sending an arbitrary root does not make it trusted.

Roll the new credential across endpoints and observe fresh handshakes from the client perspective. Existing connections and resumption can hide an old or broken served chain, so deliberately test a full handshake. Keep a bounded rollback path that does not restore a known-compromised private key.

For CA migration, distribute the new trust anchor before issuing identities that depend on it, then remove the old anchor after dependencies are migrated. Root removal and leaf renewal are separate changes. During a compromise, ordinary overlap may be inappropriate; define revocation and replacement according to the actual exposure.

Plan for 200-, 100- and 47-day certificates

CA/Browser Forum ballot SC-081v3 (April 2025) shortens publicly trusted TLS certificates in steps, together with the time a CA may reuse a domain validation. Manual renewal does not survive these intervals, as the outages in /domains/identity-crypto/key-management-pki/case-study-certificate-expiry/ show: automate issuance, deployment and reload, and alert on any served certificate in the last third of its validity.

Let’s Encrypt moves faster. Its opt-in tlsserver profile has issued 45-day certificates since 13 May 2026, its default profile switches to 64-day certificates on 10 February 2027 and to 45-day certificates on 16 February 2028, and its shortlived profile issues certificates valid for about six days.

Revocation has changed too. Let’s Encrypt stopped including OCSP URLs on 7 May 2025 and turned off its OCSP responders on 6 August 2025, publishing CRLs instead. Short lifetimes bound how long a stolen key stays useful. Check that non-browser clients, such as VPN and mail software, accept certificates without an OCSP URL.

Plan for 200-, 100- and 47-day certificates
Certificate issued on or afterMaximum validityDomain validation reuse
Before 15 March 2026398 days398 days
15 March 2026200 days200 days
15 March 2027100 days100 days
15 March 202947 days10 days

Add HSTS, CAA and Certificate Transparency monitoring

  • Send Strict-Transport-Security: max-age=63072000; includeSubDomains once every subdomain serves HTTPS. Preloading also needs preload, a max-age of at least 31536000 and a submission at hstspreload.org, and removal takes months.
  • Publish CAA records that name the only CAs allowed to issue, such as example.com. CAA 0 issue "letsencrypt.org", plus issuewild and iodef records. The RFC 8657 accounturi and validationmethods parameters tie issuance to your ACME account and challenge type, and CAs must honor them from 15 March 2027.
  • Monitor Certificate Transparency logs for certificates that name your domains, through a monitoring service or crt.sh, so that unexpected issuance is noticed and revoked.
  • Do not pin public certificates; with 47-day lifetimes they change constantly. Pin only in clients you update yourself, pin a public key or private CA, and ship a backup pin.

Verify a server from the command line

Run these checks against every termination point after each renewal; set host and port first. They were tested with OpenSSL 3.6.4 against local servers: a wrong hostname or untrusted root makes the second check fail, a certificate with less than 30 days left makes the third exit with status 1, and the fourth prints nothing when the server does not accept the hybrid group. For a full audit of protocols, cipher suites and known vulnerabilities, use testssl.sh or the Qualys SSL Labs server test.

Testcheck-tls.shShell
host=api.example.com port=443

# 1. Served chain, protocol, cipher and key-exchange group from a fresh handshake
openssl s_client -connect "$host:$port" -servername "$host" -showcerts </dev/null

# 2. Exit non-zero unless the chain is trusted and valid for this hostname
openssl s_client -connect "$host:$port" -servername "$host" -verify_hostname "$host" \
  -verify_return_error </dev/null >/dev/null 2>&1 && echo "trusted for $host"

# 3. Exit status 1 if the served certificate expires within 30 days
openssl s_client -connect "$host:$port" -servername "$host" </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -enddate -checkend 2592000

# 4. Prints a line only if hybrid post-quantum key exchange is accepted (OpenSSL 3.5+)
openssl s_client -connect "$host:$port" -servername "$host" -groups X25519MLKEM768 \
  </dev/null 2>&1 | grep 'Negotiated TLS1.3 group: X25519MLKEM768'

# 5. Does this private key match this certificate? The two digests must be equal.
openssl x509 -in cert.pem -noout -pubkey | openssl sha256
openssl pkey -in key.pem -pubout | openssl sha256

Test identity failure as well as successful encryption

A supported client should establish a fresh connection to the expected hostname and see the intended certificate chain. Wrong-hostname, expired, untrusted-root, and incomplete-chain cases must fail in clients whose policy requires those validations. These negative cases catch the common mistake of proving encryption while disabling identity checks.

Test every termination point and one consumer from each distinct trust-store family. A browser success does not prove a Java service, embedded device, or private CA client has the same trust configuration. Record the actual served certificate fingerprint and expiry, not only the renewal job’s exit status.

Guided practice

It is 10 October 2026 and clients of api.example.com report failures. Read the real OpenSSL output below, explain each error, and write the fix and the checks that would have caught it. Produce your answers before reading the worked ones.

Supplied scenario material
Plain text
$ openssl s_client -connect lb-a.example.net:443 -servername api.example.com \
    -verify_hostname api.example.com -CAfile root.pem </dev/null 2>&1 | grep -E "verify error|Verify return"
verify error:num=20:unable to get local issuer certificate
verify error:num=62:hostname mismatch
verify error:num=21:unable to verify the first certificate
verify error:num=10:certificate has expired
Verify return code: 10 (certificate has expired)

$ openssl s_client -connect lb-a.example.net:443 -servername api.example.com </dev/null 2>/dev/null \
    | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
subject=CN=www.example.com
issuer=CN=Example Issuing CA 2
notBefore=Sep  1 00:00:00 2026 GMT
notAfter=Oct  4 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
    DNS:www.example.com, DNS:example.com

LB-B: the renewal job reported success on 1 October and /etc/ssl/api.pem on disk has notAfter=Apr 19 2027,
but a fresh handshake to LB-B still shows notAfter=Oct  4 23:59:59 2026 GMT.
  1. 1. Explain each error

    Say what each of the four verify errors on LB-A means.

    Expected result and reasoning

    Error 20 and 21: the server sent only its leaf certificate, without the Example Issuing CA 2 intermediate, so a client that trusts only the root cannot build the chain. Error 62: the certificate names www.example.com and example.com, not api.example.com, so LB-A is serving the wrong certificate for this hostname. Error 10: notAfter was 4 October 2026, six days ago. The certificate was generated for this exercise and the output is real OpenSSL 3.6 output.

  2. 2. Explain LB-B

    Explain why LB-B still serves the expired certificate although renewal succeeded.

    Expected result and reasoning

    The new certificate was written to disk but the listener was never reloaded, so the process still holds the old certificate in memory. A renewal job's exit code and a file on disk prove nothing about what a listener serves; only a fresh handshake does.

  3. 3. Write the fix

    List the changes for LB-A and LB-B and the command you run afterwards.

    Expected result and reasoning

    LB-A: install a certificate whose SAN includes api.example.com, serve it with its intermediate (full chain), and attach it to the right listener or SNI name. LB-B: reload or restart the listener and make the renewal job do so automatically. Then rerun the first command against both and expect Verify return code: 0 (ok), and check notAfter from a fresh handshake.

  4. 4. Write the monitoring check

    Write the check that would have alerted before 4 October.

    Expected result and reasoning

    A scheduled job that opens a fresh handshake to every termination point, verifies chain and hostname with -verify_hostname and -verify_return_error, and fails when openssl x509 -checkend reports less than 30 days (or less than a third of the lifetime) left. The checker in the certificate expiry case study at /domains/identity-crypto/key-management-pki/case-study-certificate-expiry/ does this for a list of endpoints.

  5. 5. Plan for shorter lifetimes

    Say what changes when public certificates are limited to 100 days from 15 March 2027 and 47 days from 15 March 2029.

    Expected result and reasoning

    Manual or calendar-driven renewal stops being viable: automate issuance, deployment and reload with ACME, let ARI (RFC 9773) pick the renewal time or renew at two-thirds of the lifetime, and keep the served-certificate check as the safety net.

Your deliverable: An explanation of each error, the fixes for LB-A and LB-B, the monitoring check and the automation plan.

The certificates were generated for the exercise with a private root; with a public CA the same errors appear for the same reasons. Run the commands against your own endpoints to see your real state.

Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.

No draft saved on this device yet.

Sources & further reading

The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.

  1. RFC 9325: Secure use of TLS and DTLS. November 2022 (BCP 195); updated by RFC 9852 and RFC 10015.
  2. RFC 9846: TLS 1.3. July 2026; obsoletes RFC 8446.
  3. RFC 9525: Service identity in TLS. November 2023.
  4. Mozilla Server Side TLS configuration generator (guidelines 6.0)
  5. CA/Browser Forum Baseline Requirements: validity schedule. Version 2.3.1 (4 October 2026).
  6. CA/Browser Forum ballot SC-081v3. Adopted 11 April 2025.
  7. Let’s Encrypt: decreasing certificate lifetimes to 45 days. 2 December 2025.
  8. Let’s Encrypt: ending OCSP support. 5 December 2024.
  9. Let’s Encrypt: ending TLS client authentication. 14 May 2025.
  10. RFC 9773: ACME Renewal Information. June 2025.
  11. Cloudflare: post-quantum key agreement support by client
  12. OWASP Transport Layer Security Cheat Sheet
  13. RFC 10015: Deprecating obsolete key exchange methods in TLS 1.2 and DTLS 1.2. July 2026.

Reference information

ASVS 5.0.0 requirements
v5.0.0-12.1.1 · v5.0.0-12.1.2 · v5.0.0-12.2.2 · v5.0.0-12.3.2 · v5.0.0-12.3.4 · v5.0.0-3.4.1
CAPEC attack patterns

Download this guide as Markdown