Understanding X.509
The certificate format that runs the internet.
What's inside a TLS certificate, why the format is ASN.1 instead of JSON, and the extensions worth knowing about when something breaks.
A signed document.
An X.509 certificate is a structured document: subject name, public key, issuer name, validity period, extensions. The whole document is signed by the issuer's private key. Anyone with the issuer's public key (root CA, or an intermediate the root signed) can verify the signature, and therefore trust that the public key inside the certificate really belongs to the subject. That's the whole PKI story in one sentence.
ASN.1 and DER.
The format is ASN.1 (Abstract Syntax Notation One), serialised as DER (Distinguished Encoding Rules). ASN.1 dates to 1984; it's a strict, deterministic binary format with a tag-length-value structure. PEM is the ASCII-armored form (-----BEGIN CERTIFICATE----- with base64-encoded DER inside). Why not JSON? Because the signature has to be byte-for-byte deterministic — JSON's whitespace and key ordering would make signing brittle. ASN.1 is awful to read but stable.
The standard fields.
Version (currently 3). Serial number (unique per CA). Signature algorithm. Issuer DN (Distinguished Name — country, org, CN). Validity (notBefore, notAfter). Subject DN. SubjectPublicKeyInfo (the public key plus algorithm). Then the extensions block — and almost everything interesting lives there.
The important extensions.
SubjectAlternativeName (SAN): the list of DNS names the certificate is valid for — modern browsers ignore CN entirely and only check SAN. KeyUsage / ExtendedKeyUsage: allowed operations — signing, key encipherment, TLS server auth, code signing. BasicConstraints: whether the certificate can sign others (CA bit). CRLDistributionPoints and AuthorityInformationAccess: where to find revocation info. SignedCertificateTimestamp: CT-log proof that the cert was published.
A worked decode.
A typical 2048-bit RSA cert is ~1.5 KB DER. Decoded: serial 0x04a1b2c3..., signature algorithm sha256WithRSAEncryption, issuer "CN=Let's Encrypt R3", validity 90 days, subject CN=example.com, SAN: example.com, www.example.com, public key 2048-bit RSA modulus. Extension: KeyUsage = digitalSignature, keyEncipherment; ExtendedKeyUsage = serverAuth, clientAuth; SCTs from two CT logs. The whole thing rendered as text fits on one screen.
Let's Encrypt cert
DER → text
Decode the fields, find the SAN list.
version + serial + sigAlg + issuer + validity + subject + key + extensions
= ~1.5 KB DER, 90 days valid
Chain assembly.
A real TLS connection sends a chain, not a single cert. example.com's leaf is signed by "Let's Encrypt R3", which is signed by "ISRG Root X1", which is in your OS trust store. The server has to send the leaf plus all intermediates (but not the root). Misconfiguring this is the #1 reason "the certificate works in Chrome but not curl" — Chrome has aggressive intermediate-fetching; curl doesn't. Test withopenssl s_client -showcerts.