PKI and Certificates
How we trust a website we have never met: certificates, certificate authorities, the chain of trust, TLS and what goes wrong in practice.
The trust problem
When you open your bank's website, your browser receives a public key and uses it to set up encryption. Encryption alone is not enough: if an attacker on the same Wi-Fi can hand you their public key instead, you would set up a perfectly encrypted connection — to the attacker. The real question is: how do you know that this public key belongs to your bank?
You have never met the bank's servers, so you need someone you already trust to vouch for them. A public key infrastructure (PKI) is the system that does this: the certificates, the certificate authorities that issue and sign them, the policies and procedures they follow, and the ways to check and revoke certificates. The cryptography underneath — key pairs, hashes and digital signatures — is explained in Cryptography Basics.
Digital certificates
A digital certificate is
a small signed document that says, in effect: "the public key below
belongs to www.example.com, from this date until that
date — signed, the issuer". Because the issuer signed it, nobody can
change the name or swap the key without breaking the signature. The
certificate is public; the matching private key
stays on the server and is never part of the certificate.
The format used almost everywhere is X.509 (version 3). The fields you will meet most:
| Field | What it holds | Why it matters |
|---|---|---|
| Subject |
Who the certificate is for, e.g.
CN=www.example.com
|
The common name (CN) is now informational; browsers match names against the SAN |
| Subject Alternative Name (SAN) | The list of names the certificate is valid for | The name you typed must appear here, or the browser warns |
| Issuer | The CA that signed it | Tells the client which certificate to check next |
| Validity (Not Before / Not After) | The start and end date and time | Outside this window the certificate is rejected |
| Public key | The subject's public key and its algorithm (RSA, ECDSA) | The key the client uses to verify the server |
| Serial number | A unique number per issuer | Used to identify the certificate when it is revoked |
| Extensions | Key usage, extended key usage, basic constraints, CRL and OCSP locations | Limit what the key may do (e.g. server authentication only) and whether it may sign other certificates |
| Signature | The issuer's digital signature over all of the above | Proves the content was not changed since the CA signed it |
Certificate authorities and the chain of trust
A certificate authority (CA) is an organization — or, inside a company, a server — that checks a requester's claim to a name and then signs a certificate for it. Public CAs are audited and must follow the Baseline Requirements of the CA/Browser Forum, a body of CAs and browser makers.
Root and intermediate CAs
CAs are arranged in a hierarchy. At the top is a root CA, whose certificate is self-signed: it vouches for itself, so it is only trusted because your device ships with it. A root key is so valuable that it is kept offline, usually in a hardware security module (HSM). The root signs one or more intermediate CAs, and the intermediates sign the server ("leaf") certificates day to day. If an intermediate is compromised, it can be revoked without replacing the root on every device in the world.
The chain of trust is the path a client follows from the server certificate up to a trusted root: each certificate is signed by the next one up, and the client checks every signature, validity period and constraint on the way. The server sends its own certificate plus the intermediates; the client supplies the root from its own trust store.
Where the trusted roots come from
Each operating system and some browsers maintain a root store: the list of root certificates they trust, managed by root programs such as those of Microsoft, Apple, Google (Chrome) and Mozilla. Roots are added after audits and removed when a CA breaks the rules — browsers have distrusted CAs for misissuing certificates. Updates to the root store arrive with normal OS and browser updates, which is one more reason to patch.
Organizations can add their own roots, for example for an internal CA or for a TLS-inspecting proxy (see Network Security in Depth). Every root you add can vouch for any name, so add only what you control, and manage it centrally.
TLS: putting the certificate to work
Transport Layer Security (TLS) is the protocol that protects HTTPS, secure email transport, many VPNs and countless APIs. It replaced the older Secure Sockets Layer (SSL); people still say "SSL certificate", but every current version is TLS. TLS gives three things: the server's identity is verified (through its certificate), the data is encrypted (confidentiality) and any tampering is detected (integrity).
The handshake at a high level (TLS 1.3, simplified)
- Client hello. The client lists the TLS versions and cipher suites it supports, sends its share of a key exchange, and names the site it wants (Server Name Indication, SNI), so one server can host many sites.
- Server hello and certificate. The server picks a version and cipher suite, sends its key share, its certificate chain, and a signature made with its private key over the handshake so far. That signature proves it really holds the private key belonging to the certificate. In TLS 1.3 both sides already derive handshake keys from the key shares, so the certificate is sent encrypted.
- The client checks. Does the chain lead to a trusted root? Are all signatures valid and all certificates within their dates? Is the name in the SAN? Any "no" means a warning or a refused connection.
- Session keys. From the two key shares, both sides compute the same secret (an ephemeral Diffie–Hellman key exchange) and derive symmetric keys from it.
- Encrypted data. Everything afterward is encrypted with a fast symmetric cipher such as AES-GCM or ChaCha20-Poly1305.
This is the hybrid pattern described in Cryptography Basics: asymmetric cryptography for identity and key agreement, symmetric cryptography for the bulk data. Because the session keys come from ephemeral key shares that are thrown away, a recording of today's traffic cannot be decrypted later even if the server's private key leaks — this property is called forward secrecy.
TLS 1.2 and TLS 1.3
TLS 1.3 (2018) is the current version; TLS 1.2 (2008) is still widely supported and acceptable when configured with strong cipher suites. TLS 1.3 needs one round trip less to set up a connection, removed old and weak options (static RSA key exchange, CBC-mode ciphers, SHA-1 signatures), always provides forward secrecy, and encrypts the certificate itself during the handshake. SSL 2.0 and 3.0, TLS 1.0 and TLS 1.1 are deprecated and should be disabled.
What the padlock does — and does not — prove
The padlock (or, in newer browsers such as Chrome, a neutral "tune" icon) means: the connection is encrypted, and the server proved it holds the key for a certificate that a trusted CA issued for this exact name. It does not mean the site is honest, safe or belongs to the company you think of.
Certificate types
Certificates differ in how thoroughly the CA checked the requester (validation level) and in how many names they cover.
| Type | What it means | Typical use |
|---|---|---|
| Domain validated (DV) | The CA only checked that the requester controls the domain (a DNS record, a file on the web server) | Most websites; what ACME CAs issue automatically |
| Organization validated (OV) | The CA also checked that the organization exists | Company sites that want the organization name in the certificate |
| Extended validation (EV) | A stricter, documented check of the organization | Still sold, but browsers stopped showing a special green bar around 2019, so users rarely see the difference |
| Wildcard |
*.example.com covers one level of subdomains:
www.example.com and
shop.example.com, but not
example.com itself or
a.b.example.com
|
Many subdomains on the same servers |
| Multi-domain (SAN) | One certificate with an explicit list of names in the SAN |
A few related names, e.g. example.com and
www.example.com
|
| Self-signed | Signed with its own key; no CA vouches for it | Labs and device setup pages; never for public services |
Getting a certificate
The classic process starts on the server: generate a key pair, then create a certificate signing request (CSR) that contains the public key and the requested names, signed with the private key to prove possession. The CA validates the request and returns the signed certificate. The private key never leaves the server.
$ openssl req -new -newkey rsa:3072 -nodes -keyout shop.key -out shop.csr \
-subj "/CN=shop.example.com" \
-addext "subjectAltName=DNS:shop.example.com,DNS:www.shop.example.com"
-nodes (-noenc in OpenSSL 3.0 and later)
leaves the key file unencrypted, which servers usually need to start
unattended — so protect shop.key with strict file
permissions and keep it out of version control.
ACME and Let's Encrypt
The Automatic Certificate Management Environment (ACME, RFC 8555) protocol automates the whole cycle: an ACME client on the server proves control of the domain (by serving a token over HTTP or publishing a DNS record), receives the certificate, installs it and renews it on a schedule. Let's Encrypt, a free nonprofit CA, popularized this; many other CAs and most web servers, load balancers and hosting panels now support ACME. Common clients are Certbot, acme.sh and the built-in support in servers such as Caddy.
Shorter and shorter lifetimes
Public TLS certificates used to be valid for years; the maximum fell to 398 days in 2020. In 2025 the CA/Browser Forum voted to shrink it further in steps — at the time of writing, to 200 days from March 2026, 100 days from March 2027 and 47 days from March 2029. Let's Encrypt certificates have long lasted 90 days, and it has announced plans for even shorter ones. Check the current rules before you plan, because these schedules can change.
The reasoning: a short-lived certificate limits how long a stolen key or a wrongly issued certificate can be abused, and it forces organizations to automate renewal instead of relying on a calendar reminder. The practical consequence is clear: manual renewal of public certificates is on its way out.
Revocation
Sometimes a certificate must stop being trusted before it expires: the private key leaked, the server was decommissioned, or the certificate was issued in error. Certificate revocation is the CA declaring it invalid ahead of time. Clients learn about it in a few ways:
- Certificate revocation list (CRL): a list of revoked serial numbers, signed and published by the CA at regular intervals. Simple, but lists can grow large and are only as fresh as their last publication.
- Online Certificate Status Protocol (OCSP): the client asks the CA's responder about one certificate and gets a signed "good", "revoked" or "unknown". It is fresher, but it tells the CA which sites you visit and fails awkwardly when the responder is down.
- OCSP stapling: the server fetches a recent signed OCSP response itself and "staples" it to the handshake, so the client does not have to ask the CA.
In practice, browsers have moved away from live OCSP checks; at the time of writing, major browsers mostly rely on compact revocation lists that the browser vendor pushes to clients, and several CAs — including Let's Encrypt — have stopped running OCSP services in favor of CRLs. Short certificate lifetimes are partly a response to revocation being hard to do well: a certificate that expires in a few weeks matters less if a revocation is missed.
Common problems and how to read them
Certificate errors look alarming, but nearly all of them fall into a handful of causes. The browser codes below are Chrome's; other browsers use similar names.
| Symptom | Usual cause | Fix |
|---|---|---|
Expired (NET::ERR_CERT_DATE_INVALID) |
Renewal failed or was forgotten | Renew and install; automate renewal and monitor expiry |
Name mismatch
(NET::ERR_CERT_COMMON_NAME_INVALID)
|
The name typed is not in the SAN — e.g. the certificate
covers www.example.com but the user opened
example.com, or an internal name or IP address
|
Reissue with every name users actually use |
| Incomplete chain | The server sends its own certificate but not the intermediate. Browsers often repair this, by downloading the missing intermediate (AIA fetching) or from a cache, so it "works on my laptop" but fails in other browsers, apps and scripts | Configure the server with the full chain file |
Untrusted issuer or self-signed
(NET::ERR_CERT_AUTHORITY_INVALID)
|
A self-signed or internal-CA certificate on a device that does not trust that root — or an interception attempt | Use a public CA for public services; distribute the internal root centrally for internal ones |
| Valid certificate reported as expired or "not yet valid" | The client's clock is wrong (dead CMOS battery, no time synchronization) | Fix the time; see NTP and CMOS |
Inspecting a certificate with openssl
openssl s_client connects like a browser and shows the
chain and the verification result. Here a user reports that the VPN
portal "suddenly gives a warning":
$ openssl s_client -connect vpn.example.com:443 -servername vpn.example.com </dev/null
CONNECTED(00000003) depth=0 CN = vpn.example.com verify error:num=10:certificate has expired --- Certificate chain 0 s:CN = vpn.example.com i:C = NL, O = Example Trust, CN = Example Issuing CA R1 1 s:C = NL, O = Example Trust, CN = Example Issuing CA R1 i:C = NL, O = Example Trust, CN = Example Root CA --- Verify return code: 10 (certificate has expired)
The chain is complete (the server sends the leaf at depth 0 and the
intermediate at depth 1), but the leaf has expired. Pipe the
certificate into openssl x509 to read the fields
themselves:
$ openssl s_client -connect vpn.example.com:443 -servername vpn.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
subject=CN = vpn.example.com
issuer=C = NL, O = Example Trust, CN = Example Issuing CA R1
notBefore=Mar 12 00:00:00 2026 GMT
notAfter=Sep 28 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
DNS:vpn.example.com
Other verify codes you will meet:
-
21 (unable to verify the first certificate)— the intermediate is missing: an incomplete chain. -
20 (unable to get local issuer certificate)— the issuer is not in the local trust store (an internal CA, or a missing intermediate). -
18 (self-signed certificate)— no CA behind it.
For a certificate file on disk,
openssl x509 -in cert.pem -noout -checkend 2592000
reports whether it expires within the next 30 days (2,592,000
seconds), which is handy in scripts.
Internal PKI and certificate inventory
Many organizations run their own CA for things the public internet never sees: internal web applications, Wi-Fi with 802.1X and certificate-based authentication (EAP-TLS, see 802.1X), VPN clients, domain controllers, device identities in mobile device management (MDM), and machines in operational technology networks. On Windows networks this is often Active Directory Certificate Services (AD CS). The internal root is distributed to company devices through Group Policy or MDM.
The same rules apply as for public CAs, on a smaller scale:
- Keep the root CA offline and use it only to sign intermediates; protect CA keys, ideally in an HSM.
- Restrict who may request which kind of certificate. Overly permissive certificate templates are a known path to privilege escalation in Windows domains, so review them like any other privileged access.
- Publish revocation information, and document every step.
Monitoring expiry
An expired certificate is one of the most common — and most avoidable — causes of outages: a payment page, a VPN or an API between two companies stops working at midnight. The fix is an inventory of every certificate (name, issuer, owner, expiry, where it is installed) and an alert well before each expiry date. Commercial certificate management tools and monitoring systems do this at scale; the logic is simple. This example uses a fixed "today" so the output never changes:
from datetime import date
today = date(2026, 10, 1) # fixed "today" so the output never changes
inventory = [
("www.example.com", "Public CA", date(2027, 1, 14)),
("mail.example.com", "Public CA", date(2026, 10, 19)),
("vpn.example.com", "Public CA", date(2026, 9, 28)),
("intranet.corp.example", "Internal CA", date(2026, 10, 9)),
("scada-hmi.corp.example", "Internal CA", date(2027, 6, 30)),
]
def status(days_left):
if days_left < 0:
return "EXPIRED"
if days_left <= 14:
return "RENEW NOW"
if days_left <= 30:
return "renew soon"
return "ok"
rows = []
for name, issuer, not_after in inventory:
days_left = (not_after - today).days
rows.append((days_left, name, issuer))
for days_left, name, issuer in sorted(rows):
print(f"{name:<24} {issuer:<12} {days_left:>5} days {status(days_left)}")
attention = sum(1 for days_left, _, _ in rows if days_left <= 30)
print(f"{attention} of {len(rows)} certificates need attention")
vpn.example.com Public CA -3 days EXPIRED intranet.corp.example Internal CA 8 days RENEW NOW mail.example.com Public CA 18 days renew soon www.example.com Public CA 105 days ok scada-hmi.corp.example Internal CA 272 days ok 3 of 5 certificates need attention
Sorting by days left puts the urgent work at the top. In real life the expiry dates come from scanning your own endpoints or from the CA's records, and the alert goes to the team that owns each certificate — an alert that reaches nobody is no alert. Public CAs also log every certificate they issue in Certificate Transparency logs, which you can search for your own domains to find certificates you did not know about.