---
key: "tls-pki-operations"
title: "Operate TLS and certificates: configuration, renewal and verification"
url: "https://softwaresecurity.com/domains/identity-crypto/key-management-pki/tls-pki-operations/"
api: "https://softwaresecurity.com/api/v1/articles/tls-pki-operations.json"
domain: "identity-crypto"
topic: "Identity \u0026 cryptography"
type: "Guide"
identifiers: ["CWE-295","CWE-297","CWE-298","CWE-319","A04:2025","A07:2025","API8:2023","V12.1.1","V12.1.2","V12.2.2","V12.3.2","V12.3.4","V3.4.1","CAPEC-94"]
tags: ["Browser security","PKI and certificates","TLS","Post-quantum cryptography"]
stages: ["design","operate"]
roles: ["developer","platform-sre"]
license: "CC-BY-4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
code_license: "MIT"
---

# Operate TLS and certificates: configuration, renewal and verification

Configure a modern TLS profile with hybrid post-quantum key exchange, automate renewal for certificate lifetimes that fall to 47 days, and verify every termination point with a fresh handshake.

Canonical URL: https://softwaresecurity.com/domains/identity-crypto/key-management-pki/tls-pki-operations/

## 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.

## Recommended defaults

| Setting | Value | Note | Source |
| --- | --- | --- | --- |
| Protocols | TLS 1.3 and TLS 1.2 only | SSL 2.0, SSL 3.0, TLS 1.0 and TLS 1.1 disabled (RFC 8996). | RFC 9325; Mozilla Server Side TLS 6.0 (intermediate) |
| TLS 1.2 cipher suites | ECDHE with AES-GCM or ChaCha20-Poly1305 | No CBC, RC4, 3DES, static RSA key transport or export suites; RFC 10015 (July 2026) forbids RSA and finite-field DHE key exchange in TLS 1.2. | Mozilla Server Side TLS 6.0 (intermediate); RFC 10015 |
| Key exchange groups | `X25519MLKEM768`, `X25519`, `prime256v1`, `secp384r1` | The hybrid group is the default first key share in OpenSSL 3.5 and later. | Mozilla Server Side TLS 6.0; OpenSSL 3.5 release notes |
| Certificate key | ECDSA P-256, or RSA 2048 bits or more | Generate a new key at each renewal where automation allows. | Mozilla Server Side TLS 6.0 |
| Maximum certificate validity | 200 days (from 15 March 2026); 100 days (2027); 47 days (2029) | Domain validation reuse falls to 200, 100 and finally 10 days. | CA/Browser Forum Baseline Requirements 2.3.1, section 6.3.2 |
| Renewal | At the ACME ARI window, otherwise at two-thirds of the lifetime | Alert when a served certificate has less than 30 days left. | RFC 9773; Let’s Encrypt, December 2025 |
| HSTS | `max-age=63072000; includeSubDomains` | Preload needs `preload` and a max-age of at least 31536000. | Mozilla Server Side TLS 6.0; hstspreload.org |
| Client certificates (mTLS) | Issued by a private CA | Public CAs are separating server and client authentication. | Let’s Encrypt, May 2025 (updated March 2026) |

## 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.

## 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.

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

Before you start:

- Distinguish certificate identity, chain validation, private keys and trust stores.

Time: About 33 minutes: 8 to read and 25 for the exercise

## 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.

| Certificate issued on or after | Maximum validity | Domain validation reuse |
| --- | --- | --- |
| Before 15 March 2026 | 398 days | 398 days |
| 15 March 2026 | 200 days | 200 days |
| 15 March 2027 | 100 days | 100 days |
| 15 March 2029 | 47 days | 10 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.

```bash
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 material

```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. Explain each error

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

Worked answer: 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. Explain LB-B

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

Worked answer: 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. Write the fix

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

Worked answer: 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. Write the monitoring check

Write the check that would have alerted before 4 October.

Worked answer: 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. 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.

Worked answer: 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.

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

Scope: 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.

Articles and teaching material: CC BY 4.0. Original executable code: MIT. Attribution: Software Security.

## Sources

- [RFC 9325: Secure use of TLS and DTLS](https://www.rfc-editor.org/rfc/rfc9325.html). November 2022 (BCP 195); updated by RFC 9852 and RFC 10015.
- [RFC 9846: TLS 1.3](https://www.rfc-editor.org/rfc/rfc9846.html). July 2026; obsoletes RFC 8446.
- [RFC 9525: Service identity in TLS](https://www.rfc-editor.org/rfc/rfc9525.html). November 2023.
- [Mozilla Server Side TLS configuration generator (guidelines 6.0)](https://ssl-config.mozilla.org/)
- [CA/Browser Forum Baseline Requirements: validity schedule](https://github.com/cabforum/servercert/blob/main/docs/BR.md). Version 2.3.1 (4 October 2026).
- [CA/Browser Forum ballot SC-081v3](https://cabforum.org/2025/04/11/ballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods/). Adopted 11 April 2025.
- [Let’s Encrypt: decreasing certificate lifetimes to 45 days](https://letsencrypt.org/2025/12/02/from-90-to-45/). 2 December 2025.
- [Let’s Encrypt: ending OCSP support](https://letsencrypt.org/2024/12/05/ending-ocsp/). 5 December 2024.
- [Let’s Encrypt: ending TLS client authentication](https://letsencrypt.org/2025/05/14/ending-tls-client-authentication/). 14 May 2025.
- [RFC 9773: ACME Renewal Information](https://www.rfc-editor.org/rfc/rfc9773.html). June 2025.
- [Cloudflare: post-quantum key agreement support by client](https://developers.cloudflare.com/ssl/post-quantum-cryptography/pqc-support/)
- [OWASP Transport Layer Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html)
- [RFC 10015: Deprecating obsolete key exchange methods in TLS 1.2 and DTLS 1.2](https://www.rfc-editor.org/rfc/rfc10015.html). July 2026.