Skip to content

Latest commit

 

History

History
391 lines (318 loc) · 11.2 KB

kerby-asn1.md

File metadata and controls

391 lines (318 loc) · 11.2 KB

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