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:

FieldWhat it holdsWhy it matters
SubjectThe identity (historically CN)Legacy name of the entity
Subject Alternative Name (SAN)DNS names, IPs, emailsThe names browsers actually check
IssuerWho signed this certificatePoints to the next link in the chain
Validity (notBefore/notAfter)Time windowOutside it, the cert is invalid
Public KeyThe subject’s public keyWhat the private key must match
Serial NumberUnique per issuerUsed by revocation lists
Key Usage / Extended Key UsageAllowed operationsRestricts to serverAuth, clientAuth, etc.
Basic ConstraintsCA true/falseWhether this cert may sign others
SignatureIssuer’s signature over the aboveThe 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.

SituationUse
Public websiteCA-signed (Let’s Encrypt or commercial)
Internal service behind your own CACA-signed by your private CA
Local development, throwaway testSelf-signed is fine
Encrypting a link where both ends are pinned to a known keySelf-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

  1. Always put every hostname in the SAN extension; a certificate without a matching SAN entry fails validation regardless of the CN.
  2. When debugging TLS, run openssl x509 -noout -text first; the fault is usually a visible name mismatch, expiry, or missing extension.
  3. Use elliptic curve keys (P-256) for new certificates: smaller, faster, and as strong as RSA-3072.
  4. Set short validity for leaf certificates and automate renewal; long-lived leaf certs are an operational and security liability.
  5. Reserve self-signed certificates for local development or key-pinned links; anything with real users should chain to a trusted CA.