Pointycastle has a build in ASN1 support to handle the most common ASN1 objects.
The package contains an ASN1Parser that can parse the ASN1 objects, like ASN1Integer, ASN1Sequence, ASN1BitString and many more.
The following table lists all supported types and the possible encoding and decoding rules supported.
ASN1 Type | Decode From | Encode To |
---|---|---|
BOOLEAN | DER | DER |
INTEGER | DER | DER |
BIT_STRING | DER / BER Constructed / BER Long Length Form / BER Padded / BER Constructed Indefinite Length | DER / BER Constructed / BER Long Length Form / BER Padded / BER Constructed Indefinite Length |
OCTET_STRING | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length |
NULL | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length |
OBJECT_IDENTIFIER | DER | DER |
ENUMERATED | DER | DER |
UTF8_STRING | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length |
SEQUENCE | DER | DER |
SET | DER | DER |
PRINTABLE_STRING | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length |
IA5_STRING | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length | DER / BER Constructed / BER Long Length Form / BER Constructed Indefinite Length |
UTC_TIME | DER | DER |
More information about the different encoding rules can be found at https://www.bouncycastle.org/asn1_layman_93.txt
Every ASN1 object can be instanced in several ways.
The fromBytes constructed will create an instance according to the given bytes. It detects automatically the encoding rule used within the given bytes.
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);
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
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