/flash-src/third-party/com/hurlant/util/der/DER.as
ActionScript | 210 lines | 163 code | 7 blank | 40 comment | 26 complexity | 346d733225a2c57a680b24f060381ce9 MD5 | raw file
1/** 2 * DER 3 * 4 * A basic class to parse DER structures. 5 * It is very incomplete, but sufficient to extract whatever data we need so far. 6 * Copyright (c) 2007 Henri Torgemane 7 * 8 * See LICENSE.txt for full license information. 9 */ 10package com.hurlant.util.der 11{ 12 import com.hurlant.math.BigInteger; 13 14 import flash.utils.ByteArray; 15 import com.hurlant.util.der.Sequence; 16 import com.hurlant.util.Hex; 17 18 // goal 1: to be able to parse an RSA Private Key PEM file. 19 // goal 2: to parse an X509v3 cert. kinda. 20 21 /** 22 * DER for dummies: 23 * http://luca.ntop.org/Teaching/Appunti/asn1.html 24 * 25 * This class does the bare minimum to get by. if that. 26 */ 27 public class DER 28 { 29 public static var indent:String = ""; 30 31 public static function parse(der:ByteArray, structure:*=null):IAsn1Type { 32/* if (der.position==0) { 33 trace("DER.parse: "+Hex.fromArray(der)); 34 } 35 */ // type 36 var type:int = der.readUnsignedByte(); 37 var constructed:Boolean = (type&0x20)!=0; 38 type &=0x1F; 39 // length 40 var len:int = der.readUnsignedByte(); 41 if (len>=0x80) { 42 // long form of length 43 var count:int = len & 0x7f; 44 len = 0; 45 while (count>0) { 46 len = (len<<8) | der.readUnsignedByte(); 47 count--; 48 } 49 } 50 // data 51 var b:ByteArray 52 switch (type) { 53 case 0x00: // WHAT IS THIS THINGY? (seen as 0xa0) 54 // (note to self: read a spec someday.) 55 // for now, treat as a sequence. 56 case 0x10: // SEQUENCE/SEQUENCE OF. whatever 57 // treat as an array 58 var p:int = der.position; 59 var o:Sequence = new Sequence(type, len); 60 var arrayStruct:Array = structure as Array; 61 if (arrayStruct!=null) { 62 // copy the array, as we destroy it later. 63 arrayStruct = arrayStruct.concat(); 64 } 65 while (der.position < p+len) { 66 var tmpStruct:Object = null 67 if (arrayStruct!=null) { 68 tmpStruct = arrayStruct.shift(); 69 } 70 if (tmpStruct!=null) { 71 while (tmpStruct && tmpStruct.optional) { 72 // make sure we have something that looks reasonable. XXX I'm winging it here.. 73 var wantConstructed:Boolean = (tmpStruct.value is Array); 74 var isConstructed:Boolean = isConstructedType(der); 75 if (wantConstructed!=isConstructed) { 76 // not found. put default stuff, or null 77 o.push(tmpStruct.defaultValue); 78 o[tmpStruct.name] = tmpStruct.defaultValue; 79 // try the next thing 80 tmpStruct = arrayStruct.shift(); 81 } else { 82 break; 83 } 84 } 85 } 86 if (tmpStruct!=null) { 87 var name:String = tmpStruct.name; 88 var value:* = tmpStruct.value; 89 if (tmpStruct.extract) { 90 // we need to keep a binary copy of this element 91 var size:int = getLengthOfNextElement(der); 92 var ba:ByteArray = new ByteArray; 93 ba.writeBytes(der, der.position, size); 94 o[name+"_bin"] = ba; 95 } 96 var obj:IAsn1Type = DER.parse(der, value); 97 o.push(obj); 98 o[name] = obj; 99 } else { 100 o.push(DER.parse(der)); 101 } 102 } 103 return o; 104 case 0x11: // SET/SET OF 105 p = der.position; 106 var s:Set = new Set(type, len); 107 while (der.position < p+len) { 108 s.push(DER.parse(der)); 109 } 110 return s; 111 case 0x02: // INTEGER 112 // put in a BigInteger 113 b = new ByteArray; 114 der.readBytes(b,0,len); 115 b.position=0; 116 return new Integer(type, len, b); 117 case 0x06: // OBJECT IDENTIFIER: 118 b = new ByteArray; 119 der.readBytes(b,0,len); 120 b.position=0; 121 return new ObjectIdentifier(type, len, b); 122 default: 123 trace("I DONT KNOW HOW TO HANDLE DER stuff of TYPE "+type); 124 // fall through 125 case 0x03: // BIT STRING 126 if (der[der.position]==0) { 127 //trace("Horrible Bit String pre-padding removal hack."); // I wish I had the patience to find a spec for this. 128 der.position++; 129 len--; 130 } 131 case 0x04: // OCTET STRING 132 // stuff in a ByteArray for now. 133 var bs:ByteString = new ByteString(type, len); 134 der.readBytes(bs,0,len); 135 return bs; 136 case 0x05: // NULL 137 // if len!=0, something's horribly wrong. 138 // should I check? 139 return null; 140 case 0x13: // PrintableString 141 var ps:PrintableString = new PrintableString(type, len); 142 ps.setString(der.readMultiByte(len, "US-ASCII")); 143 return ps; 144 case 0x22: // XXX look up what this is. openssl uses this to store my email. 145 case 0x14: // T61String - an horrible format we don't even pretend to support correctly 146 ps = new PrintableString(type, len); 147 ps.setString(der.readMultiByte(len, "latin1")); 148 return ps; 149 case 0x17: // UTCTime 150 var ut:UTCTime = new UTCTime(type, len); 151 ut.setUTCTime(der.readMultiByte(len, "US-ASCII")); 152 return ut; 153 } 154 } 155 156 private static function getLengthOfNextElement(b:ByteArray):int { 157 var p:uint = b.position; 158 // length 159 b.position++; 160 var len:int = b.readUnsignedByte(); 161 if (len>=0x80) { 162 // long form of length 163 var count:int = len & 0x7f; 164 len = 0; 165 while (count>0) { 166 len = (len<<8) | b.readUnsignedByte(); 167 count--; 168 } 169 } 170 len += b.position-p; // length of length 171 b.position = p; 172 return len; 173 } 174 private static function isConstructedType(b:ByteArray):Boolean { 175 var type:int = b[b.position]; 176 return (type&0x20)!=0; 177 } 178 179 public static function wrapDER(type:int, data:ByteArray):ByteArray { 180 var d:ByteArray = new ByteArray; 181 d.writeByte(type); 182 var len:int = data.length; 183 if (len<128) { 184 d.writeByte(len); 185 } else if (len<256) { 186 d.writeByte(1 | 0x80); 187 d.writeByte(len); 188 } else if (len<65536) { 189 d.writeByte(2 | 0x80); 190 d.writeByte(len>>8); 191 d.writeByte(len); 192 } else if (len<65536*256) { 193 d.writeByte(3 | 0x80); 194 d.writeByte(len>>16); 195 d.writeByte(len>>8); 196 d.writeByte(len); 197 } else { 198 d.writeByte(4 | 0x80); 199 d.writeByte(len>>24); 200 d.writeByte(len>>16); 201 d.writeByte(len>>8); 202 d.writeByte(len); 203 } 204 d.writeBytes(data); 205 d.position=0; 206 return d; 207 208 } 209 } 210}