Apache Harmony is retired at the Apache Software Foundation since Nov 16, 2011.

The information on these pages may be out of date, or may refer to resources that have moved or have been made read-only.
For more information please refer to the Apache Attic

ASN.1 Framework

ASN.1 Framework

  1. Revision History
  2. About this Document
    1. Purpose
    2. Intended Audience
    3. Documentation Conventions
  3. Introduction to ASN.1
    1. About
    2. ASN.1 Basic Types
  4. ASN.1 Framework in Harmony
    1. About
    2. Mapping between ASN.1 and Java Types
    3. Harmony ASN.1 Classes
    4. Encoding Rules
    5. Implementing ASN.1 Notations
  5. Appendix: Usage Examples
  6. References

Revision History

Version Version Information Date
Initial version Nadya Morozova, Stepan Mishura: document created
Special thanks to Sergey Dmitriev for assistance
November 16, 2005
Formatting update Nadya Morozova September 21, 2006

About This Document

Purpose

This document introduces the ASN.1 (Abstract Syntax Notation) framework delivered as part of the Harmony classlibrary. This document provides an overview of ASN.1 types and encoding rules with focus on the characteristics of the current implementation. The document gives details on the framework design and provides an overall description of the ASN.1 package.

Intended Audience

The target audience for the document includes a wide community of engineers interested in using ASN.1 and in further work with the product to contribute to its development. The document assumes that readers are familiar with the ASN.1 notation and the Java* programming language.

Documentation Conventions

This document uses the unified conventions for the Harmony documentation kit.

Back to Top

Introduction to ASN.1

About

ASN.1 (Abstract Syntax Notation One) is an international standard of notation used to specify data structures with a high level of abstraction, which is reflected in the ASN.1 specification [2]. ASN.1 is fully platform- and language-independent. ASN.1 goes with the encoding rules, which determine how to represent a value of an abstract type as a string of octets [3].

The Java* API specification [1] employs ASN.1 in the following ways:

To learn more about ASN.1, you can use online documentation [4], [5], and publications [6], [7].

Back to Top

ASN.1 Basic Types

ASN.1 has the following basic types:

These types are used to specify a wide range of other abstract types, as shown in Example 1.

Example 1

This example is based on RFC 3280 [8].

Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension

Extension ::= SEQUENCE {
    extnID OBJECT IDENTIFIER,
    critical BOOLEAN DEFAULT FALSE,
    extnValue OCTET STRING
}

Version ::= INTEGER { v1(0), v2(1), v3(2) }

In this example, the basic ASN.1 types SEQUENCE, OBJECT IDENTIFIER, BOOLEAN and OCTET STRING are used to specify a new abstract type Extension. The newly created type is then used with another basic type SEQUENCE OF to describe the Extensions type. The ASN.1 INTEGER type is used to specify the Version abstract type and to provide constraints for this type.

Back to Top

ASN.1 Framework in Harmony

This part of the document describes the ASN.1 framework as a whole, defines the mapping principles to establish the correspondence between ASN.1 and Java* types, and represents the hierarchy of ASN.1 types representation in the current framework.

About

The ASN.1 framework provides a common, easy and efficient approach for working with ASN.1 basic types, notations and encoding rules. This framework can be described as a layer between a Java* object and its ASN.1 encoded form, as shown in Figure 1.



overview

Figure 1: ASN.1 Framework Layer

The Harmony ASN.1 framework is characterized by:

The framework enables the following:

Note

The current ASN.1 framework is a partial implementation of the ASN.1 and encoding rules specifications. This framework covers certain ASN.1 basic types and basic encoding rules (BER), and provides most restrictions employed by the distinguished encoding rules (DER).

Mapping between ASN.1 and Java* Types

The framework maps all ASN.1 abstract types and notations to Java* primitive types or Java* classes.

Example 2

The notations in Example 1 can be represented as the following Java* classes:

public class Extension {
    private String extnID;
    private boolean critical;
    private byte extnValue[];
}

public class Extensions {
    // contains elements of Extension class
    private List extensions;
}

The Extension notation corresponds to a Java* class with three fields, where every field corresponds to one entry in the Extension notation. For example, the critical BOOLEAN DEFAULT FALSE field in the Extension notation corresponds to the boolean critical field in the Java* class. The Extensions notation equals to a Java* class with a field that contains an ordered collection of the instances of the Extension class.

The table below describes the default mapping ASN.1 types to Java* types, and indicates the class providing the specified mapping in the current framework.

ASN.1 Type Java* Type Framework Class
Primitive
INTEGER byte[] ASN1Integer
ENUMERATED byte[] ASN1Enumerated
OBJECT IDENTIFIER int[] ASN1Oid
BOOLEAN java.lang.Boolean ASN1Boolean
String BitString asn1.BitString ASN1BitString
OctetString byte[] ASN1OctetString
PrintableString java.lang.String ASN1StringType
T61String java.lang.String ASN1StringType
IA5String java.lang.String ASN1StringType
UTF8String java.lang.String ASN1StringType
BMPString java.lang.String ASN1StringType
GeneralString java.lang.String ASN1StringType
TeletexString java.lang.String ASN1StringType
UniversalString java.lang.String ASN1StringType
UTCTime java.util.Date ASN1UTCTime
GeneralizedTime java.util.Date ASN1GeneralizedTime
Constructed SEQUENCE Object[] ASN1Sequence
SEQUENCE OF java.util.List ASN1SequenceOf
SET OF java.util.List ASN1SetOf
Tagged EXPLICIT based type ASN1Explicit
IMPLICIT based type ASN1Implicit
Other ANY byte[] ASN1Any
CHOICE one of chosen types ASN1Choice

Back to Top

Harmony ASN.1 Classes

Basic ASN.1 types are in the org.apache.harmony.security.asn1 package in accordance with the hierarchy shown in Figure 2.

package

Figure 2: Class Hierarchy

The subsequent sections provide as short description of the classes included in the package.

Primitive Types

ASN1Integer
This class represents the ASN.1 INTEGER type that denotes an arbitrary integer with positive, negative, or zero values and any magnitude. Because an integer value is not restricted, it is up to the application class to choose the Java* type for storing the integer value, for example, an instance of the java.math.BigInteger class. By default, an integer value is stored in an array of bytes.
ASN1Enumerated
This class represents the ASN.1 ENUMERATED type that denotes a set of integer values. The implementation of this class is similar to that of the ASN1Integer class.
ASN1Oid
This class implements the ASN.1 OBJECT IDENTIFIER type. This type is a sequence of integer components that identifies an entity, such as an organization or an algorithm. Integer components have no negative values. An OBJECT IDENTIFIER value includes at least two components. The corresponding Java* type is an array of integer values.
ASN1Boolean
This class implements the ASN.1 BOOLEAN type, which corresponds to the java.lang.Boolean Java* class.

String Types

ASN1StringType
This is an abstract class that contains common functionality for all ASN.1 string types, and includes the implementation of the following types: BMPString, IA5String, GeneralString, PrintableString, TeletexString, UniversalString, and UTF8String. The class maps all these types to the java.lang.String object.

Note

The current implementation does not verify allowed characters for any of ASN.1 restricted characters types. The class only stores and retrieves contents bytes to and from the String object.

ASN1BitString
This class represents the ASN.1 BitString type. The corresponding Java* class is BitString included in the asn1 package. The class provides implementation for decoding or encoding BitString objects.

Note

A special use case for this ASN.1 type exists, when the type is declared as Named BitString. For example:

    Keys ::= BIT STRING {
        Key1 (0),
        Key2 (1),
        MyKey (2)
    }

In this case, the BER specification [3] enables adding and removing any number of trailing to and from the basic encoding. To provide a correct type implementation, use the ASN1Bitstring.ASN1NamedBitList class. By default, the class maps the ASN.1 Named BitString type to an array of primitive boolean values.

ASN1OctetString
This class implements the ASN.1 OctetString type, which corresponds to the Java* type of an array of bytes.
ASN1UTCTime
This class represents the ASN.1 UTCTime type. The corresponding Java* class is java.util.Date.
ASN1GeneralizedTime
This class represents the ASN.1 GeneralizedTime type. The corresponding Java* class is java.util.Date.

Constructed Types

ASN1Sequence
The class represents a ASN.1 type consisting of an ordered collection of more than one type. A type in the collection can be optional or can have default values. If a type in the collection is marked as optional or default, then its value may be absent in the encoding of the sequence type. If a default type is absent, then its default value is used.
An instance of the class is initialized with a Java* array of ASN.1 classes that corresponds to the notation of a sequence type. The order of ASN.1 classes in an initialization array must strictly correspond to the type.
For example, if a sequence type has the following types collection: integer, boolean, ANY, then an initialization array must contain three class instances in the same order: ASN1Boolean, ASN1Integer, ASN1Any.
ASN1SequenceOf
The SEQUENCE OF type denotes an ordered collection of one or more values of the selected ASN.1 type. An instance of the class is initialized with an instance of the ASN.1 class according to the type notation. The passed instance is used to decode or encode all values in an collection.
ASN1SetOf
The SET OF type denotes an unordered collection of one or more values of the selected ASN.1 type. This class is similar to the ASN1SequenceOf class.

Tagged Types

ASN1Explicit
The class implements the ASN.1 EXPLICIT type tagging. Explicit tagging denotes a type derived from another type by adding an outer tag to the base type. This type can be represented as a sequence type with only one component, so that the implementation class acts as a container for another ASN.1 type.
ASN1Implicit
The class implements the ASN.1 IMPLICIT type tagging. An implicitly tagged type is a type derived from another type by changing a tag of the base type. The implementation class for this type changes only the tag when decoding or encoding the base type.

Other Types

ASN1Any
The class implements the ASN.1 ANY type. The type denotes any ASN.1 type that can be defined by a value of the OBJECT IDENTIFIER or by an integer index. The class handles only raw data represented as a Java* byte array. It is up to the application class to select the appropriate decoder or encoder for retrieving or storing the content respectively.
ASN1Choice
The class implements the ASN.1 CHOICE type. The type represents a list of one more type alternatives with distinct tags. an instance of the class is initialized with the Java* array of ASN.1 classes, which corresponds to a type notation.
For example, if a CHOICE type is specified as a list of boolean, OctetString and UTCTime, then an initialization array contains instances of the ASN1Boolean, ASN1OctetString and ASN1UTCTime classes. During decoding or encoding, a required type alternative is selected.

Back to Top

Encoding Rules

Encoding rules specify how to represent an ASN.1 type as a sequence of octets. ASN.1 encoding rules are represented in the org.apache.harmony.security.asn1 package, as follows:

Back to Top

Encoding

Encoding an object is the process of extracting all required information from an object and storing it in a sequence of octets according to the specified ASN.1 notation and encoding rules. The common encoding functionality is implemented in the BerOutputStream class. Encoding for DER is represented by the DEROutputStream class.

The encoding of data for each ASN.1 type includes:

DER Encoding

In contrast to BER, DER enables using only the definite form of length encoding. That is why, before encoding an ASN.1 type value, the ASN.1 framework must obtain the length of the value. This requirement determines the whole process of DER encoding: to calculate the length of a constructed ASN.1 type, the framework calculates lengths of its components, which can also be constructed, and so on. DER encoding goes in the following stages:

Decoding

Decoding or verifying the provided encoded form is a sequential process of parsing strings of octets according to the specified ASN.1 notation and encoding rules.

An iteration of decoding an ASN.1 type includes decoding its tag, length, and content octets. The class BerInputStream provides a common decoding implementation for all basic ASN.1 types. To provide specific decoding for a basic ASN.1 type, a derived class must override one of the corresponding methods. For example, DerInputStream provides a custom implementation for decoding the ASN.1 Boolean type by overriding the method readBoolean().

Back to Top

Implementing ASN.1 Notations

Basic Classes

In the current framework, the basic classes meet the following requirements:

Back to Top

Custom Classes

Classes from the asn1 package that represent ASN.1 basic types are used as building blocks for implementing a custom ASN.1 encoding or decoding class. A custom ASN.1 class provides mapping of an abstract ASN.1 type to a definite Java* class.

Two approaches for implementing custom ASN.1 classes are available. Custom classes can be designed as singleton Java* classes or not. The choice depends on further use of the class decoder. The singleton approach is not efficient when decoding only one Java* object. However, for decoding a series of encodings (for example, with an application server), the singleton approach is rather effective because only one reusable decoder instance exists. Creating a new decoder object for each decoded or encoded Java* object leads to performance penalties.

To implement the singleton approach, a custom ASN.1 class must meet the following requirements:

Example 3

This example illustrates the singleton approach to static instances of ASN.1 custom classes for the Extensions and Extension classes used in Example 2 . For this, create an instance of a custom ASN.1 class as an instance of an anonymous class as shown below.

class Extensions {
    // instance of decoder/encoder
    public static final ASN1SequenceOf ASN1 =
        new ASN1SequenceOf(Extension.ASN1);

    private List extensions;
}

class Extension {
    // instance of decoder/encoder
    public static final ASN1Sequence ASN1 = 
        new ASN1Sequence(new ASN1Type[] {
                             ASN1Oid.getInstance(),            // extnID
                             ASN1Boolean.getInstance(),        // critical
                             ASN1OctetString.getInstance()}) { // extnValue
        // replace constructor
        {
            // set default value for critical field
            // first parameter - a default value
            // second parameter - an index of ASN.1 type in a sequence starting with 0
            setDefault(Boolean.FALSE, 1);
        }
    };

    private String extnID;
    private boolean critical;
    private byte extnValue[];

}

The static final ASN1 field instance provides a mapping between the Java* Extension class and its ASN.1 notation. The field is initialized so that it reflects the ASN.1 notation of the class: it is the instance of the ASN1Sequence class that is initialized with instances of the ASN1Oid, ASN1Boolean and ASN1OctetString classes.

The Extensions class has a similar field. The field also reflects the ASN.1 notation: it is the instance of the ASN1SequenceOf class and it is initialized by the ASN1 field from the Extension class.

Figure 3 displays the correspondence between the application object tree and the object tree of ASN.1 custom classes.

Tree

Figure 3: Java Object and ASN.1 Custom Class Trees

Back to Top

Appendix: Usage Examples

This section demonstrates the usage of the ASN.1 framework.

ASN.1 Boolean

An abstract type can be defined as ASN.1 Boolean, for example:

MyBooleanType ::= BOOLEAN;

Then the following code can be used to decode and encode values of this type:

// represents encoded ASN.1 Boolean type: false value
byte encoding[] = new byte[] {0x01, 0x01, 0x00};

// get instance of ASN.1 Boolean decoder/encoder
ASN1Type asn1 = ASN1Boolean.getInstance();

// decoded value is Boolean.FALSE
Boolean value = (Boolean)asn1.decode(encoding);

// encode Boolean.TRUE value

// an array value equals to {0x01, 0x01, 0xFF}
byte enc[] = asn1.encode(Boolean.TRUE);

ASN.1 Tagged Types

The following ASN.1 notation can be used to define a tagged type:

MyTaggedType ::= [APPLICATION 0] EXPLICIT BOOLEAN;

By default, a tagged type, MyTaggedType, is mapped to the same Java* type as a basic type, see ASN.1 BOOLEAN above.

Then the following code can be used to decode and encode the values of this type:

// represents encoded explicitly tagged ASN.1 Boolean type: false value
byte encoding[] = new byte[] {0x60, 0x03, 0x01, 0x01, 0x00};
 
// create an instance of custom decoder/encoder for tagged type
ASN1Type asn1 = new ASN1Explicit(
    ASN1Constants.CLASS_APPLICATION, // tagging class
    0,                               // tag number
    ASN1Boolean.getInstance()        // type to be tagged
);

// decoded value is Boolean.FALSE
Boolean value = (Boolean)asn1.decode(encoding);

// encode Boolean.TRUE value as explicitly tagged

// an array value equals to {0x60, 0x03,0x01, 0x01, 0xFF}
byte enc[] = asn1.encode(Boolean.TRUE);

ASN.1 Sequence Type

A constructed ASN.1 type notation can go as follows.

MyConstructedType ::= SEQUENCE {
    classVersion INTEGER,
    isExtendable BOOLEAN DEFAULT FALSE
}

By default, a sequence type is mapped to an array of objects. In the example, it is an array of two objects: the first object represents classVersion and the second object represents the isExtendable flag.

The following code can be used to decode and encode the values of this type:

// create an instance custom decoder/encoder
ASN1Type asn1 =
    new ASN1Sequence(new ASN1Type[] {ASN1Integer.getInstance(),    // classVersion
                                     ASN1Boolean.getInstance()}) { // isExtendable
        // replace constructor
        {
            // set default value for isExtendable field
            // first parameter - a default value
            // second parameter - an index of ASN.1 type in a sequence starting with 0
            setDefault(Boolean.FALSE, 1);
        }
    };
  
// decoded sequence value is mapped to array of objects
Object value[] = (Object[])asn1.decode(someEncoding);

// first value (ASN.1 Integer) is  mapped by default to an  array of bytes
byte version[] = (byte[])value[0];

// second value (ASN.1 Boolean) is mapped by default to a Boolean object
Boolean isCritical = (Boolean)value[1]; 

When it is necessary to change the default mapping of an array of objects for the ASN.1 Sequence type to a custom Java* class, two methods are overridden.

// class for storing MyConstructedType values
class A {
    int version;
    boolean isExtendable;
}

// create an instance custom decoder/encoder
ASN1Type asn1 =
    new ASN1Sequence(new ASN1Type[] {ASN1Integer.getInstance(),    // classVersion
                                     ASN1Boolean.getInstance()}) { // isExtendable
        // replace constructor
        {
            // set default value for isExtendable field
            // first parameter - a default value
            // second parameter - an index of ASN.1 type in a sequence starting with 0
            setDefault(Boolean.FALSE, 1);
        }

        // for decoding
        public Object getDecodedObject(BerInputStream in) throws IOException {
            Object values[] = (Object[])in.content;

            A a = new A();

            // array of bytes to primitive int value
            a.version = (new BigInteger((byte[])value[0])).intValue;

            // Boolean object to primitive boolean
            a.isExtendable = ((Boolean)value[1]).booleanValue();

            return a;
        }
 
        // for encoding 
        public void getValues(Object object, Object values[]) {
            A a = (A)object;

            // primitive int value to array of bytes
            values[0] = BigInteger.valueOf(a.version).toByteArray();

            // primitive boolean value to Boolean object
            values[1] = Boolean.valueOf(a.isCritical);
        }
    };

// decoded sequence value is mapped to custom A class
A a1 = (A)asn1.decode(someEncoding);

// encode an instance of A class
A a2 = new A();
a2.version = 5;
a2.isExtendable = true;
byte enc[] = asn1.encode(a);

Back to Top

References

[1] Java API Specification, http://java.sun.com/j2se/1.5.0/docs/api/

[2] Abstract Syntax Notation One (ASN.1) Specification of Basic Notation ITU-T Rec. X.680 (2002) | ISO/IEC 8824-1:2002

[3] Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER) ITU-T Rec. X.690 (2002) | ISO/IEC 8825-1:2002

[4] ASN.1 Information Site, http://asn1.elibel.tm.fr/en/standards/index.htm

[5] A Layman's Guide to a Subset of ASN.1, BER, and DER, http://luca.ntop.org/Teaching/Appunti/asn1.html

[6] Olivier Dubuisson, translated by Philippe Fouquart, ASN.1 - Communication between heterogeneous systems, http://www.oss.com/asn1/dubuisson.html

[7] Professor John Larmouth, ASN.1 Complete, http://www.oss.com/asn1/larmouth.html

[8] The Internet Engineering Task Force, Requests for Comments, http://www.ietf.org/

Back to Top

* Other brands and names are the property of their respective owners.