Decoding a CSR: Reading SAN & Subject Fields
How a PKCS#10 Certificate Signing Request is actually structured at the ASN.1 level, how to read openssl req -text output line by line, and why the SAN extension — not the Common Name — is the field that actually governs which hostnames a certificate protects.
What a CSR Actually Contains, Structurally
A PKCS#10 CSR — formally defined in RFC 2986 — is a single ASN.1 SEQUENCE built from exactly three parts, in a fixed order that never changes regardless of which fields you filled in when it was generated. The first and largest part is certificationRequestInfo, itself a nested SEQUENCE holding a version number, the subject distinguished name, the public key, and an optional attributes field. The second part, signatureAlgorithm, names the exact algorithm and hash used to sign everything in the first part. The third, signature, is the raw signature bytes computed over the DER encoding of certificationRequestInfo specifically — not over the whole CSR, which matters because it means the signature can be checked before you even look at what algorithm signed it. Understanding this three-part shape is the key to reading any decoded output sensibly: everything you'll see printed by a decoder maps onto one of these three buckets, and knowing which bucket a field belongs to tells you whether it was something the requester chose (inside CertificationRequestInfo) or something imposed by the signing process itself.
Reading Real Output: openssl req -text, Field by Field
Running openssl req -in file.csr -noout -text against any CSR prints a human-readable rendering of the exact ASN.1 structure above. The first line, Version: 1 (0x0), looks like it should mean something configurable, but it's fixed — PKCS#10's only defined version integer value is 0, which OpenSSL confusingly labels "Version: 1" in its display (a historical off-by-one in the tool's own text, not in the standard). Next comes Subject:, listing whichever of C, ST, L, O, OU, CN, and emailAddress were populated, in the order they appear in the underlying RDN sequence. Below that, Subject Public Key Info shows the key algorithm and either the RSA modulus/exponent or the ECDSA curve name and point. If any extensions were requested, an Attributes: block follows, almost always containing Requested Extensions: with the SAN list nested inside. Finally, Signature Algorithm: and the raw signature bytes close out the printout, corresponding directly to the second and third top-level ASN.1 fields described above.
The Subject Field's X.500 Origins
The C, ST, L, O, OU, and CN abbreviations predate SSL/TLS entirely — they come from X.500, an ITU-T directory service standard from the 1980s designed to organize entries in a hierarchical, postal-address-like naming system for large institutional directories. Country, State/Province, Locality, Organization, and Organizational Unit map directly onto X.500's idea of narrowing down through a physical and organizational hierarchy before finally naming a specific entity with a Common Name. When SSL certificates borrowed X.509 (X.500's certificate format) in the 1990s, they inherited this entire naming scheme wholesale, which is why a field designed to identify an entry in a corporate directory ended up being reused to name a website's domain. This history explains a few things that otherwise look arbitrary: why Country is capped at exactly two characters (it's an ISO 3166-1 code, a convention layered on top of X.500 rather than part of it), and why Organizational Unit exists as a separate field from Organization at all (X.500 directories commonly nested departments under a parent company entry).
PrintableString vs UTF8String vs teletexString
Every string-valued field in the subject is DER-encoded with an explicit ASN.1 type tag, and that tag choice is visible if you look at the raw bytes rather than OpenSSL's already-decoded text. PrintableString (ASN.1 tag 0x13) is restricted to uppercase and lowercase Latin letters, digits, spaces, and a small fixed set of punctuation marks — no accented characters, no non-Latin scripts. UTF8String (tag 0x0C) allows the entire Unicode range and is what essentially all current tooling uses for Organization, Organizational Unit, and Common Name, since real company names frequently need characters PrintableString can't represent. Country is the one field convention still typically encodes as PrintableString, since two-letter ISO codes never need anything outside its character set anyway. A third, older type, teletexString (tag 0x14), shows up occasionally in CSRs generated by legacy X.500-lineage software from the 1990s and early 2000s; most current CA parsers reject it outright, which is why a CSR decoded and found to contain a teletexString-tagged field is worth flagging as a likely source of validation failure regardless of what the actual text content says.
SAN's GeneralName Choice: More Than Just Domains
The Subject Alternative Name extension, defined in RFC 5280, is a SEQUENCE OF GeneralName, and GeneralName is itself an ASN.1 CHOICE type supporting several distinct kinds of identifier, each with its own context-specific tag number: dNSName [2] for hostnames, iPAddress [7] for a raw IP address, rfc822Name [1] for an email address, and uniformResourceIdentifier [6] for a URI, among a few less common choices like otherName and directoryName. Public web server certificates use dNSName almost exclusively, which is why every decoder you'll encounter shows SAN output as a simple list of domain strings — but the underlying extension format is genuinely more general than that narrow usage suggests, and other certificate use cases (S/MIME email certificates, IPsec/IKE peer certificates) lean on the other GeneralName choices routinely.
Why Browsers Read SAN and Ignore CN for Hostname Matching
This wasn't always true. Early implementations of hostname verification fell back to Common Name when no SAN extension was present at all, and for years CAs issued certificates where CN alone was considered sufficient. That changed formally with the CA/Browser Forum's baseline requirements, and the practical enforcement deadline that mattered most was Chrome's 2017 rollout of strict SAN-only matching (Chrome 58), which every other major browser followed shortly after. The result: a certificate whose only name lives in CN, with no matching SAN entry, now fails hostname verification in every current browser — not a warning, an outright connection failure. This is precisely why the CSR generation and decoding tools on this site treat SAN as the authoritative name list and CN as effectively a legacy label kept for backward compatibility and CA record-keeping, not a security-relevant field.
Reading the Public Key Section Correctly
For an RSA key, the decoded output shows the full modulus in hex (a number whose bit length directly determines the "2048-bit" or "4096-bit" figure you see quoted elsewhere) and the public exponent, which is essentially always 65537 (0x10001) in any modern-generated key — a value chosen for computational efficiency during signature verification, not for any security property specific to that number. For ECDSA, there's no modulus at all; instead you'll see a named curve identifier (P-256, P-384, or occasionally P-521) and an encoded elliptic curve point representing the public key. A useful sanity check when decoding a CSR you didn't generate yourself: the stated key size or curve should match whatever policy the destination CA publishes as a minimum, since a CSR carrying a key below policy will be rejected regardless of how clean every other field looks.
The Attributes Field and the extensionRequest OID
SAN doesn't live directly inside CertificationRequestInfo as its own top-level field — PKCS#10's original 1990s design predates X.509v3 extensions and has no dedicated slot for them. Instead, extensions are smuggled in through the generic attributes field using a specific, well-known attribute type: extensionRequest, identified by OID 1.2.840.113549.1.9.14, whose value is a standard X.509 Extensions SEQUENCE — the exact same structure a finished certificate uses to carry SAN, key usage, and other extensions. This is why decoded output labels the section "Requested Extensions" rather than simply "Extensions": it's a CSR politely asking the CA to carry these extensions forward into the issued certificate, not a guarantee that it will, since the CA independently decides what actually ends up in the final cert regardless of what was requested.
A Byte-Level Walkthrough of DER Tag-Length-Value
Every ASN.1 DER element follows the same Tag-Length-Value pattern, and seeing one concretely demystifies the rest. Take a Common Name of "example.com" encoded as UTF8String: the bytes are 0C 0B 65 78 61 6D 70 6C 65 2E 63 6F 6D. 0C is the tag for UTF8String. 0B is the length — 11 bytes, matching "example.com"'s 11 characters. The remaining 11 bytes are the ASCII value itself. That single element then sits nested inside a SEQUENCE (tag 30) pairing it with the Common Name's OID 2.5.4.3, which itself sits inside a SET (tag 31, since X.500 technically allows multiple values per RDN even though CSRs virtually never use that), which sits inside the outer subject SEQUENCE alongside every other RDN. Every field in a decoded CSR, no matter how deeply nested it looks in a text printout, unpacks to this same tag-length-value pattern at the byte level — there's no separate parsing logic for "special" fields, just consistent recursive application of the same rule.
Comparing Ways to Decode a CSR
| Method | Shows Raw ASN.1 Tags? | Verifies Signature? | Setup Needed |
|---|---|---|---|
| openssl req -text | No (pre-decoded, human-readable) | No (use -verify separately) | OpenSSL installed locally |
| openssl asn1parse | Yes, full tag/length breakdown | No | OpenSSL installed locally |
| Browser-based CSR decoder | No (pre-decoded, human-readable) | Not typically | None — runs in-browser |
| Manual hex/byte inspection | Yes, full control | No, manual only | Hex editor, ASN.1 reference |
For everyday work — confirming a CSR's SAN list and subject before submitting it — a human-readable decoder is almost always the right tool, since raw tag/length output is genuinely useful only when debugging a malformed or unusually-encoded CSR that a normal decoder is choking on.
Encoding Mistakes That Cause Silent CA Rejections
A handful of specific encoding choices, all invisible if you only glance at a decoded field's text value, account for most CSR rejections that aren't about the actual content of the names requested. A subject field encoded as teletexString instead of UTF8String, as covered earlier, is one. Another is a CSR whose SAN extension was built correctly in terms of content but placed as a standalone X.509v3 extension rather than wrapped inside the proper extensionRequest attribute — some older or nonstandard generator libraries have gotten this wrong, producing a CSR that decodes and "looks right" in casual inspection but that standards-compliant CA parsers don't recognize as carrying any extensions at all. A third is inconsistent line-wrapping or missing header/footer lines in the PEM encoding itself, which isn't an ASN.1 issue but a Base64 transport issue — covered in more depth in our companion guide on verifying and troubleshooting CSR errors.
When You'd Actually Need to Decode a CSR by Hand
Most people decoding a CSR fall into one of a few concrete situations. Someone inherited a legacy CSR-generation script from a departed team member and needs to confirm exactly what subject fields and SAN list it's producing before trusting it in a renewal pipeline. A security review flagged a certificate request as containing unexpected fields, and someone needs to trace precisely what's encoded rather than trusting a summary. A CA support ticket references a specific ASN.1 field by name ("your CSR's attributes field is malformed") and resolving it requires actually looking at that structure rather than guessing. Or, less dramatically, someone simply wants to confirm a CSR their hosting control panel generated automatically actually requests the domains they expect, before submitting it somewhere that charges per certificate and doesn't offer free reissuance.
Related Reading
For the actual cryptographic verification step this article doesn't cover, see our companion guide Verifying & Troubleshooting CSR Errors. For generating a fresh CSR from scratch with a compliant SAN list, use the CSR Generator. For background on certificate validation levels these subject fields feed into, read SSL Certificate Validation Levels: EV vs OV vs DV. For the broader TLS/certificate landscape, see TLS/SSL Protocol & Certificate Types Guide.
ToolsNovaHub guides are researched against primary sources (RFCs, vendor docs) and kept up to date as standards change. Spotted an error? Let us know.
📋 Related Tools & Guides Comparison
| Resource | Type | Link |
|---|---|---|
| CSR Decoder | Tool | Open Tool → |
| Certificate Decoder | Tool | Open Tool → |
| CSR Generator | Tool | Open Tool → |
| Verifying & Troubleshooting CSR Errors | Guide | Read Guide → |
| X.509 Certificate Structure: Fields & SAN Extension | Guide | Read Guide → |
| SSL Certificate Validation Levels: EV vs OV vs DV | Guide | Read Guide → |
| SSL Certificate Checker | Tool | Open Tool → |