ASN1

Pointycastle has a build in ASN1 support to handle the most common ASN1 objects.

Overview

The package contains an ASN1Parser that can parse the ASN1 objects, like ASN1Integer, ASN1Sequence, ASN1BitString and many more.

Supported ASN1 objects

The following table lists all supported types and the possible encoding and decoding rules supported.

ASN1 TypeDecode FromEncode To
BOOLEANDERDER
INTEGERDERDER
BIT_STRINGDER / BER Constructed / BER Long Length Form / BER Padded / BER Constructed Indefinite LengthDER / BER Constructed / BER Long Length Form / BER Padded / BER Constructed Indefinite Length
OCTET_STRINGDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite LengthDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length
NULLDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite LengthDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length
OBJECT_IDENTIFIERDERDER
ENUMERATEDDERDER
UTF8_STRINGDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite LengthDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length
SEQUENCEDERDER
SETDERDER
PRINTABLE_STRINGDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite LengthDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length
IA5_STRINGDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite LengthDER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length
UTC_TIMEDERDER

More information about the different encoding rules can be found at https://www.bouncycastle.org/asn1_layman_93.txt

Working with ASN1 objects

Every ASN1 object can be instanced in several ways.

Named constructor fromBytes

The fromBytes constructed will create an instance according to the given bytes. It detects automatically the encoding rule used within the given bytes.

Default constructor

The default constructor takes different arguments, depending on the object. Every default constructor automatically sets the right ASN1 tag. If needed the tag can also be overridden.

Example for the default constructor of an ASN1UTF8String entity:

var asn1Object = ASN1UTF8String(utf8StringValue: 'Hello World');

If an ASN1 object supports constructed encoding/decoding, the default constructor allows to pass a list of child elements. In this case, the tag needs to be set manually to the right constructed tag.

var e1 = ASN1UTF8String(utf8StringValue: 'Hello');

var e2 = ASN1UTF8String(utf8StringValue: ' World');

varar asn1Object = ASN1UTF8String(elements: [e1, e2], tag: ASN1Tags.UTF8_STRING_CONSTRUCTED);

Parsing ASN1

To parse an ASN1Object, we need to pass the bytes representing the object to the parser. In this example we use the following structure :

   SEQUENCE (4 elem)
      SET (1 elem)
        SEQUENCE (2 elem)
          OBJECT IDENTIFIER 2.5.4.6 countryName (X.520 DN component)
          PrintableString US
      SET (1 elem)
        SEQUENCE (2 elem)
          OBJECT IDENTIFIER 2.5.4.10 organizationName (X.520 DN component)
          PrintableString DigiCert Inc
      SET (1 elem)
        SEQUENCE (2 elem)
          OBJECT IDENTIFIER 2.5.4.11 organizationalUnitName (X.520 DN component)
          PrintableString www.digicert.com
      SET (1 elem)
        SEQUENCE (2 elem)
          OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
          PrintableString Thawte RSA CA 2018
// Base64 encoded string that represents the above structure
var base64String =
        'MFwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xGzAZBgNVBAMTElRoYXd0ZSBSU0EgQ0EgMjAxOA==';
// Convert to byte list
var bytes = base64.decode(base64String);
// Pass the byte list to the ASN1Parser
var parser = ASN1Parser(bytes);

Now we can use the parser to move between every object within the above structure. The first element is an ASN1Sequence with 4 child elements.

// Grab the first element by calling nextObject() and cast it to ASN1Sequence
var sequence = parser.nextObject() as ASN1Sequence;
// Iterate over each element
sequence.elements.forEach((element) {
    var asn1Set = element as ASN1Set;
    var seq = asn1Set.elements.elementAt(0) as ASN1Sequence
    var objectIdentifier = seq.elements.elementAt(0) as ASN1ObjectIdentifier;
    var printableString = seq.elements.elementAt(1) as ASN1PrintableString;
    print('${objectIdentifier.objectIdentifierAsString} = ${printableString.stringValue}')
});

The above code will print out the following lines :

2.5.4.6 = US
2.5.4.10 = DigiCert Inc
2.5.4.11 = www.digicert.com
2.5.4.3 = Thawte RSA CA 2018

Encoding ASN1

The ASN1 objects can be also used to create ASN1 structures and encode them and process them as base64 encoded string in other software. As an example, we will create a RSA Public Key PEM string.


final BEGIN_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----'; final END_PUBLIC_KEY = '-----END PUBLIC KEY-----'; // Create a new RSA pulic key var publicKey; // Create the top level sequence var topLevelSeq = ASN1Sequence(); // Create the sequence holding the algorithm information var algorithmSeq = ASN1Sequence(); var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0])); algorithmSeq.add(ASN1ObjectIdentifier.fromComponentString('1.2.840.113549.1.1.1')); algorithmSeq.add(paramsAsn1Obj); // Create the constructed ASN1BitString var modulus = ASN1Integer(publicKey.modulus); var exponent = ASN1Integer(publicKey.exponent); var publicKeySeqBitString = ASN1BitString(elements : [modulus, exponent], tag: ASN1Tags.BIT_STRING_CONSTRUCTED); // Add ASN1 objects to the top level sequence topLevelSeq.add(algorithmSeq); topLevelSeq.add(publicKeySeqBitString); // Encode base64 var dataBase64 = base64.encode(topLevelSeq.encodedBytes); var chunks = StringUtils.chunk(dataBase64, 64); print('$BEGIN_PUBLIC_KEY\n${chunks.join('\n')}\n$END_PUBLIC_KEY');

The result will be something similar to this:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg1ea9Y7zO2Gt7kzYOSp5
I4dZHY75ZCYNMegmg0obKVxauRXeBL+pfhuDgq1+k0Z3iOfOELczOgaonTsJiHo8
kQcqgYNj0dmKKb+/318+3aEIZa6PreIgWJ0scM0oupHeiEA/M45vQIDMOv6jjVa5
mB1u/nHXQpvzH1i4H9tlODTSvIoIabW2/14+JNIm1KNQz/H/NsbooraY6POvqGtr
Ek743CRJNH9mgEYEQnOF0dYiS8h+JoXxcIDuaLz/WCRx1AWvINj3YNBgiIc6N4/Y
2nv5lAKajhfh8grs7hCRvXUzT9wcvj9aNtmOHm4cH/WpVK4sXGuLxN1MxDyqaPIv
9QIDAQAB
-----END PUBLIC KEY-----

And the ASN1 structure looks like this :

SEQUENCE (2 elem)
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
    NULL
  BIT STRING (2160 bit) 001100001000001000000001000010100000001010000010000000010000000100000
    SEQUENCE (2 elem)
      INTEGER (2048 bit) 165804177387087149617220457493747588873959131224426853644136343567339
      INTEGER 65537