X.509 and ASN.1

seed

Canonical digital certificate format. RFC 5280 (Internet X.509 PKI Certificate and CRL Profile) is the reference. Everything involving TLS, S/MIME, code signing, ICP-Brasil derives from here.

Structure of an X.509 v3 cert

Certificate  ::=  SEQUENCE  {
    tbsCertificate       TBSCertificate,        -- "to be signed"
    signatureAlgorithm   AlgorithmIdentifier,
    signatureValue       BIT STRING
}

TBSCertificate  ::=  SEQUENCE  {
    version              [0] EXPLICIT Version DEFAULT v1,
    serialNumber         CertificateSerialNumber,
    signature            AlgorithmIdentifier,
    issuer               Name,
    validity             Validity,         -- notBefore + notAfter
    subject              Name,
    subjectPublicKeyInfo SubjectPublicKeyInfo,
    extensions           [3] EXPLICIT Extensions OPTIONAL
}

Encoding: ASN.1 DER (Distinguished Encoding Rules) — canonical binary. PEM = base64 of DER with -----BEGIN CERTIFICATE----- envelope. Inspection: openssl x509 -in cert.pem -text -noout.

Critical extensions (v3)

Extension OID What it's for
BasicConstraints 2.5.29.19 CA:TRUE/FALSE + pathLenConstraint. Distinguishes a CA from an end-entity. A CA forged by an end-entity was the basis of several historical attacks
SubjectAltName (SAN) 2.5.29.17 DNS names, IPs, emails. The subject CN has been deprecated for hostname matching since 2017 (CA/B BR). Browsers only look at the SAN
KeyUsage 2.5.29.15 digitalSignature, keyEncipherment, keyCertSign, … restricts operations
ExtKeyUsage (EKU) 2.5.29.37 serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, OCSPSigning
CRLDistributionPoints 2.5.29.31 Where to fetch the CRL for revocation
AuthorityInfoAccess (AIA) 1.3.6.1.5.5.7.1.1 OCSP responder URLs + issuer cert
CertificatePolicies 2.5.29.32 Policy OIDs (e.g., ICP-Brasil A1A3A4 have specific OIDs)
SCTList 1.3.6.1.4.1.11129.2.4.2 Embedding of Signed Certificate Timestamps (CT logs)
NameConstraints 2.5.29.30 Restricts the domains a sub-CA may issue for (CRITICAL in delegated sub-CAs, ignored in the field)

Trust path validation

RFC 5280 §6 defines the algorithm. Summary:

  1. Chain: end-entity → intermediate(s) → root. The root is the trust anchor (already trusted by configuration).
  2. Each (cert, issuer) pair checks: valid signature (issuer.publicKey verifies cert.signatureValue), notBefore ≤ now ≤ notAfter, compatible BasicConstraintsKeyUsageEKU, not revoked (OCSPCRLCRLite/embedded SCTs).
  3. Common pitfall: confusing "it's in the chain sent by the server" with "it's in the trust store". The TLS server sends the chain up to (and excluding) the root; the client splices in its local root.

Operational pitfalls

  • CN ≠ SAN: never rely on the CN. A modern cert without a SAN is invalid for hostname binding.
  • Long validity: the public maximum in 2026 is 398 days (CA/B BR §6.3.2). Roadmap heading toward 90 days (Apple proposal 2024) and 47 days (Google proposal).
  • Signature algorithm: SHA-1 banned in public CAs since 2017. A cert with sha1WithRSAEncryption in production is a red flag.
  • Cross-sign: the same subject+key can have multiple certs issued by different issuers — useful for root transition, see 06-ssl-and-tls/05-creating-a-letsencrypt-style-ca.kmd §Cross-signing.
  • Path building: given a pool of intermediates, finding the right chain is NP-hard in the worst case; serious libs have heuristics. Real failures occur when intermediates are missing.

ASN.1 — enough to survive

ASN.1 = abstract type description language. DER = canonical binary encoding (TLV: Tag + Length + Value).

  • SEQUENCE = ordered struct
  • SET = unordered struct
  • OCTET STRING = bytes
  • BIT STRING = bits (with a padding offset)
  • OBJECT IDENTIFIER (OID) = hierarchical type identifier (1.2.840.113549.1.1.11 = sha256WithRSAEncryption)
  • UTCTime (YYMMDDHHMMSSZ, valid until 2049) vs GeneralizedTime (YYYYMMDDHHMMSSZ)

Tooling to inspect: openssl asn1parse, dumpasn1, lapo.it/asn1js (browser).

Canonical libs

  • Go: crypto/x509 (stdlib) — complete and ergonomic
  • Rust: rustls-webpki (path validation), x509-parser (parsing)
  • Dart: package:basic_utils (limited parsing; for heavy ops, FFI bridge)
  • Python: cryptography.x509

References