.NET's System.DirectoryServices.Protocols assembly specifies a light-weight ASN.1 BER encoder, but the documentation is not great: BerConverter
.
You call it by specifying a format string and a list of objects to encode.
INTEGER
s can be encoded with format characters i
or e
OCTET STRING
s can be encoded with o
characterSEQUENCE
s can be encoded with {
and }
I'd really like to make use of this simple converter so I don't have to take on an additional dependency. Taking on a dependency is undesirable because C# is callable from Powershell, and it is great to distribute a script that does something fancy with C# so long as it doesn't require an assembly that isn't included with .NET.
However, BerConverter doesn't seem to have a way to specify an Application or Context-Specific tag, which are often used to remove ambiguity in ASN.1, for example when components of a constructed type are marked as OPTIONAL
So, I can encode the following:
BerConverter.Encode("{i{i}}", 1, 2);
Which gives:
30 84 00 00 00 0c 02 01 01 30 84 00 00 00 03 02 01 02
But, if that second sequence needs to be [Application 1]
or 61
... I'm not sure what to put in the format string to emit that in the encoding.
Does BerConverter even have this capability?
I'm not sure why I didn't just do this up front, but I used a decompiler to look at the code. I found that BerConverter.Encode()
is based on ber_printf()
, which has much better documentation.
From the docs:
Character: 't'
Description:
Tag. The next argument is a ber_tag_t that specifies the tag to override the next element written to the BerElement. This works across calls.
So, I was able to change my code to:
BerConverter.Encode("{it{i}}", 1, 0x61, 2)
Which allows me to set the tag value to 0x61
and the code emits:
30 84 00 00 00 0c 02 01 01 61 84 00 00 00 03 02 01 02