tlssslcertificatescsrx509security

How to Read a CSR and an X.509 Certificate

· Cosyslabs

A CSR contains the subject distinguished name, the public key, and a signature made with the matching private key. A certificate authority keeps those, adds an issuer name, serial number, validity window and its own signature, and returns an X.509 certificate. Browsers match hostnames using subject alternative names only.

What a CSR is

A certificate signing request is a PKCS#10 structure — defined in RFC 2986 — that you generate alongside a key pair and send to a CA. It is DER-encoded ASN.1, usually wrapped in a -----BEGIN CERTIFICATE REQUEST----- PEM block. Three things are inside, and only three matter:

The subject distinguished name. Attribute-value pairs — C country, ST state, L locality, O organization, OU organizational unit, CN common name. Historically the CN held the hostname; it no longer does the job people assume it does.

The public key. The algorithm (RSA, ECDSA, Ed25519), its size or named curve, and for RSA the modulus and exponent. The matching private key never appears in the CSR and must never leave the machine that generated it.

A signature over the whole request, made with that private key. This is proof of possession: whoever produced the request controls the private key matching the public key inside it. It is not proof that you control the domain, and a CA does not treat it as such.

A fourth section, attributes, is where extension requests live — subject alternative names, key usage and basic constraints travel here. Two others turn up occasionally, challengePassword and unstructuredName; neither means anything to a browser.

What the CA adds

The CA validates domain control by its own means — an HTTP or DNS challenge, an email to an approved address — then builds an X.509 certificate (RFC 5280). The public key crosses over from the CSR; almost everything else is new:

FieldComes fromNotes
VersionCAv3 is the only useful value; v1 cannot carry extensions at all
Serial numberCAUnique per issuer; carries required entropy
Signature algorithmCAAppears twice — inside and outside the signed body, and the two must agree
IssuerCAThe DN of the certificate that signed this one
ValidityCAnotBefore and notAfter; public TLS certificates are capped well under a year
SubjectCSR, editedThe CA may drop or rewrite fields it did not validate
Subject public key infoCSRCopied verbatim
ExtensionsMostly CASANs, key usage, EKU, AIA, CRL distribution points, SCTs
SignatureCAOver the encoded tbsCertificate, using the issuer's private key

The signature is the point. A certificate asserts "this public key belongs to this name", and the CA's signature is what makes that assertion checkable by anyone holding the issuer's public key.

Reading the fields

Decoded, a server certificate looks roughly like this:

Version:            v3
Serial number:      04:AF:23:9E:...
Signature:          sha256WithRSAEncryption
Issuer:             C=US, O=Example CA, CN=Example CA R3
Validity:           2026-06-01 → 2026-08-30
Subject:            CN=example.com
Public key:         RSA (rsaEncryption), 2048 bits, exponent 65537
SANs:               DNS:example.com, DNS:www.example.com
basicConstraints:   CA:FALSE                (critical)
keyUsage:           digitalSignature, keyEncipherment   (critical)
extKeyUsage:        serverAuth, clientAuth

What to look at, in order of how often it is the problem:

  • Subject alternative names. The hostnames this certificate covers. If the name in the address bar is not here, nothing else matters.
  • Validity. Both timestamps are UTC. Watch for a notBefore in the future — a certificate issued by a host with a skewed clock fails until real time catches up.
  • Public key size. RSA under 2048 bits is rejected by public CAs and browsers. EC keys are stated as a named curve; P-256 beats RSA-2048 at a fraction of the size.
  • Signature algorithm. Anything with sha1 or md5 in the name is dead.
  • basicConstraints. CA:TRUE on a certificate a server presents as its leaf means the bundle is wrong.
  • extKeyUsage. A TLS server certificate needs serverAuth. One issued for codeSigning will not work for HTTPS however correct the rest looks.

An encoding detail for reading dates by hand: RFC 5280 requires UTCTime through 2049 and GeneralizedTime from 2050. UTCTime has a two-digit year where 50 or above means 19xx, so 490101000000Z is 2049 and 500101000000Z is 1950.

Browsers ignore the Common Name

This is the most consequential thing to know about reading a certificate, and it is not visible from the field names. The Common Name was originally where the hostname went. RFC 2818 deprecated that in 2000, RFC 6125 formalized the replacement in 2011, and Chrome removed the CN fallback outright in version 58 (2017). Other browsers followed. The CA/Browser Forum Baseline Requirements now require the subjectAltName extension on every publicly trusted TLS certificate.

The practical consequence: a certificate with CN=example.com and no SAN extension matches no hostname at all. Chrome reports NET::ERR_CERT_COMMON_NAME_INVALID on a certificate whose CN reads exactly right — a confusing error if you do not know the CN is never consulted.

Where this bites depends on who signs the request. Public CAs build the SAN list from the names on the order, so a CSR missing SANs usually still yields a working certificate; the CN in your CSR is largely decorative. Private and internal CAs are where certificates get broken. openssl x509 -req discards the CSR's extensions unless you pass -copy_extensions copy (OpenSSL 3.0 and later) or supply them with -extfile; openssl ca needs copy_extensions = copy in its config; Microsoft AD CS takes extensions from the certificate template, not the request. In all three the default is to silently drop your SANs and issue a certificate no browser will accept. So when you decode a CSR headed for an internal CA and see no SAN entries, that is the finding — regenerate the request rather than discovering it after issuance.

Expired is not the same as untrusted

Two failures that look similar in a browser and have nothing in common.

Expired means notAfter is in the past. The certificate is well-formed, the chain may be perfect, the signature still verifies — it is simply outside its stated window. Chrome shows NET::ERR_CERT_DATE_INVALID. The check is relative to the client's clock, so a device with the wrong date produces the same error against a perfectly current certificate. "Check the system time" is a real first step.

Untrusted means the chain does not terminate at a certificate in the client's trust store. Chrome shows NET::ERR_CERT_AUTHORITY_INVALID, and the certificate may have years left. Usual causes: a self-signed certificate that terminates at itself, a private CA whose root is not installed, or a server not sending an intermediate. That last one is deceptive — browsers cache intermediates from previous sites and some fetch a missing one via the AIA extension, so an incomplete chain often works in Chrome on your laptop and fails in curl, in Java, and on every mobile device that has never seen that intermediate.

The fixes are unrelated. Expiry is fixed by renewing; trust is fixed by fixing the bundle, or by installing a root — and if the answer is "install a root", you are running a private PKI and should know it.

Chain order, and what a chain check does not tell you

A server sends a list of certificates, ordered from the leaf outward:

1. Leaf          Subject: CN=example.com          Issuer: CN=Example CA R3
2. Intermediate  Subject: CN=Example CA R3        Issuer: CN=Example Root X1
   (root usually omitted)

Each certificate's issuer must be the next certificate's subject. RFC 8446 requires the leaf to come first and says each following certificate should certify the one before it; earlier TLS versions required strict ordering throughout. Some clients tolerate a shuffled list, many do not, and concatenating files in alphabetical order is a common way to get one wrong. The root is normally left out — a client that trusts it already has it, and one that does not will not start trusting it because it arrived over the wire.

Now the caveat that matters most. Comparing an issuer DN against a subject DN is a name comparison, not signature verification. Anyone can mint a certificate claiming any issuer name they like; the names will line up and the cryptography will not. Real validation uses the issuer's public key to verify the child's signature, then checks revocation and trust anchors. Read "the chain is ordered correctly" as "the names line up in the right sequence" — it catches the bundling mistakes people actually make and proves nothing about authenticity. Run openssl verify -untrusted intermediate.pem leaf.pem for the cryptographic answer.

Try it now

Decode a request or a certificate with the CSR & Certificate Decoder. Paste the PEM block and it prints the subject fields, key algorithm and size, subject alternative names, signature algorithm and — for certificates — issuer, serial number and validity dates, with warnings for a missing SAN, an undersized RSA key or a SHA-1 signature. Parsing runs in your browser, which matters: a CSR states the identity you are about to have signed.

For the wider job — inspecting a whole chain and its ordering, converting between PEM and DER, splitting a bundle into individual certificates, listing what is inside a PKCS#12 or Java JKS keystore, or generating a key pair with a matching CSR — use the Certificate & Keystore Toolkit.

Related: MD5 vs SHA-1 vs SHA-256 on why the signature algorithm matters, the RSA and SHA-256 glossary entries, the SSH Key Generator for the other half of your server access, and the Hash Generator for checking a fingerprint against one you were given.

Summary

  • A CSR holds a subject DN, a public key, and a self-signature proving possession of the private key
  • Proof of possession is not proof of domain control; the CA validates that separately
  • The CA adds issuer, serial number, validity, extensions and its own signature
  • Browsers ignore the Common Name entirely — subject alternative names match the hostname
  • Private CAs drop CSR extensions by default, which is where SAN-less certificates come from
  • Expired means the clock; untrusted means the chain — unrelated fixes
  • A chain runs leaf → intermediate, and comparing DNs is not signature verification

More tools from Cosyslabs

  • PDF Convert All — Convert, merge and compress PDFs; document signing rests on the same X.509 certificates and chain-of-trust model as TLS.
  • Routine Toolkit — Everyday utilities including a date difference calculator, useful for working out how many days a validity window leaves you.
  • Rough Estimator — Estimate the effort of rolling out an internal PKI or moving a fleet to automated certificate renewal.
  • Cosyslabs — The studio behind Dev Tools !, Unit Convert All, Astrilio, CastFleet, and more.