4 * A basic class to parse DER structures.
\r
5 * It is very incomplete, but sufficient to extract whatever data we need so far.
\r
6 * Copyright (c) 2007 Henri Torgemane
\r
8 * See LICENSE.txt for full license information.
\r
10 package com.hurlant.util.der
\r
12 import com.hurlant.math.BigInteger;
\r
14 import flash.utils.ByteArray;
\r
15 import com.hurlant.util.der.Sequence;
\r
16 import com.hurlant.util.Hex;
\r
18 // goal 1: to be able to parse an RSA Private Key PEM file.
\r
19 // goal 2: to parse an X509v3 cert. kinda.
\r
23 * http://luca.ntop.org/Teaching/Appunti/asn1.html
\r
25 * This class does the bare minimum to get by. if that.
\r
29 public static var indent:String = "";
\r
31 public static function parse(der:ByteArray, structure:*=null):IAsn1Type {
\r
32 /* if (der.position==0) {
\r
33 trace("DER.parse: "+Hex.fromArray(der));
\r
36 var type:int = der.readUnsignedByte();
\r
37 var constructed:Boolean = (type&0x20)!=0;
\r
40 var len:int = der.readUnsignedByte();
\r
42 // long form of length
\r
43 var count:int = len & 0x7f;
\r
46 len = (len<<8) | der.readUnsignedByte();
\r
53 case 0x00: // WHAT IS THIS THINGY? (seen as 0xa0)
\r
54 // (note to self: read a spec someday.)
\r
55 // for now, treat as a sequence.
\r
56 case 0x10: // SEQUENCE/SEQUENCE OF. whatever
\r
57 // treat as an array
\r
58 var p:int = der.position;
\r
59 var o:Sequence = new Sequence(type, len);
\r
60 var arrayStruct:Array = structure as Array;
\r
61 if (arrayStruct!=null) {
\r
62 // copy the array, as we destroy it later.
\r
63 arrayStruct = arrayStruct.concat();
\r
65 while (der.position < p+len) {
\r
66 var tmpStruct:Object = null
\r
67 if (arrayStruct!=null) {
\r
68 tmpStruct = arrayStruct.shift();
\r
70 if (tmpStruct!=null) {
\r
71 while (tmpStruct && tmpStruct.optional) {
\r
72 // make sure we have something that looks reasonable. XXX I'm winging it here..
\r
73 var wantConstructed:Boolean = (tmpStruct.value is Array);
\r
74 var isConstructed:Boolean = isConstructedType(der);
\r
75 if (wantConstructed!=isConstructed) {
\r
76 // not found. put default stuff, or null
\r
77 o.push(tmpStruct.defaultValue);
\r
78 o[tmpStruct.name] = tmpStruct.defaultValue;
\r
79 // try the next thing
\r
80 tmpStruct = arrayStruct.shift();
\r
86 if (tmpStruct!=null) {
\r
87 var name:String = tmpStruct.name;
\r
88 var value:* = tmpStruct.value;
\r
89 if (tmpStruct.extract) {
\r
90 // we need to keep a binary copy of this element
\r
91 var size:int = getLengthOfNextElement(der);
\r
92 var ba:ByteArray = new ByteArray;
\r
93 ba.writeBytes(der, der.position, size);
\r
94 o[name+"_bin"] = ba;
\r
96 var obj:IAsn1Type = DER.parse(der, value);
\r
100 o.push(DER.parse(der));
\r
104 case 0x11: // SET/SET OF
\r
106 var s:Set = new Set(type, len);
\r
107 while (der.position < p+len) {
\r
108 s.push(DER.parse(der));
\r
111 case 0x02: // INTEGER
\r
112 // put in a BigInteger
\r
114 der.readBytes(b,0,len);
\r
116 return new Integer(type, len, b);
\r
117 case 0x06: // OBJECT IDENTIFIER:
\r
119 der.readBytes(b,0,len);
\r
121 return new ObjectIdentifier(type, len, b);
\r
123 trace("I DONT KNOW HOW TO HANDLE DER stuff of TYPE "+type);
\r
125 case 0x03: // BIT STRING
\r
126 if (der[der.position]==0) {
\r
127 //trace("Horrible Bit String pre-padding removal hack."); // I wish I had the patience to find a spec for this.
\r
131 case 0x04: // OCTET STRING
\r
132 // stuff in a ByteArray for now.
\r
133 var bs:ByteString = new ByteString(type, len);
\r
134 der.readBytes(bs,0,len);
\r
137 // if len!=0, something's horribly wrong.
\r
140 case 0x13: // PrintableString
\r
141 var ps:PrintableString = new PrintableString(type, len);
\r
142 ps.setString(der.readMultiByte(len, "US-ASCII"));
\r
144 case 0x22: // XXX look up what this is. openssl uses this to store my email.
\r
145 case 0x14: // T61String - an horrible format we don't even pretend to support correctly
\r
146 ps = new PrintableString(type, len);
\r
147 ps.setString(der.readMultiByte(len, "latin1"));
\r
149 case 0x17: // UTCTime
\r
150 var ut:UTCTime = new UTCTime(type, len);
\r
151 ut.setUTCTime(der.readMultiByte(len, "US-ASCII"));
\r
156 private static function getLengthOfNextElement(b:ByteArray):int {
\r
157 var p:uint = b.position;
\r
160 var len:int = b.readUnsignedByte();
\r
162 // long form of length
\r
163 var count:int = len & 0x7f;
\r
166 len = (len<<8) | b.readUnsignedByte();
\r
170 len += b.position-p; // length of length
\r
174 private static function isConstructedType(b:ByteArray):Boolean {
\r
175 var type:int = b[b.position];
\r
176 return (type&0x20)!=0;
\r
179 public static function wrapDER(type:int, data:ByteArray):ByteArray {
\r
180 var d:ByteArray = new ByteArray;
\r
182 var len:int = data.length;
\r
185 } else if (len<256) {
\r
186 d.writeByte(1 | 0x80);
\r
188 } else if (len<65536) {
\r
189 d.writeByte(2 | 0x80);
\r
190 d.writeByte(len>>8);
\r
192 } else if (len<65536*256) {
\r
193 d.writeByte(3 | 0x80);
\r
194 d.writeByte(len>>16);
\r
195 d.writeByte(len>>8);
\r
198 d.writeByte(4 | 0x80);
\r
199 d.writeByte(len>>24);
\r
200 d.writeByte(len>>16);
\r
201 d.writeByte(len>>8);
\r
204 d.writeBytes(data);
\r