PKI & TLS
X.509 Certificates
Reading, issuing, and understanding certificates.
X.509 is the certificate format that the entire web runs on, yet most engineers only ever see it as an opaque .pem blob that either works or throws an error. Learning to read one turns TLS debugging from guesswork into a two-minute inspection. This page dissects the structure, shows how to inspect real certificates, and explains when a self-signed certificate is fine and when you need a CA.
Why This Matters
When a connection fails with certificate verify failed, the answer is almost always visible in the certificate itself: the name does not match, it expired, an extension is missing, or the chain is broken. If you can read the fields, you can diagnose it. If you cannot, you are reduced to disabling verification, which trades a debugging problem for a security hole.
Certificate Anatomy
An X.509 certificate is a structured record signed by the issuer. The fields you care about most:
| Field | What it holds | Why it matters |
|---|---|---|
| Subject | The identity (historically CN) | Legacy name of the entity |
| Subject Alternative Name (SAN) | DNS names, IPs, emails | The names browsers actually check |
| Issuer | Who signed this certificate | Points to the next link in the chain |
| Validity (notBefore/notAfter) | Time window | Outside it, the cert is invalid |
| Public Key | The subject’s public key | What the private key must match |
| Serial Number | Unique per issuer | Used by revocation lists |
| Key Usage / Extended Key Usage | Allowed operations | Restricts to serverAuth, clientAuth, etc. |
| Basic Constraints | CA true/false | Whether this cert may sign others |
| Signature | Issuer’s signature over the above | The cryptographic binding |
The single most important modern fact: browsers ignore the Common Name and validate hostnames against the SAN extension only. A certificate with the right CN but no matching SAN will be rejected.
Inspecting Real Certificates
Fetch and decode a live certificate end to end.
cd ~/crypto-lab
openssl s_client -connect wikipedia.org:443 -servername wikipedia.org </dev/null 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
Expected output:
X509v3 Subject Alternative Name:
DNS:*.wikipedia.org, DNS:wikipedia.org, DNS:*.m.wikipedia.org, DNS:*.wikimedia.org
To see the full breakdown of any PEM file on disk:
openssl x509 -in cert.pem -noout -text | head -20
Expected output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
0a:1b:2c:3d:4e:5f:60:71:82:93:a4:b5:c6:d7:e8:f9
Signature Algorithm: ecdsa-with-SHA384
Issuer: C=US, O=DigiCert Inc, CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1
Validity
Not Before: Jan 15 00:00:00 2026 GMT
Not After : Jan 15 23:59:59 2027 GMT
Subject: CN=*.wikipedia.org
Self-Signed vs CA-Signed
A self-signed certificate is one where subject and issuer are identical: the key vouches for itself. It is cryptographically valid but nothing external trusts it, so browsers warn.
| Situation | Use |
|---|---|
| Public website | CA-signed (Let’s Encrypt or commercial) |
| Internal service behind your own CA | CA-signed by your private CA |
| Local development, throwaway test | Self-signed is fine |
| Encrypting a link where both ends are pinned to a known key | Self-signed with pinning is acceptable |
Create a self-signed certificate for a lab in one command.
cd ~/crypto-lab
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
-keyout dev.key -out dev.crt -days 30 -nodes \
-subj "/CN=dev.local" -addext "subjectAltName=DNS:dev.local"
openssl x509 -in dev.crt -noout -subject -issuer
Expected output:
subject=CN=dev.local
issuer=CN=dev.local
Subject equals issuer confirms it is self-signed. For anything shared or public, sign with a CA instead, covered in the sibling page Certificate Authorities. Once you understand the fields here, the sibling TLS Handshake page shows how the certificate is actually presented and checked on the wire, and Mutual TLS applies the same anatomy to client certificates.
Practical Guidance
- Always put every hostname in the SAN extension; a certificate without a matching SAN entry fails validation regardless of the CN.
- When debugging TLS, run
openssl x509 -noout -textfirst; the fault is usually a visible name mismatch, expiry, or missing extension. - Use elliptic curve keys (P-256) for new certificates: smaller, faster, and as strong as RSA-3072.
- Set short validity for leaf certificates and automate renewal; long-lived leaf certs are an operational and security liability.
- Reserve self-signed certificates for local development or key-pinned links; anything with real users should chain to a trusted CA.