Crypt::ASN1(3)

okay bestie, welcome to Crypt::ASN1(3) from the User Contributed Perl Documentation β€” the manual page, not a drill. It’s giving β€œDER ASN.1 parser and encoder based on libtomcrypt,” and honestly? Iconic. πŸ’…

🌸 NAME 🌸

Crypt::ASN1 β€” a DER ASN.1 parser and encoder based on libtomcrypt. That’s the whole vibe, no cap. πŸ’–

✨ SYNOPSIS ✨

use Crypt::ASN1 qw(asn1_decode_der asn1_encode_der asn1_to_string);

# --- decode ---
my $tree = asn1_decode_der($der_bytes);
my $tree = asn1_decode_der($der_bytes, { int => 'hex', bin => 'hex' });

# --- inspect ---
print asn1_to_string($tree);

# --- encode a decoded tree ---
my $der2 = asn1_encode_der($tree);

# --- build from scratch ---
my $der = asn1_encode_der([{
  type  => 'SEQUENCE',
  value => [
    { type => 'INTEGER',      value => '42' },
    { type => 'BOOLEAN',      value => 1 },
    { type => 'OID',          value => '1.2.840.113549.1.1.11' },
    { type => 'UTF8_STRING',  value => 'hello' },
    { type => 'OCTET_STRING', value => "\x00\x01\x02" },
    { type => 'BIT_STRING',   value => "\x03\x02\x01", bits => 20 },
    { type => 'NULL' },
    { type => 'UTCTIME',      value => '2025-06-15T12:00:00Z' },
    { type => 'CUSTOM', class => 'CONTEXT_SPECIFIC',
      constructed => 1, tag => 0,
      value => [{ type => 'INTEGER', value => '2' }] },
  ],
}]);

πŸ’– DESCRIPTION πŸ’–

Since: CryptX-0.089

So what does it do? It parses DER-encoded ASN.1 data into a Perl data structure without requiring any schema, and it encodes Perl data structures back to DER. It uses libtomcrypt’s der_decode_sequence_flexi for decoding β€” yes, that’s the exact function name, bestie, write it down. 🫢

Both the decoder output and the encoder input use the same node hash structure described below. And when it gets a tree produced by the decoder, the encoder does its best to produce the same ASN.1 that was originally parsed, regardless of what decode options were used. It’s out here doing its best and honestly? Same. 😭

πŸŽ€ EXPORT πŸŽ€

Nothing is exported by default. Zero. None. The default export list is empty, periodt.

You can export selected functions like this:

use Crypt::ASN1 qw(asn1_decode_der asn1_encode_der);

Or all of them at once, if you’re feeling main-character energy:

use Crypt::ASN1 ':all';

πŸ’– NODE HASH STRUCTURE πŸ’–

okay so bestie, here's the tea: both the decoder and the encoder are working with the exact same data structure β€” an arrayref of node hashrefs. each hashref is one ASN.1 TLV (Tag-Length-Value) element. iconic, honestly. πŸŽ€


Common keys πŸ—οΈ

every single node has these three keys, no cap:

  • type (string, required) β€” the ASN.1 type name. built-in values include:
BOOLEAN  INTEGER  NULL  OID
OCTET_STRING  BIT_STRING  UTF8_STRING
PRINTABLE_STRING  IA5_STRING  TELETEX_STRING
UTCTIME  GENERALIZEDTIME
SEQUENCE  SET  CUSTOM

that list is not exhaustive for decoded input, bestie. if the decoder runs into an ASN.1 tag that doesn't map to one of the built-in type names above, it comes back as CUSTOM with the right class, constructed, and tag fields. this includes unsupported universal tags like ENUMERATED, which decode as CUSTOM with class => "UNIVERSAL". πŸ’…

  • value (varies, required for most types) β€” the decoded value. its Perl type depends on type and sometimes on the format key β€” see "Per-type details" below. ✨
  • format (string, decoder sets it, encoder reads it) β€” tells the encoder how value is represented so it can convert it back to DER. the decoder sets this automatically; when you're building nodes from scratch you can omit it β€” the encoder then assumes the default representation for each type. 🫢

Per-type details 🧬

each subsection below documents one type. for types where the value representation depends on the decode option used, there's a format rundown that lists every format/value combination. the encoder accepts every combination shown β€” it reads format and converts value back to DER automatically. obsessed. πŸ”₯

BOOLEAN πŸŽ€

keys: type, format, value.

value is 1 (true) or 0 (false). format is always bool.

{ type => "BOOLEAN", format => "bool", value => 1 }

INTEGER πŸ”’

keys: type, format, value.

value is an arbitrary-precision signed integer. format describes the representation:

  • decimal β€” value: decimal string; decode option: (default); example: "255"
  • hex β€” value: lowercase hex string; decode option: int => 'hex'; example: "ff"
  • bytes β€” value: big-endian binary string; decode option: int => 'bytes'; example: "\xff"

all three forms are accepted by the encoder. when format is absent, the encoder treats value as a decimal string (a Perl integer is fine too).

negative integers: decimal and hex carry a leading - (e.g. "-5"). bytes stores the unsigned magnitude only and is intended for naturally unsigned values such as RSA moduli. when decoding with int => 'bytes', negative ASN.1 INTEGER values are rejected. 🚨

NULL 🌫️

keys: type, format, value.

value is always undef. format is always null. the encoder ignores value.

{ type => "NULL", format => "null", value => undef }

OID 🧚

keys: type, format, value, and optionally name.

value is a dotted-decimal OID string (at least two arcs). format is always oid.

{ type => "OID", format => "oid", value => "1.2.840.113549.1.1.11" }

as a convenience, the encoder accepts textual arcs with leading zeros and lets DER encoding canonicalize them. for example, 2.000.1 encodes and decodes back as 2.0.1. πŸ’«

optional key: name β€” present only when the oidmap decode option is supplied and the OID is found in the map. ignored by the encoder.

{ ..., name => "sha256WithRSAEncryption" }   # when oidmap matches

OCTET_STRING πŸ§ƒ

keys: type, format, value.

value is binary data. format describes the representation:

  • bytes β€” value: raw binary string; decode option: (default); example: "\x04\x01"
  • hex β€” value: lowercase hex string; decode option: bin => 'hex'; example: "0401"
  • base64 β€” value: Base64-encoded string; decode option: bin => 'base64'; example: "BAE="

all three forms are accepted by the encoder. when format is absent, the encoder treats value as raw bytes. 🌈

BIT_STRING πŸŽ›οΈ

keys: type, format, value, bits.

value is the packed bit data (MSB-first). format follows the same rules as OCTET_STRING (bytes, hex, or base64). all three forms are accepted by the encoder.

bits is the exact number of significant bits. the quantity 8 * byte_length(value) - bits gives the number of unused trailing bits in the last byte.

when format is absent the encoder treats value as raw bytes. when bits is absent it defaults to 8 * length(value) (no unused bits).

# default format (raw bytes, 25 significant bits)
{ type => "BIT_STRING", format => "bytes",
  value => "\x03\x02\x01\x00", bits => 25 }

# hex format
{ type => "BIT_STRING", format => "hex",
  value => "03020100", bits => 25 }

UTF8_STRING 🌸

keys: type, format, value.

value is a Perl Unicode string (utf8 flag on). format is always utf8.

{ type => "UTF8_STRING", format => "utf8", value => "caf\x{e9}" }

PRINTABLE_STRING, IA5_STRING, TELETEX_STRING 🧡

keys: type, format, value.

value is a byte string. format is always string for all three.

{ type => "PRINTABLE_STRING", format => "string", value => "abc" }
{ type => "IA5_STRING",       format => "string", value => "ia5" }
{ type => "TELETEX_STRING",   format => "string", value => "tele" }

UTCTIME ⏰

keys: type, format, value.

value is a timestamp. format describes the representation:

  • rfc3339 β€” value: RFC 3339 string; decode option: (default); example: "2024-01-15T10:30:00Z"
  • epoch β€” value: Unix timestamp (integer); decode option: dt => 'epoch'; example: 1705314600

both forms are accepted by the encoder. when format is absent, the encoder auto-detects: an all-digit value is treated as epoch, a value matching YYYY- is treated as RFC 3339. ✨

for UTCTIME, encoder input must fall within the UTCTime year window 1950..2049; values outside that range are rejected. fractional seconds are also rejected for UTCTIME. 😭

time validation in the encoder is currently syntactic, not full calendar validation. the encoder checks the accepted input shape and ASN.1-specific constraints above, but it does not verify that every RFC 3339-looking date and time is semantically valid. so like… don't expect it to fact-check your calendar, bestie.

the decoder expands the 2-digit UTCTime year using the RFC 5280 window (YY >= 50 β†’ 19YY, else 20YY). timezone offsets are preserved (e.g. "2024-01-15T10:30:00+05:30"). πŸ•°οΈ

GENERALIZEDTIME πŸ—“οΈ

keys: type, format, value.

same format rules as UTCTIME; both forms are accepted by the encoder. fractional seconds are preserved (e.g. "2024-01-15T10:30:00.125Z"). validation is likewise syntactic only; semantically invalid calendar values that match the accepted timestamp syntax are not currently rejected. πŸ’€

SEQUENCE πŸͺ†

keys: type, format, value.

value is an arrayref of child node hashrefs (in order). format is always array.

{ type => "SEQUENCE", format => "array", value => [ ...children... ] }

SET 🎁

keys: type, format, value.

same structure as SEQUENCE. format is always array. both ASN.1 SET and SET OF are represented as type => "SET" (they share the same DER tag 0x31). πŸ‘‘

CUSTOM πŸ¦‹

represents any tag that does not map to one of the built-in type names above. this is commonly used for context-specific implicit/explicit tags ("[0]", "[1]", ...) found in X.509 certificates and other ASN.1 schemas, but it can also be emitted by the decoder for unsupported universal tags.

keys: type, format, value, class, constructed, tag.

  • class (string) β€” CONTEXT_SPECIFIC, APPLICATION, UNIVERSAL, or PRIVATE
  • constructed (integer) β€” 1 if constructed, 0 if primitive
  • tag (integer) β€” the tag number (e.g. 0 for "[0]") Must be a non-negative integer within the range supported by the current encoder build.

constructed (constructed => 1): value is an arrayref of child nodes. format is array.

{ type => "CUSTOM", format => "array",
  class => "CONTEXT_SPECIFIC", constructed => 1, tag => 0,
  value => [ { type => "INTEGER", ... } ] }

primitive (constructed => 0): value is raw data. format follows the same rules as OCTET_STRING (bytes, hex, or base64 depending on the bin decode option). all three forms are accepted by the encoder. primitive CUSTOM values must not be references. 🚩

# default format
{ type => "CUSTOM", format => "bytes",
  class => "CONTEXT_SPECIFIC", constructed => 0, tag => 1,
  value => "\xAA\xBB" }

# hex format (bin => 'hex')
{ type => "CUSTOM", format => "hex",
  class => "CONTEXT_SPECIFIC", constructed => 0, tag => 1,
  value => "aabb" }

Re-encoding Decoded Trees πŸ”

the encoder reads format and converts value back to DER before encoding. when you give it a tree returned by asn1_decode_der, it does its best to produce the same ASN.1 that was originally parsed, regardless of the decode options used:

my $tree = asn1_decode_der($der, { int=>'hex', bin=>'base64', dt=>'epoch' });
my $der2 = asn1_encode_der($tree);

Building nodes from scratch πŸ› οΈ

when you're constructing nodes by hand you need type and value (plus the extra keys noted above for CUSTOM and BIT_STRING). you may omit format; the encoder assumes:

  • INTEGER β€” decimal string or Perl integer
  • OCTET_STRING β€” raw bytes
  • BIT_STRING β€” raw packed bytes, bits = length(value) * 8
  • UTCTIME β€” RFC 3339 string (or all-digit epoch)
  • GENERALIZEDTIME β€” RFC 3339 string (or all-digit epoch)
  • CUSTOM primitive β€” raw bytes

you may also supply format explicitly if you prefer to work with hex or base64 representations:

# these two produce identical DER
{ type => "OCTET_STRING", value => "\x04\x01" }
{ type => "OCTET_STRING", format => "hex", value => "0401" }

πŸ’– FUNCTIONS πŸ’–

Okay bestie, this is the whole function menu β€” every helper, every option, zero skips. Pull up a chair and let's get into it. πŸ’…

πŸ¦‹ asn1_decode_der

my $tree = asn1_decode_der($der_bytes);
my $tree = asn1_decode_der($der_bytes, \%opts);

This one takes $der_bytes, parses them, and hands you back an arrayref full of top-level node hashrefs. If the parse goes sideways it croaks β€” no quiet little failure, fr fr. 😭

The optional %opts hashref controls value formatting β€” it's your remote control:

  • int => 'hex' | 'bytes' β€” how to represent INTEGER values. Default is a decimal string (format=>"decimal"). 'hex' gives you a lowercase hex string (format=>"hex"). 'bytes' gives you a raw big-endian binary string (format=>"bytes") for non-negative INTEGER values only; decoding croaks if the DER INTEGER is negative. 🚨
  • bin => 'hex' | 'base64' β€” how to represent OCTET_STRING, BIT_STRING, and primitive CUSTOM values. Default is raw binary bytes (format=>"bytes"). 'hex' gives a lowercase hex string (format=>"hex"). 'base64' gives a Base64-encoded string (format=>"base64"). ✨
  • dt => 'epoch' β€” how to represent UTCTIME and GENERALIZEDTIME values. Default is an RFC 3339 string (format=>"rfc3339"). 'epoch' gives a Unix timestamp integer (format=>"epoch"). This works reliably only on Perls with 64-bit integers; on 32-bit integer Perls, large timestamps may overflow or lose precision. ⚠️
  • oidmap => \%map β€” a hashref mapping dotted OID strings to friendly names. When a decoded OID node's value exists as a key in %map, the node gets an additional name key with the mapped value. Does not affect encoding. 🏷️

πŸ¦‹ asn1_decode_pem

my $tree = asn1_decode_pem($pem_string);
my $tree = asn1_decode_pem($pem_string, \%opts);

Same energy as above, but it lowkey decodes the PEM envelope first (via pem_to_der in Crypt::Misc), then parses the resulting DER bytes. It accepts the same %opts as asn1_decode_der. πŸ’Œ

πŸ¦‹ asn1_decode_der_file

my $tree = asn1_decode_der_file($filename);
my $tree = asn1_decode_der_file($filename, \%opts);

Reads $filename as raw binary and parses it as DER. File-based, no cap. πŸ“

πŸ¦‹ asn1_decode_pem_file

my $tree = asn1_decode_pem_file($filename);
my $tree = asn1_decode_pem_file($filename, \%opts);

Reads $filename, decodes the PEM envelope, then parses the DER bytes. Basically the PEM-file main character. πŸ‘‘

πŸ¦‹ asn1_encode_der

my $der_bytes = asn1_encode_der($tree);

Encodes $tree (an arrayref of node hashrefs) to DER bytes. The input can be a tree you got back from asn1_decode_der or one you built from scratch. Croaks on invalid input. πŸ”₯

The encoder normalizes every node before encoding: it reads format (if present) to determine how to interpret value, converts it to the canonical DER form, and encodes it. 🧼

The current low-level encoder supports element content lengths up to 0xffffffff bytes; larger values are rejected. That's the ceiling, bestie. 🚫

πŸ¦‹ asn1_encode_pem

my $pem_string = asn1_encode_pem($tree, $header);

Encodes $tree to DER, then wraps it in a PEM envelope with the given $header (e.g. CERTIFICATE, RSA PRIVATE KEY). Defaults to DATA if $header is omitted. 🎁

πŸ¦‹ asn1_encode_der_file

asn1_encode_der_file($tree, $filename);

Encodes $tree to DER and writes it to $filename. Simple, iconic, done. πŸ’Ύ

πŸ¦‹ asn1_encode_pem_file

asn1_encode_pem_file($tree, $header, $filename);

Encodes $tree to PEM and writes it to $filename. Same file-writing energy, but PEM-flavored. 🌸

πŸ¦‹ asn1_to_string

my $text = asn1_to_string($tree);

Returns a human-readable text representation of $tree (an arrayref of node hashrefs as returned by any asn1_decode_* function). Useful for debugging and inspection, similar to openssl asn1parse output. It's giving "finally, I can see what's inside" energy β€” iykyk. πŸ”

print asn1_to_string(asn1_decode_pem_file("cert.pem"));

produces output like:

         SEQUENCE (3 elem)
           SEQUENCE (8 elem)
             context_specific [0] cons (1 elem)
               INTEGER:2
             INTEGER:17923815188543234454
             SEQUENCE (2 elem)
               OBJECT:1.2.840.113549.1.1.11
               NULL:
             ...
           BIT STRING:3082010a0282010100c242299a49420c21dcf9b957afcdc49... (2160 bit)

Binary values (OCTET_STRING, BIT_STRING, primitive CUSTOM) are shown as lowercase hex, truncated to 64 characters with ... for longer values. BIT_STRING additionally shows the bit count in parentheses. OID nodes that have a name key (via oidmap) show the name in parentheses after the dotted value. 🧬

The function handles trees decoded with any combination of decode options (int, bin, dt). Mix and match, bestie. πŸ’…


πŸ‘€ SEE ALSO πŸ‘€

  • CryptX
  • Crypt::Misc

That's the whole function tour, bestie πŸ’– β€” page vibes come from perl v5.42.0, dated 2026-05-11, and it's Crypt::ASN1(3). Now go decode something iconic β€” periodt. πŸŽ€βœ¨

πŸ“¦ where this page comes from