Skip to content

Instantly share code, notes, and snippets.

@lionello
Created May 5, 2017 12:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lionello/48cc00ec2aa25ed93f2db683c2f951cb to your computer and use it in GitHub Desktop.
Save lionello/48cc00ec2aa25ed93f2db683c2f951cb to your computer and use it in GitHub Desktop.
Parse a DER or PEM (base64) encoded ASN.1 file (certificates, keys, ...)
import std.file : read;
import std.stdio;
import std.base64;
import std.string;
import std.conv;
import std.algorithm : map;
enum DerTag {
EndOfContent = 0x0,
Bool = 0x1,
Int = 0x2,
Bits = 0x3,
Octets = 0x4,
Null = 0x5,
Oid = 0x6,
Enum = 0xA,
String = 0xC,
Printable = 0x13,
T61String = 0x14,
IA5String = 0x16,
UtcTime = 0x17,
GeneralizedTime = 0x18,
UniversalString = 0x1c,
BmpString = 0x1e,
Seq = 0x30,
Set = 0x31,
Tag0 = 0xa0,
Tag1 = 0xa1,
Tag2 = 0xa2,
Tag3 = 0xa3,
Tag4 = 0xa4,
}
uint[] parseOid(in ubyte[] bits) pure {
int offset = 0;
uint[16] output;
int len = 0;
output[len++] = bits[offset]/40;
output[len++] = bits[offset++]%40;
while (offset < bits.length) {
while (1) {
output[len] += bits[offset] & 0x7F;
if (bits[offset++] < 0x80)
break;
output[len] <<= 7;
}
len++;
}
return output[0..len].dup;
}
/// Utility function to convert byte array into hex chars
char[] toHex(char separator = 0)(in ubyte[] data) pure
{
enum HEX = "0123456789abcdef";
char[] v = new char[data.length*(separator?3:2)];
size_t i;
foreach (b; data) {
v[i++] = HEX[b>>4];
v[i++] = HEX[b&15];
static if (separator)
v[i++] = separator;
}
return v;
}
static assert(toHex([0x61,0x62]) == "6162");
immutable char[80] spaces = ' ';
void parseChunk(int level, in ubyte[] f) {
debug writeln(level, f);
uint offset = 0;
while (offset < f.length) {
DerTag tag = cast(DerTag)f[offset++];
uint len = f[offset++];
bool lenknown = true;
if (len == 0x80) {
lenknown = false;
len = 0;
}
else if (len > 0x80) {
auto lenlen = len - 0x80;
len = 0;
foreach(_;0..lenlen)
len = (len<<8) + f[offset++];
}
writeln(spaces[0..level*2], to!string(tag), ' ', len);
if (tag == DerTag.EndOfContent)
break;
else if (tag >= DerTag.Seq && (tag < 0x80 || f[offset+1] == len-2 || f[offset] == 0x30)) {
parseChunk(level + 1, f[offset..lenknown?offset+len:$]);
}
else if (len) {
write(spaces[0..level*2]);
switch (tag) {
case DerTag.Bits:
len--;
if (f[offset++] != 0x00) {
break;
}
write("Key? ");
case DerTag.Octets:
if (f[offset] == DerTag.Seq || f[offset+1] == len-2) {
writeln("Seq?:");
parseChunk(level, f[offset..offset+len]);
break;
}
default:
write(toHex!' '(f[offset..offset+len]));
break;
case DerTag.GeneralizedTime:
case DerTag.UtcTime:
case DerTag.Printable:
case DerTag.IA5String:
case DerTag.String:
write(cast(char[])f[offset..offset+len]);
break;
case DerTag.T61String:
// TODO: decode to UTF8
write(cast(char[])f[offset..offset+len]);
break;
case DerTag.Bool:
write(f[offset] != 0);
break;
case DerTag.Oid:
write(parseOid(f[offset..offset+len]).map!(to!string).join('.'));
break;
}
writeln();
}
offset += len;
}
debug assert(offset == f.length);
}
void main(string[] args)
{
ubyte[] f = cast(ubyte[])read(args[1]);
if (f[0] == '-') {
char[] s = cast(char[])f;
auto firstline = s.indexOf('\n');
if (firstline > 0)
s = s[firstline+1..$];
auto epilogue = s.indexOf('-');
if (epilogue > 0)
s = s[0..epilogue];
s = replace(s, "\n","");
parseChunk(0, Base64.decode(s));
}
else {
parseChunk(0, f);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment