Created
May 5, 2017 12:16
-
-
Save lionello/48cc00ec2aa25ed93f2db683c2f951cb to your computer and use it in GitHub Desktop.
Parse a DER or PEM (base64) encoded ASN.1 file (certificates, keys, ...)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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