Kerby ASN1

ASN1 hierarcy

A ASN1 parser with easy and simple API

// encoding
Asn1Integer aValue = new Asn1Integer(8899);
byte[] encoded = aValue.encode();

// decoding
byte[] contentToDecode = ...
Asn1Integer decodedValue = new Asn1Integer();
decodedValue.decode(contentToDecode);
Integer value = decodedValue.getValue();

Data-driven ASN1 encoding/decoding framework and parser

With the following definition from Kerberos protocol

AuthorizationData ::= SEQUENCE OF SEQUENCE {
     ad-type         [0] Int32,
     ad-data         [1] OCTET STRING
 }

You can model AuthzDataEntry as follows

public class AuthorizationDataEntry extends KrbSequenceType {
/**
 * The possible fields
 */
protected enum AuthorizationDataEntryField implements EnumType {
    AD_TYPE,
    AD_DATA;

    /**
     * {@inheritDoc}
     */
    @Override
    public int getValue() {
        return ordinal();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
        return name();
    }
}

/** The AuthorizationDataEntry's fields */
private static Asn1FieldInfo[] fieldInfos = new Asn1FieldInfo[] {
        new ExplicitField(AuthorizationDataEntryField.AD_TYPE, Asn1Integer.class),
        new ExplicitField(AuthorizationDataEntryField.AD_DATA, Asn1OctetString.class)
};

/**
 * Creates an AuthorizationDataEntry instance
 */
public AuthorizationDataEntry() {
    super(fieldInfos);
}

/**
 * @return The AuthorizationType (AD_TYPE) field
 */
public AuthorizationType getAuthzType() {
    Integer value = getFieldAsInteger(AuthorizationDataEntryField.AD_TYPE);
    return AuthorizationType.fromValue(value);
}

/**
 * Sets the AuthorizationType (AD_TYPE) field
 * @param authzType The AuthorizationType to set
 */
public void setAuthzType(AuthorizationType authzType) {
    setFieldAsInt(AuthorizationDataEntryField.AD_TYPE, authzType.getValue());
}

/**
 * @return The AuthorizationType (AD_DATA) field
 */
public byte[] getAuthzData() {
    return getFieldAsOctets(AuthorizationDataEntryField.AD_DATA);
}

/**
 * Sets the AuthorizationData (AD_DATA) field
 * @param authzData The AuthorizationData to set
 */
public void setAuthzData(byte[] authzData) {
    setFieldAsOctets(AuthorizationDataEntryField.AD_DATA, authzData);
}
}

And then define AuthorizationData simply

public class AuthorizationData extends KrbSequenceOfType<AuthorizationDataEntry> {

}

Then you can process with above definitions, encode and decode, without caring about the details.

Think about how to implement the following more complex and pratical sample from ITU-T Rec. X.680 ISO/IEC 8824-1:

A.1 ASN.1 description of the record structure
The structure of the hypothetical personnel record is formally described below using ASN.1 specified in
ITU-T Rec. X.680 | ISO/IEC 8824-1 for defining types.

PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
    Name Name,
    title [0] VisibleString,
    number EmployeeNumber,
    dateOfHire [1] Date,
    nameOfSpouse [2] Name,
    children [3] IMPLICIT
    SEQUENCE OF ChildInformation DEFAULT {}
}

ChildInformation ::= SET {
    name Name,
    dateOfBirth [0] Date
}

Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
    givenName VisibleString,
    initial VisibleString,
    familyName VisibleString
}

EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD ``` Similarly as above, we can have (from the unit test codes):

public class PersonnelRecord extends Asn1TaggingSet {
protected enum PersonnelRecordField implements EnumType {
    NAME,
    TITLE,
    NUMBER,
    DATE_OF_HIRE,
    NAME_OF_SPOUSE,
    CHILDREN;

    @Override
    public int getValue() {
        return ordinal();
    }

    @Override
    public String getName() {
        return name();
    }
}

static Asn1FieldInfo[] fieldInfos = new Asn1FieldInfo[] {
        new ExplicitField(PersonnelRecordField.NAME, -1, Name.class),
        new ExplicitField(PersonnelRecordField.TITLE, 0, Asn1VisibleString.class),
        new ExplicitField(PersonnelRecordField.NUMBER, -1, EmployeeNumber.class),
        new ExplicitField(PersonnelRecordField.DATE_OF_HIRE, 1, Date.class),
        new ExplicitField(PersonnelRecordField.NAME_OF_SPOUSE, 2, Name.class),
        new ImplicitField(PersonnelRecordField.CHILDREN, 3, Children.class)
};

public PersonnelRecord() {
    super(0, fieldInfos, true, true);
}

public void setName(Name name) {
    setFieldAs(PersonnelRecordField.NAME, name);
}

public Name getName() {
    return getFieldAs(PersonnelRecordField.NAME, Name.class);
}

public void setTitle(String title) {
    setFieldAs(PersonnelRecordField.TITLE, new Asn1VisibleString(title));
}

public String getTitle() {
    return getFieldAsString(PersonnelRecordField.TITLE);
}

public void setEmployeeNumber(EmployeeNumber employeeNumber) {
    setFieldAs(PersonnelRecordField.NUMBER, employeeNumber);
}

public EmployeeNumber getEmployeeNumber() {
    return getFieldAs(PersonnelRecordField.NUMBER, EmployeeNumber.class);
}

public void setDateOfHire(Date dateOfHire) {
    setFieldAs(PersonnelRecordField.DATE_OF_HIRE, dateOfHire);
}

public Date getDateOfHire() {
    return getFieldAs(PersonnelRecordField.DATE_OF_HIRE, Date.class);
}

public void setNameOfSpouse(Name spouse) {
    setFieldAs(PersonnelRecordField.NAME_OF_SPOUSE, spouse);
}

public Name getNameOfSpouse() {
    return getFieldAs(PersonnelRecordField.NAME_OF_SPOUSE, Name.class);
}

public void setChildren(Children children) {
    setFieldAs(PersonnelRecordField.CHILDREN, children);
}

public Children getChildren() {
    return getFieldAs(PersonnelRecordField.CHILDREN, Children.class);
}

public static class Children extends Asn1SequenceOf<ChildInformation> {
    public Children(ChildInformation ... children) {
        super();
        for (ChildInformation child : children) {
            addElement(child);
        }
    }

    public Children() {
        super();
    }
}

public static class ChildInformation extends Asn1SetType {
    protected enum ChildInformationField implements EnumType {
        CHILD_NAME,
        DATE_OF_BIRTH;

        @Override
        public int getValue() {
            return ordinal();
        }

        @Override
        public String getName() {
            return name();
        }
    }

    static Asn1FieldInfo[] tags = new Asn1FieldInfo[] {
            new ExplicitField(ChildInformationField.CHILD_NAME, -1, Name.class),
            new ExplicitField(ChildInformationField.DATE_OF_BIRTH, 0, Date.class)
    };

    public ChildInformation() {
        super(tags);
    }

    public void setName(Name name) {
        setFieldAs(ChildInformationField.CHILD_NAME, name);
    }

    public Name getName() {
        return getFieldAs(ChildInformationField.CHILD_NAME, Name.class);
    }

    public void setDateOfBirth(Date date) {
        setFieldAs(ChildInformationField.DATE_OF_BIRTH, date);
    }

    public Date getDateOfBirth() {
        return getFieldAs(ChildInformationField.DATE_OF_BIRTH, Date.class);
    }
}

public static class Name extends Asn1TaggingSequence {

    protected enum NameField implements EnumType {
        GIVENNAME,
        INITIAL,
        FAMILYNAME;

        @Override
        public int getValue() {
            return ordinal();
        }

        @Override
        public String getName() {
            return name();
        }
    }

    static Asn1FieldInfo[] tags = new Asn1FieldInfo[] {
            new ExplicitField(NameField.GIVENNAME, -1, Asn1VisibleString.class),
            new ExplicitField(NameField.INITIAL, -1, Asn1VisibleString.class),
            new ExplicitField(NameField.FAMILYNAME, -1, Asn1VisibleString.class)
    };

    public Name() {
        super(1, tags, true, true);
    }

    public Name(String givenName, String initial, String familyName) {
        this();
        setGivenName(givenName);
        setInitial(initial);
        setFamilyName(familyName);
    }

    public void setGivenName(String givenName) {
        setFieldAs(NameField.GIVENNAME, new Asn1VisibleString(givenName));
    }

    public String getGivenName() {
        return getFieldAsString(NameField.GIVENNAME);
    }

    public void setInitial(String initial) {
        setFieldAs(NameField.INITIAL, new Asn1VisibleString(initial));
    }

    public String getInitial() {
        return getFieldAsString(NameField.INITIAL);
    }

    public void setFamilyName(String familyName) {
        setFieldAs(NameField.FAMILYNAME, new Asn1VisibleString(familyName));
    }

    public String getFamilyName() {
        return getFieldAsString(NameField.FAMILYNAME);
    }
}

public static class EmployeeNumber extends Asn1Tagging<Asn1Integer> {
    public EmployeeNumber(Integer value) {
        super(2, new Asn1Integer(value), true, true);
    }

    public EmployeeNumber() {
        super(2, new Asn1Integer(), true, true);
    }
}

public static class Date extends Asn1Tagging<Asn1VisibleString> {
    public Date(String value) {
        super(3, new Asn1VisibleString(value), true, true);
    }
    public Date() {
        this(null);
    }
}
}

Asn1 API and parsing/dumping facilities

  • ASN1 dumping tool to help analyze ASN1 encoding stream or packet. It can be used to exercise the framework with all kinds of testing binary inputs.
  • The shortcut API for ASN1 parser, encoding, decoding.

Notes

  • Extensive tests coverage for BER & DER encoding and decoding
  • Fully self-contained, no extra dependency

License

Apache V2 License