/**
 * PEM
 * 
 * A class to parse some PEM stuff.
 * Copyright (c) 2007 Henri Torgemane
 * 
 * See LICENSE.txt for full license information.
 */
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;
	import com.hurlant.util.Hex;
	
	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 CERTIFICATE_HEADER:String = "-----BEGIN CERTIFICATE-----";
		private static const CERTIFICATE_FOOTER:String = "-----END CERTIFICATE-----";
		
		
		
		/**
		 * 
		 * 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 {
			var der:ByteArray = extractBinary(RSA_PRIVATE_KEY_HEADER, RSA_PRIVATE_KEY_FOOTER, str);
			if (der==null) return null;
			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 der:ByteArray = extractBinary(RSA_PUBLIC_KEY_HEADER, RSA_PUBLIC_KEY_FOOTER, str);
			if (der==null) return null;
			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;
			}
		}

		public static function readCertIntoArray(str:String):ByteArray {
			var tmp:ByteArray = extractBinary(CERTIFICATE_HEADER, CERTIFICATE_FOOTER, str);
			return tmp;
		}
		
		private static function extractBinary(header:String, footer:String, str:String):ByteArray {
			var i:int = str.indexOf(header);
			if (i==-1) return null;
			i += header.length;
			var j:int = str.indexOf(footer);
			if (j==-1) return null;
			var b64:String = str.substring(i, j);
			// remove whitesapces.
			b64 = b64.replace(/\s/mg, '');
			// decode
			return Base64.decodeToByteArray(b64);
		}
		
	}
}