$title =

How to Decode ASN.1 DER

;

$content = [

ASN.1 (Abstract Syntax Notation One) is a schema language used to describe structured data. DER (Distinguished Encoding Rules) is a strict, canonical binary encoding for ASN.1 values. DER shows up everywhere in security and networking—X.509 certificates, PKCS#8 private keys, CMS/PKCS#7, OCSP, CRLs, and many other formats.

Decoding DER becomes straightforward once you understand its core model: TLV (Tag–Length–Value), parsed recursively for nested structures.


1) ASN.1 vs DER: What You’re Actually Decoding

  • ASN.1 defines types (e.g., INTEGER, SEQUENCE, OBJECT IDENTIFIER) and structures (e.g., “a certificate is a SEQUENCE of fields…”).
  • DER defines exactly how an ASN.1 value becomes bytes, using TLV and additional canonical constraints.

DER is a subset of BER (Basic Encoding Rules). BER allows multiple encodings for the same value; DER eliminates ambiguity by enforcing a single canonical encoding. That’s why DER is used for cryptographic signatures—if multiple encodings were valid, two parties might “sign different bytes” while representing the “same data.”


2) DER Is TLV: Tag, Length, Value

Every DER element is:

  1. Identifier octet(s) (Tag)
  2. Length octet(s)
  3. Content octet(s) (Value)

For constructed types (like SEQUENCE), the “Value” is itself a concatenation of inner TLVs.


3) Step 1: Parse the Tag (Identifier Octets)

The first octet (and sometimes more) encodes:

3.1 Tag Class (2 bits)

  • 00 = Universal (standard ASN.1 types)
  • 01 = Application
  • 10 = Context-specific (very common in X.509)
  • 11 = Private

3.2 Primitive vs Constructed (1 bit)

  • 0 = Primitive (content is raw bytes for the type)
  • 1 = Constructed (content contains nested TLVs)

3.3 Tag Number (5 bits, or more)

  • If the lower 5 bits are 0..30, that is the tag number.
  • If they are 31 (0x1F), the tag number continues in “high-tag-number form” across additional octets (base-128 continuation format).

Common Universal tag numbers

  • 0x02 INTEGER
  • 0x03 BIT STRING
  • 0x04 OCTET STRING
  • 0x05 NULL
  • 0x06 OBJECT IDENTIFIER
  • 0x0C UTF8String
  • 0x10 SEQUENCE (constructed)
  • 0x11 SET (constructed)
  • 0x13 PrintableString
  • 0x17 UTCTime (UTC indicated by trailing Z)
  • 0x18 GeneralizedTime (UTC indicated by trailing Z)

Example: 0x30
Binary 0011 0000:

  • Class 00 = Universal
  • Constructed bit 1
  • Tag number 10000 (16) = SEQUENCE
    So 0x30 is “SEQUENCE (constructed)”.

4) Step 2: Parse the Length

DER length has two forms:

4.1 Short form

If the first length octet is < 0x80, that octet is the length.

Example: 0x0A means 10 bytes of content follow.

4.2 Long form

If the first length octet is >= 0x80, the low 7 bits say how many subsequent length bytes follow.

  • 0x81 nn → length is nn
  • 0x82 nn nn → length is two bytes (big-endian)
  • and so on…

Example: 0x82 0x01 0xF4 → length = 0x01F4 = 500 bytes.

DER constraints (important when validating)

  • Indefinite length is not allowed in DER (that’s BER/CER territory).
  • Length must be encoded in the shortest possible way (no leading zero length bytes).

5) Step 3: Parse the Value (Content Octets)

How you interpret the content depends on (class, tag number, primitive/constructed). For constructed types, you recursively decode child TLVs until you consume exactly the stated length.


6) Worked Example: Decode a Simple DER Structure by Hand

Let’s decode this hex:

30 0A 02 01 05 04 05 68 65 6C 6C 6F

6.1 Outer element

  • 30 → SEQUENCE (constructed)
  • 0A → length = 10 bytes

So the SEQUENCE content is the next 10 bytes:

02 01 05 04 05 68 65 6C 6C 6F

6.2 First child

  • 02 → INTEGER (primitive)
  • 01 → length = 1
  • 05 → value bytes

INTEGER in DER is two’s-complement big-endian. 0x05 = 5.
So child #1 is: INTEGER 5.

6.3 Second child

  • 04 → OCTET STRING (primitive)
  • 05 → length = 5
  • 68 65 6C 6C 6F → ASCII “hello”

So child #2 is: OCTET STRING "hello".

Decoded structure

  • SEQUENCE
    • INTEGER: 5
    • OCTET STRING: “hello”

This “read tag → read length → read value” approach is the universal recipe.


7) Decoding Key Types Correctly (Where People Trip Up)

7.1 INTEGER (0x02)

  • Two’s complement, big-endian.
  • DER requires the minimal number of bytes:
    • Positive values must not have unnecessary leading 0x00.
    • But if the highest bit would be 1 (making it look negative), a single leading 0x00 is required.

Example: value 128 (0x80) must be encoded as 00 80 (so it remains positive).

7.2 BIT STRING (0x03)

Content format:

  • First content byte = number of unused bits in the last byte (0–7)
  • Remaining bytes = the bitstring payload

This matters in certificates (e.g., SubjectPublicKeyInfo contains a BIT STRING).

7.3 OBJECT IDENTIFIER (0x06)

OID encoding rules:

  • First byte encodes the first two arcs: 40 * arc0 + arc1
    • Valid arc0 is 0, 1, or 2
    • arc1 range depends on arc0
  • Remaining arcs are base-128 encoded with continuation bits.

You don’t need to memorize; just remember it’s variable-length base-128 and decode each arc.

7.4 UTCTime (0x17) and GeneralizedTime (0x18)

  • UTCTime typically: YYMMDDHHMMSSZ (Z indicates UTC)
  • GeneralizedTime: YYYYMMDDHHMMSSZ
    DER has strict rules about formatting (including timezone indication and allowed fractions), so a decoder that validates DER must enforce those constraints.

7.5 Context-specific tags (very common)

In X.509, you’ll see tags like A0, A1, A2…:

  • Class 10 (context-specific)
  • Constructed bit often set
  • Tag number in the low bits

Example: A0 = context-specific, constructed, tag 0
A decoder must treat these according to the schema (e.g., Certificate’s version field is [0] EXPLICIT Version).


8) Practical Decoding with Tools

8.1 OpenSSL: quick inspection

If you have a DER file (e.g., cert.der):

openssl asn1parse -inform DER -in cert.der -i

Helpful flags:

  • -i indents nested structures
  • -dump dumps raw content (can be large)

If you have PEM, OpenSSL can often detect or you can convert:

openssl x509 -in cert.pem -outform DER -out cert.der

8.2 dumpasn1: very readable trees

dumpasn1 (Peter Gutmann) is popular because it prints a clear tree and can use known templates for common structures.

8.3 Wireshark

For protocols embedding ASN.1 (LDAP, SNMP, some telecom stacks), Wireshark can dissect and show ASN.1 fields directly.


9) Programmatic Decoding (Safe, Repeatable)

9.1 Python (pyasn1): decode raw DER

from pyasn1.codec.der import decoder
from pyasn1.type import univ
data = bytes.fromhex("300A020105040568656C6C6F")
value, rest = decoder.decode(data, asn1Spec=univ.Sequence())
print(value) # parsed sequence
print(rest) # should be b'' if fully consumed

Notes:

  • If you know the schema, provide an asn1Spec matching your structure for meaningful field decoding.
  • Always check rest to ensure you consumed exactly one DER object.

9.2 Python (asn1crypto): great for X.509 and PKCS*

For certificates/keys, schema-aware libraries are easier than generic TLV parsing.

from asn1crypto import x509
with open("cert.der", "rb") as f:
cert = x509.Certificate.load(f.read())
print(cert.subject.human_friendly)
print(cert.issuer.human_friendly)

9.3 Go (encoding/asn1): strong for simple schemas

package main
import (
"encoding/asn1"
"fmt"
)
type Example struct {
N int
S []byte
}
func main() {
data := []byte{0x30, 0x0A, 0x02, 0x01, 0x05, 0x04, 0x05, 0x68, 0x65, 0x6C, 0x6C, 0x6F}
var ex Example
_, err := asn1.Unmarshal(data, &ex)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", ex)
}

10) Security and Robustness: Don’t Just “Parse,” Validate

ASN.1 decoders have historically been a rich source of vulnerabilities. When decoding untrusted DER:

  • Enforce maximum length limits (avoid allocating huge buffers).
  • Enforce maximum recursion depth (avoid stack exhaustion).
  • Reject indefinite length (not DER).
  • Enforce DER minimality rules if you’re verifying signatures or comparing canonical data.
  • Ensure you consume the entire object (no trailing bytes unless your format explicitly allows concatenation).

For cryptographic workflows, canonical DER is not a nicety—it’s essential to avoid signature malleability and parsing ambiguities.


11) A Repeatable Manual Decoding Checklist

  1. Read identifier octet(s):
    • class, constructed/primitive, tag number
  2. Read length:
    • short vs long form
  3. Slice out exactly that many content bytes
  4. If constructed:
    • decode children until content is exhausted
  5. Interpret primitive types according to tag rules
  6. Validate DER constraints if required (no indefinite, minimal lengths, canonical forms)

Conclusion

Decoding ASN.1 DER is mainly disciplined TLV parsing plus strict canonical rules. Once you can confidently parse tag and length, everything else is either recursion (constructed types) or type-specific interpretation (INTEGER, OID, BIT STRING, times, strings). For real-world objects like certificates and keys, pair your TLV understanding with schema-aware tooling (OpenSSL, asn1parse, asn1crypto, pyasn1) so you can validate and interpret fields safely and correctly.

];

$date =

;

$category =

;

$author =

;

$previous =

;

$next =

;