blob: e763e4477beb228a649fd820bc9b801b632ae6fd [file] [log] [blame] [view]
# 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 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>
### 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:
```dart
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.
```dart
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 :
```bash
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
```
```dart
// 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.
```dart
// 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 :
```bash
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.
```dart
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:
```bash
-----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 :
```bash
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
```