Certificates look opaque because they're wearing three layers: ASN.1 (the structure), DER (the binary encoding), and PEM (the base64 armor). Peel them in order and every field you care about — expiry, SANs, key size, who signed it — is sitting at a predictable offset in a tagged tree. Here's the full walkthrough, plus the openssl one-liners for people who want the same answers from a terminal.
ASN.1 is a notation for structured data: values have types (INTEGER, OID, SEQUENCE, time, strings), and types nest. X.509 certificates are defined in ASN.1 — a certificate is a SEQUENCE containing a TBSCertificate ("to be signed"), the signature algorithm, and the signature.
DER is one way to serialize ASN.1 to bytes, chosen because it's deterministic: every value is written as a tag byte (its type), a length, and the content. 30 82 04 F3, the first four bytes of almost every certificate you'll meet, is literally "SEQUENCE, 1267 bytes, long-form length." That regularity is why a decoder can walk the structure without knowing anything about certificates.
PEM takes those DER bytes, base64-encodes them, and wraps them in BEGIN/END lines so they survive email and terminals. One PEM file can hold several blocks — a server cert plus its intermediates — which decode one at a time, in order.
It walks the DER tree and reads the certificate's fixed shape: version (a [0]-tagged wrapper, absent means v1), serial (INTEGER), signature algorithm (an OID like 1.2.840.113549.1.1.11 — sha256WithRSAEncryption), issuer name, validity (two times), subject name, public key info, and — in v3 — the extensions. Names are SEQUENCEs of RDNs, each a SET of type-value pairs like CN=api.example.com. The public key lives inside a BIT STRING: RSA is two INTEGERs (modulus and exponent, with the key size read from the modulus's bit length), EC is a curve OID plus a point, Ed25519 a raw 32-byte key.
| Field | What to look for | openssl |
|---|---|---|
| Validity | notBefore in the past, notAfter comfortably ahead; 90-day certs should renew at ~day 60 | openssl x509 -noout -dates |
| SANs | Every hostname and IP the cert serves, or the handshake fails | openssl x509 -noout -ext subjectAltName |
| Key size | RSA 2048+; ECDSA P-256/P-384; nothing at 1024 | openssl x509 -noout -text | grep Public-Key |
| Issuer | Who signed it — matches the intermediate above it in the chain | openssl x509 -noout -issuer |
| basicConstraints | CA:FALSE on leaf certs; path length on CAs | openssl x509 -noout -text | grep -A1 Basic |
| Signature algorithm | sha256WithRSAEncryption or ecdsa-with-SHA256+; SHA-1 is dead | openssl x509 -noout -text | grep Signature |
Two DER quirks account for most head-scratching. First, DER integers are signed, so a positive value whose first byte has the high bit set gets a padding 0x00 — that's why serials are shown as 16-to-20-byte hex blobs that sometimes start with 00. Second, public key material rides inside BIT STRINGs, which spend their first content byte saying how many trailing bits are unused (usually 0) — decoders strip it before parsing what's inside.
Object identifiers deserve their own sentence: an OID like 1.2.840.113549.1.1.11 encodes as base-128 bytes where the first value packs the first two arcs (1.2 → 42 → 0x2A). The decoder expands them back to dotted form and, when it knows the OID, prints the name — which is how "sha256WithRSAEncryption" appears where the raw bytes said 2A 86 48 86 F7 0D 01 01 0B.
A certificate signing request is the same trick in a smaller package: PKCS#10 wraps the requested subject and public key with a signature proving you hold the key. The field worth checking is the requested SAN extension — CAs copy the CSR's SANs into the cert (the CN alone is not enough), so a missing SAN in the CSR means a cert that won't validate. The challengePassword attribute, when present, is also visible in the structure.
The same decoder logic reads key files — that's by design, not accident. RSA PRIVATE KEY is PKCS#1 (version, modulus, exponent, private components), EC PRIVATE KEY is SEC 1, PRIVATE KEY is the algorithm-agnostic PKCS#8 wrapper, and ENCRYPTED PRIVATE KEY is PKCS#8 with a cipher OID (PBES2 → usually aes-256-cbc with PBKDF2) where the payload stays encrypted. If you paste a key into a decoder, use one that runs entirely in the page — our certificate decoder never transmits what you paste and never shows private exponents in copyable form. For the surrounding toolkit, the JWT decoder unwraps the other certificate-adjacent blob you'll meet (JOSE headers carry the same algorithm OIDs), and the base64 encoder is handy for the armor layer itself.
Paste any PEM — cert, CSR, or key — and get the full ASN.1 tree plus subject, validity, SANs, key size, and signature algorithm. Free, private, in-browser.
Certificate Decoder →Tag, length, value — repeated recursively. Once you see that rhythm in the hex, a certificate stops being a mystery blob and becomes a readable document with an expiry date, a hostname list, and a key you can size up. Decode before you deploy, and the worst outages (the expired-cert-on-Saturday kind) never happen.
Base64-encoded DER bytes between BEGIN/END markers. Strip the markers and the base64 armor, and what's left is a stream of tag-length-value records — the ASN.1 structure of a certificate, CSR, or key. Multiple BEGIN/END blocks in one file (a chain) are just multiple structures stacked in order.
Decode it and read notAfter in the Validity section — certificates carry both notBefore and notAfter as UTC dates. With openssl: openssl x509 -in cert.pem -noout -enddate. Renew at roughly two-thirds of the lifetime; for a 90-day Let's Encrypt certificate that means starting renewal around day 60, which is exactly what certbot's auto-renewal timer does.
DER integers are signed, so a positive number whose top bit is set gets a leading 0x00 byte to keep it positive, and serials are random 16-to-20-byte values. Decoders show the hex — sometimes with that padding byte — which is why the same serial can appear as 40 or 42 hex characters depending on who's printing it.
The Subject Alternative Name extension (OID 2.5.29.17) lists every DNS name, IP address, and email the certificate is valid for. Browsers since about 2017 validate against SANs and ignore the commonName entirely — a cert with CN=example.com but no matching SAN fails. Before shipping a cert, check that every hostname and IP it serves appears in the SAN list.