no commit message
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
import flash.utils.ByteArray;
|
||||
import com.hurlant.util.Hex;
|
||||
|
||||
public class ByteString extends ByteArray implements IAsn1Type
|
||||
{
|
||||
private var type:uint;
|
||||
private var len:uint;
|
||||
|
||||
public function ByteString(type:uint, length:uint) {
|
||||
this.type = type;
|
||||
this.len = length;
|
||||
}
|
||||
|
||||
public function getLength():uint
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
public function getType():uint
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
override public function toString():String {
|
||||
return DER.indent+"ByteString["+type+"]["+len+"]["+Hex.fromArray(this)+"]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
import com.hurlant.math.BigInteger;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import com.hurlant.util.der.Sequence;
|
||||
|
||||
// goal 1: to be able to parse an RSA Private Key PEM file.
|
||||
// goal 2: to parse an X509v3 cert. kinda.
|
||||
|
||||
/**
|
||||
* DER for dummies:
|
||||
* http://luca.ntop.org/Teaching/Appunti/asn1.html
|
||||
*
|
||||
* This class does the bare minimum to get by. if that.
|
||||
*/
|
||||
public class DER
|
||||
{
|
||||
public static var indent:String = "";
|
||||
|
||||
public static function parse(der:ByteArray, structure:*=null):IAsn1Type {
|
||||
// type
|
||||
var type:int = der.readUnsignedByte();
|
||||
var constructed:Boolean = (type&0x20)!=0;
|
||||
type &=0x1F;
|
||||
// length
|
||||
var len:int = der.readUnsignedByte();
|
||||
if (len>=0x80) {
|
||||
// long form of length
|
||||
var count:int = len & 0x7f;
|
||||
len = 0;
|
||||
while (count>0) {
|
||||
len = (len<<8) | der.readUnsignedByte();
|
||||
count--;
|
||||
}
|
||||
}
|
||||
// data
|
||||
var b:ByteArray
|
||||
switch (type) {
|
||||
case 0x00: // WHAT IS THIS THINGY? (seen as 0xa0)
|
||||
// (note to self: read a spec someday.)
|
||||
// for now, treat as a sequence.
|
||||
case 0x10: // SEQUENCE/SEQUENCE OF. whatever
|
||||
// treat as an array
|
||||
var p:int = der.position;
|
||||
var o:Sequence = new Sequence(type, len);
|
||||
var arrayStruct:Array = structure as Array;
|
||||
while (der.position < p+len) {
|
||||
var tmpStruct:Object = null
|
||||
if (arrayStruct!=null) {
|
||||
tmpStruct = arrayStruct.shift();
|
||||
}
|
||||
if (tmpStruct!=null) {
|
||||
var name:String = tmpStruct.name;
|
||||
var value:* = tmpStruct.value;
|
||||
var obj:IAsn1Type = DER.parse(der, value);
|
||||
o.push(obj);
|
||||
o[name] = obj;
|
||||
} else {
|
||||
o.push(DER.parse(der));
|
||||
}
|
||||
}
|
||||
return o;
|
||||
case 0x11: // SET/SET OF
|
||||
p = der.position;
|
||||
var s:Set = new Set(type, len);
|
||||
while (der.position < p+len) {
|
||||
s.push(DER.parse(der));
|
||||
}
|
||||
return s;
|
||||
case 0x02: // INTEGER
|
||||
// put in a BigInteger
|
||||
b = new ByteArray;
|
||||
der.readBytes(b,0,len);
|
||||
b.position=0;
|
||||
return new Integer(type, len, b);
|
||||
case 0x06: // OBJECT IDENTIFIER:
|
||||
b = new ByteArray;
|
||||
der.readBytes(b,0,len);
|
||||
b.position=0;
|
||||
return new ObjectIdentifier(type, len, b);
|
||||
default:
|
||||
trace("I DONT KNOW HOW TO HANDLE DER stuff of TYPE "+type);
|
||||
// fall through
|
||||
case 0x04: // OCTET STRING
|
||||
case 0x03: // BIT STRING
|
||||
// stuff in a ByteArray for now.
|
||||
var bs:ByteString = new ByteString(type, len);
|
||||
der.readBytes(bs,0,len);
|
||||
return bs;
|
||||
case 0x05: // NULL
|
||||
// if len!=0, something's horribly wrong.
|
||||
// should I check?
|
||||
return null;
|
||||
case 0x13: // PrintableString
|
||||
var ps:PrintableString = new PrintableString(type, len);
|
||||
ps.setString(der.readMultiByte(len, "US-ASCII"));
|
||||
return ps;
|
||||
case 0x14: // T61String
|
||||
ps = new PrintableString(type, len);
|
||||
ps.setString(der.readMultiByte(len, "latin1"));
|
||||
return ps;
|
||||
case 0x17: // UTCTime
|
||||
var ut:UTCTime = new UTCTime(type, len);
|
||||
ut.setUTCTime(der.readMultiByte(len, "US-ASCII"));
|
||||
return ut;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public interface IAsn1Type
|
||||
{
|
||||
function getType():uint;
|
||||
function getLength():uint;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
import com.hurlant.math.BigInteger;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
public class Integer extends BigInteger implements IAsn1Type
|
||||
{
|
||||
private var type:uint;
|
||||
private var len:uint;
|
||||
|
||||
public function Integer(type:uint, length:uint, b:ByteArray) {
|
||||
this.type = type;
|
||||
this.len = length;
|
||||
super(b);
|
||||
}
|
||||
|
||||
public function getLength():uint
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
public function getType():uint
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
override public function toString(radix:Number=0):String {
|
||||
return DER.indent+"Integer["+type+"]["+len+"]["+super.toString(16)+"]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public class OID
|
||||
{
|
||||
|
||||
public static const RSA_ENCRYPTION:String = "1.2.840.113549.1.1.1";
|
||||
public static const MD2_WITH_RSA_ENCRYPTION:String = "1.2.840.113549.1.1.2";
|
||||
public static const MD5_WITH_RSA_ENCRYPTION:String = "1.2.840.113549.1.1.4";
|
||||
public static const SHA1_WITH_RSA_ENCRYPTION:String = "1.2.840.113549.1.1.5";
|
||||
public static const DSA:String = "1.2.840.10040.4.1";
|
||||
public static const DSA_WITH_SHA1:String = "1.2.840.10040.4.3";
|
||||
public static const DH_PUBLIC_NUMBER:String = "1.2.840.10046.2.1";
|
||||
|
||||
public static const COMMON_NAME:String = "2.5.4.3";
|
||||
public static const SURNAME:String = "2.5.4.4";
|
||||
public static const COUNTRY_NAME:String = "2.5.4.6";
|
||||
public static const LOCALITY_NAME:String = "2.5.4.7";
|
||||
public static const STATE_NAME:String = "2.5.4.8";
|
||||
public static const ORGANIZATION_NAME:String = "2.5.4.10";
|
||||
public static const ORG_UNIT_NAME:String = "2.5.4.11";
|
||||
public static const TITLE:String = "2.5.4.12";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
public class ObjectIdentifier implements IAsn1Type
|
||||
{
|
||||
private var type:uint;
|
||||
private var len:uint;
|
||||
private var oid:Array;
|
||||
|
||||
public function ObjectIdentifier(type:uint, length:uint, b:ByteArray) {
|
||||
this.type = type;
|
||||
this.len = length;
|
||||
parse(b);
|
||||
}
|
||||
|
||||
private function parse(b:ByteArray):void {
|
||||
// parse stuff
|
||||
// first byte = 40*value1 + value2
|
||||
var o:uint = b.readUnsignedByte();
|
||||
var a:Array = []
|
||||
a.push(uint(o/40));
|
||||
a.push(uint(o%40));
|
||||
var v:uint = 0;
|
||||
while (b.bytesAvailable>0) {
|
||||
o = b.readUnsignedByte();
|
||||
var last:Boolean = (o&0x80)==0;
|
||||
o &= 0x7f;
|
||||
v = v*128 + o;
|
||||
if (last) {
|
||||
a.push(v);
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
oid = a;
|
||||
}
|
||||
|
||||
public function getLength():uint
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
public function getType():uint
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return DER.indent+oid.join(".");
|
||||
}
|
||||
|
||||
public function dump():String {
|
||||
return "OID["+type+"]["+len+"]["+toString()+"]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
import com.hurlant.crypto.rsa.RSAKey;
|
||||
import com.hurlant.math.BigInteger;
|
||||
import com.hurlant.util.Base64;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
public class PEM
|
||||
{
|
||||
private static const RSA_PRIVATE_KEY_HEADER:String = "-----BEGIN RSA PRIVATE KEY-----";
|
||||
private static const RSA_PRIVATE_KEY_FOOTER:String = "-----END RSA PRIVATE KEY-----";
|
||||
private static const RSA_PUBLIC_KEY_HEADER:String = "-----BEGIN PUBLIC KEY-----";
|
||||
private static const RSA_PUBLIC_KEY_FOOTER:String = "-----END PUBLIC KEY-----";
|
||||
|
||||
|
||||
private static const OID_RSA_ENCRYPTION:String = "1.2.840.113549.1.1.1";
|
||||
|
||||
/**
|
||||
*
|
||||
* Read a structure encoded according to
|
||||
* ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
|
||||
* section 11.1.2
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public static function readRSAPrivateKey(str:String):RSAKey {
|
||||
// 1. we expect a "-----BEGIN RSA PRIVATE KEY-----"
|
||||
var i:int = str.indexOf(RSA_PRIVATE_KEY_HEADER);
|
||||
if (i==-1) return null;
|
||||
i += RSA_PRIVATE_KEY_HEADER.length;
|
||||
var j:int = str.indexOf(RSA_PRIVATE_KEY_FOOTER);
|
||||
if (j==-1) return null;
|
||||
var b64:String = str.substring(i, j);
|
||||
// remove any whitespace characters from the string
|
||||
var space:RegExp= /\s/mg;
|
||||
b64 = b64.replace(space,'');
|
||||
// decode
|
||||
var der:ByteArray = Base64.decodeToByteArray(b64);
|
||||
var obj:* = DER.parse(der);
|
||||
if (obj is Array) {
|
||||
var arr:Array = obj as Array;
|
||||
// arr[0] is Version. should be 0. should be checked. shoulda woulda coulda.
|
||||
return new RSAKey(
|
||||
arr[1], // N
|
||||
arr[2].valueOf(), // E
|
||||
arr[3], // D
|
||||
arr[4], // P
|
||||
arr[5], // Q
|
||||
arr[6], // DMP1
|
||||
arr[7], // DMQ1
|
||||
arr[8]); // IQMP
|
||||
} else {
|
||||
// dunno
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a structure encoded according to some spec somewhere
|
||||
* Also, follows some chunk from
|
||||
* ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
|
||||
* section 11.1
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public static function readRSAPublicKey(str:String):RSAKey {
|
||||
var i:int = str.indexOf(RSA_PUBLIC_KEY_HEADER);
|
||||
if (i==-1) return null;
|
||||
i += RSA_PUBLIC_KEY_HEADER.length;
|
||||
var j:int = str.indexOf(RSA_PUBLIC_KEY_FOOTER);
|
||||
if (j==-1) return null;
|
||||
var b64:String = str.substring(i, j);
|
||||
// remove whitesapces.
|
||||
b64 = b64.replace(/\s/mg, '');
|
||||
// decode
|
||||
var der:ByteArray = Base64.decodeToByteArray(b64);
|
||||
var obj:* = DER.parse(der);
|
||||
if (obj is Array) {
|
||||
var arr:Array = obj as Array;
|
||||
// arr[0] = [ <some crap that means "rsaEncryption">, null ]; ( apparently, that's an X-509 Algorithm Identifier.
|
||||
if (arr[0][0].toString()!=OID_RSA_ENCRYPTION) {
|
||||
return null;
|
||||
}
|
||||
// arr[1] is a ByteArray begging to be parsed as DER
|
||||
arr[1].position = 1; // there's a 0x00 byte up front. find out why later. like, read a spec.
|
||||
obj = DER.parse(arr[1]);
|
||||
if (obj is Array) {
|
||||
arr = obj as Array;
|
||||
// arr[0] = modulus
|
||||
// arr[1] = public expt.
|
||||
return new RSAKey(arr[0], arr[1]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// dunno
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public class PrintableString implements IAsn1Type
|
||||
{
|
||||
protected var type:uint;
|
||||
protected var len:uint;
|
||||
protected var str:String;
|
||||
|
||||
public function PrintableString(type:uint, length:uint) {
|
||||
this.type = type;
|
||||
this.len = length;
|
||||
}
|
||||
|
||||
public function getLength():uint
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
public function getType():uint
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public function setString(s:String):void {
|
||||
str = s;
|
||||
}
|
||||
public function getString():String {
|
||||
return str;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return DER.indent+str;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public dynamic class Sequence extends Array implements IAsn1Type
|
||||
{
|
||||
protected var type:uint;
|
||||
protected var len:uint;
|
||||
|
||||
public function Sequence(type:uint, length:uint) {
|
||||
this.type = type;
|
||||
this.len = length;
|
||||
}
|
||||
|
||||
public function getLength():uint
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
public function getType():uint
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
var s:String = DER.indent;
|
||||
DER.indent += " ";
|
||||
var t:String = join("\n");
|
||||
DER.indent= s;
|
||||
return DER.indent+"Sequence["+type+"]["+len+"][\n"+t+"\n"+s+"]";
|
||||
}
|
||||
|
||||
/////////
|
||||
|
||||
public function findAttributeValue(oid:String):IAsn1Type {
|
||||
for each (var set:* in this) {
|
||||
if (set is Set) {
|
||||
var child:* = set[0];
|
||||
if (child is Sequence) {
|
||||
var tmp:* = child[0];
|
||||
if (tmp is ObjectIdentifier) {
|
||||
var id:ObjectIdentifier = tmp as ObjectIdentifier;
|
||||
if (id.toString()==oid) {
|
||||
return child[1] as IAsn1Type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public dynamic class Set extends Sequence implements IAsn1Type
|
||||
{
|
||||
public function Set(type:uint, length:uint) {
|
||||
super(type, length);
|
||||
}
|
||||
|
||||
|
||||
public override function toString():String {
|
||||
var s:String = DER.indent;
|
||||
DER.indent += " ";
|
||||
var t:String = join("\n");
|
||||
DER.indent= s;
|
||||
return DER.indent+"Set["+type+"]["+len+"][\n"+t+"\n"+s+"]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public class Type
|
||||
{
|
||||
public static const CERTIFICATE:Array = [
|
||||
{name:"tbsCertificate", value:[
|
||||
{name:"tag0", value:[
|
||||
{name:"version"}
|
||||
]},
|
||||
{name:"serialNumber"},
|
||||
{name:"signature"},
|
||||
{name:"issuer", value:[
|
||||
{name:"type"},
|
||||
{name:"value"}
|
||||
]},
|
||||
{name:"validity", value:[
|
||||
{name:"notBefore"},
|
||||
{name:"notAfter"}
|
||||
]},
|
||||
{name:"subject"},
|
||||
{name:"subjectPublicKeyInfo", value:[
|
||||
{name:"algorithm"},
|
||||
{name:"subjectPublicKey"}
|
||||
]},
|
||||
{name:"issuerUniqueID"},
|
||||
{name:"subjectUniqueID"},
|
||||
{name:"extensions"}
|
||||
]},
|
||||
{name:"signatureAlgorithm"},
|
||||
{name:"signatureValue"}
|
||||
];
|
||||
public static const RSA_PUBLIC_KEY:Array = [
|
||||
{name:"modulus"},
|
||||
{name:"publicExponent"}
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.hurlant.util.der
|
||||
{
|
||||
public class UTCTime implements IAsn1Type
|
||||
{
|
||||
protected var type:uint;
|
||||
protected var len:uint;
|
||||
protected var date:Date;
|
||||
|
||||
public function UTCTime(type:uint, len:uint)
|
||||
{
|
||||
this.type = type;
|
||||
this.len = len;
|
||||
}
|
||||
|
||||
public function getLength():uint
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
public function getType():uint
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public function setUTCTime(str:String):void {
|
||||
var year:uint = 2000 + parseInt(str.substr(0, 2)); // Y2.1K unsafe!@!!
|
||||
var month:uint = parseInt(str.substr(2,2));
|
||||
var day:uint = parseInt(str.substr(4,2));
|
||||
var hour:uint = parseInt(str.substr(6,2));
|
||||
var minute:uint = parseInt(str.substr(8,2));
|
||||
// XXX this could be off by a day. parse the rest. someday.
|
||||
date = new Date(year, month, day, hour, minute);
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return DER.indent+"UTCTime["+type+"]["+len+"]["+date+"]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user