ASN.1 Made Simple — Example

Suppose I need to create a Purchase Order to send electronically. The components my Purchase Order contains are the date, customer information, items purchased, etc.

In ASN.1 language the Purchase Order may look like this:


PurchaseOrder ::= SEQUENCE {
dateOfOrder DATE,
customer    CustomerInfo,
items       ListOfItems

CustomerInfo ::= SEQUENCE {
companyName    VisibleString (SIZE (3..50)),
billingAddress Address,
contactPhone   NumericString (SIZE (7..12))

Address::= SEQUENCE {
street  VisibleString (SIZE (5 .. 50)) OPTIONAL,
city    VisibleString (SIZE (2..30)),
state   VisibleString (SIZE(2) ^ FROM ("A".."Z")),
zipCode NumericString (SIZE(5 | 9))

ListOfItems ::= SEQUENCE (SIZE (1..100)) OF Item

Item ::= SEQUENCE {
itemCode        INTEGER (1..99999),
color           VisibleString ("Black" | "Blue" | "Brown"),
power           INTEGER (110 | 220),
deliveryTime    INTEGER (8..12 | 14..19),
quantity        INTEGER (1..1000),
unitPrice       REAL (1.00 .. 9999.00),
isTaxable       BOOLEAN

Note that each type (such as PurchaseOrder) in ASN.1 must begin with an uppercase letter. Items that are components of a message (such as dateOfOrder, customer and items) are called identifiers and must begin with a lowercase letter.


As you can see in the example above, there are a number of limitations you can place when defining your types. These limitation are called constraints. Usually constraints result in smaller memory footprint and more compact encodings. It is possible to combine constraints using common logical operators such as "|" for UNION and "^" for INTERSECTION. Here are a few examples of the most common limitations:

  • giving explicit individual values in single value constraint:
    • color VisibleString ("Black" | "Blue" | "Brown")
    • power INTEGER (110 | 220)
  • restricting values to a particular range in value and alphabet range constraint:
    • quantity INTEGER (1..1000)
    • deliveryTime INTEGER (8..12 | 14..19)
    • unitPrice REAL (1.00..9999.00)
    • State ::= VisibleString SIZE(2) ^ FROM ("A".."Z"))
  • restricting the length of values in size constraint:
    • contactPhone NumericString (SIZE (7..12))
    • ListOfItems ::= SEQUENCE (SIZE (1..100)) OF Item
    • zipCode NumericString (SIZE (5 | 9))

MODULE Organization

In ASN.1, all definitions are placed inside of a module which beings with the BEGIN keyword and ends with the END keyword.


An ASN.1 Tag is an internal identification for each component in an ASN.1 message. Although tags can be explicitly assigned by specification writers, it is better to always use AUTOMATIC TAGS when creating new specifications. Using AUTOMATIC TAGS makes specifications much easier to read and understand. It also eliminates any potential ambiguity from a specification writer mistakenly forgetting to add a tag, or potentially inadvertently adding conflicting tags. Use of AUTOMATIC TAGS changes Address internally to:

Address::= SEQUENCE {
street  [0] VisibleString (SIZE (5 .. 50)) OPTIONAL,
city    [1] VisibleString (SIZE (2..30)),
state   [2] VisibleString (SIZE(2) ^ FROM ("A".."Z")),
zipCode [3] NumericString (SIZE(5 | 9))

Since all tags are unique, there is no difficulty determining which values are present or absent in a message, and which components those values are for (e.g. during the decoding process).


When you are creating a type in one ASN.1 module and need to use a definition from a different module, the easiest way to do so is to IMPORT the definition. The imported item can be used as if it was defined locally in the module. For example, importing Address from a different module may look like this:

IMPORTS Address FROM PostalInformation;