How to Parse ASN.1 DER — Distinguished Encoding Rules Walkthrough

ASN.1 DER (Distinguished Encoding Rules) is the binary encoding behind X.509 certificates, PKCS keys, FIDO2 attestation signatures, SNMP, LDAP, and GlobalPlatform secure messaging. Every DER structure is a TLV (Tag-Length-Value) triple with very specific encoding rules. This guide covers tag class decoding, constructed vs primitive, length encoding (short/long/indefinite), OID parsing, and practical X.509 certificate dissection.

1. DER Tag Byte — First Byte Encoding

Tag byte (8 bits):
Bits 7-6: Tag Class
  00 = UNIVERSAL (built-in types: INTEGER, OCTET STRING, SEQUENCE...)
  01 = APPLICATION (protocol-specific)
  10 = CONTEXT-SPECIFIC (tagged types)
  11 = PRIVATE (organization-specific)

Bit 5: Constructed flag
  0 = Primitive (value IS the data)
  1 = Constructed (value CONTAINS more TLV structures)

Bits 4-0: Tag Number
  0-30: Direct encoding
  31 (0x1F): Tag number continues in subsequent bytes

2. Common UNIVERSAL Tag Numbers

Tag (hex)NameConstructed?Used In
0x01BOOLEANPrimitiveTrue/False fields
0x02INTEGERPrimitiveSerial numbers, RSA moduli, version numbers
0x03BIT STRINGPrim/ConstrPublic keys, signatures, flags
0x04OCTET STRINGPrim/ConstrRaw byte arrays, encrypted data
0x05NULLPrimitiveAlgorithm parameters = NULL
0x06OBJECT IDENTIFIERPrimitiveOIDs: 1.2.840.113549.1.1.1 (RSA)
0x0AENUMERATEDPrimitiveNamed integer values
0x0CUTF8StringPrimitiveHuman-readable text
0x13PrintableStringPrimitiveDN components (CN, O, C)
0x16IA5StringPrimitiveASCII strings (email, URL)
0x17UTCTimePrimitiveYYMMDDHHMMSSZ format
0x18GeneralizedTimePrimitiveYYYYMMDDHHMMSSZ format
0x30SEQUENCEConstructedX.509 certs, PKCS structures
0x31SETConstructedAttribute sets, RDNs

3. Length Encoding — Short, Long, and Indefinite

# Short form: length byte bit 7 = 0, bits 6-0 = length (0-127)
0x0A → length = 10 bytes
0x7F → length = 127 bytes

# Long form: length byte bit 7 = 1, bits 6-0 = number of subsequent length bytes
0x81 0xA0 → 1 byte follows, length = 160
0x82 0x01 0x00 → 2 bytes follow, length = 256
0x83 0x01 0x00 0x00 → 3 bytes follow, length = 65536

# Indefinite form (BER only, not DER): length byte = 0x80
# Terminated by two zero bytes (0x00 0x00). DER forbids indefinite length.

4. DER vs BER vs CER

RuleBERDERCER
Length formAnyShortest possibleShortest possible
Indefinite lengthAllowedForbiddenAllowed (constructed only)
BOOLEAN encodingAny non-zero0x00 or 0xFF only0x00 or 0xFF only
BIT STRING paddingAnyMinimum requiredMinimum required
SET orderingAnyDER-sortedCER-sorted
Use caseStreaming protocolsDigital signatures, certsLarge data (CMS, S/MIME)

5. OID — Object Identifier Decoding

# OID encoding: first two components encoded in first byte
# byte 0 = (OID_component_1 * 40) + OID_component_2
# Subsequent components: base-128 with high bit as continuation flag

def decode_oid(data):
    """Decode OID from DER bytes."""
    oid = []
    # First byte: component 1 and 2
    oid.append(data[0] // 40)
    oid.append(data[0] % 40)

    i = 1
    while i < len(data):
        value = 0
        while True:
            value = (value << 7) | (data[i] & 0x7F)
            if data[i] & 0x80 == 0:
                break
            i += 1
        oid.append(value)
        i += 1

    return '.'.join(str(v) for v in oid)

# Example: 2A 86 48 86 F7 0D 01 01 01
# → 1.2.840.113549.1.1.1 (rsaEncryption)

6. Dissecting an X.509 Certificate

# X.509 cert = SEQUENCE {
#     TBSCertificate SEQUENCE { version, serial, signature algo, issuer, validity, subject, public key, extensions }
#     signatureAlgorithm SEQUENCE { OID, params }
#     signatureValue BIT STRING
# }

# DER dump (hex):
# 30 82 03 21   ← SEQUENCE, length=0x0321 (801 bytes) — the whole certificate
#   30 82 02 09 ← SEQUENCE — TBSCertificate
#     A0 03     ← [0] tagged (version), length=3
#       02 01 02 ← INTEGER v3 (0x02)
#     02 09     ← INTEGER (serial number, 9 bytes)
#       00 C0 FF EE ... 08
#     30 0D     ← SEQUENCE (signature algo)
#       06 09 2A 86 48 86 F7 0D 01 01 0B  ← OID: sha256WithRSAEncryption
#       05 00                                ← NULL
#     30 19     ← SEQUENCE (issuer DN)
#       31 0B 30 09 06 03 55 04 06 13 02 55 53  ← SET { SEQUENCE { OID=countryName, "US" } }
#       ...
Test this yourself: Our ASN.1 DER Parser decodes any DER hex dump into a tree view — paste raw DER bytes and see each tag, class, length, and nested structure. Works for X.509, PKCS, FIDO2 sigs, and proprietary smart card BER-TLV.

7. Common ASN.1 Parsing Pitfalls

IssueCauseFix
Tag byte misreadHigh tag numbers use multiple bytesCheck bits 4-0 = 0x1F → subsequent bytes are tag continuation
Length byte > 127 decoded as short formLong form used for length > 127Check bit 7 — if set, number of subsequent bytes = value & 0x7F
Indefinite length in DERBER data fed to DER parserDER forbids indefinite length; use BER parser for streaming data
Constructed OCTET STRING not recursedBit 5=1 means value contains nested TLVsAlways check constructed bit before treating value as raw bytes

Related Tools

ASN.1 DER Parser — Decode DER hex to tree view | FIDO2 Parser — Parse attestation signatures (DER-encoded) | Checksum Verifier — Verify DER structure integrity | PC/SC Programming Guide