PageRenderTime 30ms CodeModel.GetById 11ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/flash-src/third-party/com/hurlant/crypto/cert/X509Certificate.as

http://github.com/gimite/web-socket-js
ActionScript | 218 lines | 172 code | 7 blank | 39 comment | 22 complexity | 722124bddb843494626313522d322765 MD5 | raw file
  1/**
  2 * X509Certificate
  3 * 
  4 * A representation for a X509 Certificate, with
  5 * methods to parse, verify and sign it.
  6 * Copyright (c) 2007 Henri Torgemane
  7 * 
  8 * See LICENSE.txt for full license information.
  9 */
 10package com.hurlant.crypto.cert {
 11	import com.hurlant.crypto.hash.IHash;
 12	import com.hurlant.crypto.hash.MD2;
 13	import com.hurlant.crypto.hash.MD5;
 14	import com.hurlant.crypto.hash.SHA1;
 15	import com.hurlant.crypto.rsa.RSAKey;
 16	import com.hurlant.util.ArrayUtil;
 17	import com.hurlant.util.Base64;
 18	import com.hurlant.util.der.ByteString;
 19	import com.hurlant.util.der.DER;
 20	import com.hurlant.util.der.OID;
 21	import com.hurlant.util.der.ObjectIdentifier;
 22	import com.hurlant.util.der.PEM;
 23	import com.hurlant.util.der.PrintableString;
 24	import com.hurlant.util.der.Sequence;
 25	import com.hurlant.util.der.Type;
 26	
 27	import flash.utils.ByteArray;
 28	
 29	public class X509Certificate {
 30		private var _loaded:Boolean;
 31		private var _param:*;
 32		private var _obj:Object;
 33		public function X509Certificate(p:*) {
 34			_loaded = false;
 35			_param = p;
 36			// lazy initialization, to avoid unnecessary parsing of every builtin CA at start-up.
 37		}
 38		private function load():void {
 39			if (_loaded) return;
 40			var p:* = _param;
 41			var b:ByteArray;
 42			if (p is String) {
 43				b = PEM.readCertIntoArray(p as String);
 44			} else if (p is ByteArray) {
 45				b = p;
 46			}
 47			if (b!=null) {
 48				_obj = DER.parse(b, Type.TLS_CERT);
 49				_loaded = true;
 50			} else {
 51				throw new Error("Invalid x509 Certificate parameter: "+p);
 52			}
 53		}
 54		public function isSigned(store:X509CertificateCollection, CAs:X509CertificateCollection, time:Date=null):Boolean {
 55			load();
 56			// check timestamps first. cheapest.
 57			if (time==null) {
 58				time = new Date;
 59			}
 60			var notBefore:Date = getNotBefore();
 61			var notAfter:Date = getNotAfter();
 62			if (time.getTime()<notBefore.getTime()) return false; // cert isn't born yet.
 63			if (time.getTime()>notAfter.getTime()) return false;  // cert died of old age.
 64			// check signature.
 65			var subject:String = getIssuerPrincipal();
 66			// try from CA first, since they're treated better.
 67			var parent:X509Certificate = CAs.getCertificate(subject);
 68			var parentIsAuthoritative:Boolean = false;
 69			if (parent == null) {
 70				parent = store.getCertificate(subject);
 71				if (parent == null) {
 72					return false; // issuer not found
 73				}
 74			} else {
 75				parentIsAuthoritative = true;
 76			}
 77			if (parent == this) { // pathological case. avoid infinite loop
 78				return false; // isSigned() returns false if we're self-signed.
 79			}
 80			if (!(parentIsAuthoritative&&parent.isSelfSigned(time)) &&
 81				!parent.isSigned(store, CAs, time)) {
 82				return false;
 83			}
 84			var key:RSAKey = parent.getPublicKey();
 85			return verifyCertificate(key);
 86		}
 87		public function isSelfSigned(time:Date):Boolean {
 88			load();
 89			
 90			var key:RSAKey = getPublicKey();
 91			return verifyCertificate(key);
 92		}
 93		private function verifyCertificate(key:RSAKey):Boolean {
 94			var algo:String = getAlgorithmIdentifier();
 95			var hash:IHash;
 96			var oid:String;
 97			switch (algo) {
 98				case OID.SHA1_WITH_RSA_ENCRYPTION:
 99					hash = new SHA1;
100					oid = OID.SHA1_ALGORITHM;
101					break;
102				case OID.MD2_WITH_RSA_ENCRYPTION:
103					hash = new MD2;
104					oid = OID.MD2_ALGORITHM;
105					break;
106				case OID.MD5_WITH_RSA_ENCRYPTION:
107					hash = new MD5;
108					oid = OID.MD5_ALGORITHM;
109					break;
110				default:
111					return false;
112			}
113			var data:ByteArray = _obj.signedCertificate_bin;
114			var buf:ByteArray = new ByteArray;
115			key.verify(_obj.encrypted, buf, _obj.encrypted.length);
116			buf.position=0;
117			data = hash.hash(data);
118			var obj:Object = DER.parse(buf, Type.RSA_SIGNATURE);
119			if (obj.algorithm.algorithmId.toString() != oid) {
120				return false; // wrong algorithm
121			}
122			if (!ArrayUtil.equals(obj.hash, data)) {
123				return false; // hashes don't match
124			}
125			return true;
126		}
127		
128		/**
129		 * This isn't used anywhere so far.
130		 * It would become useful if we started to offer facilities
131		 * to generate and sign X509 certificates.
132		 * 
133		 * @param key
134		 * @param algo
135		 * @return 
136		 * 
137		 */
138		private function signCertificate(key:RSAKey, algo:String):ByteArray {
139			var hash:IHash;
140			var oid:String;
141			switch (algo) {
142				case OID.SHA1_WITH_RSA_ENCRYPTION:
143					hash = new SHA1;
144					oid = OID.SHA1_ALGORITHM;
145					break;
146				case OID.MD2_WITH_RSA_ENCRYPTION:
147					hash = new MD2;
148					oid = OID.MD2_ALGORITHM;
149					break;
150				case OID.MD5_WITH_RSA_ENCRYPTION:
151					hash = new MD5;
152					oid = OID.MD5_ALGORITHM;
153					break;
154				default:
155					return null
156			}
157			var data:ByteArray = _obj.signedCertificate_bin;
158			data = hash.hash(data);
159			var seq1:Sequence = new Sequence;
160			seq1[0] = new Sequence;
161			seq1[0][0] = new ObjectIdentifier(0,0, oid);
162			seq1[0][1] = null;
163			seq1[1] = new ByteString;
164			seq1[1].writeBytes(data);
165			data = seq1.toDER();
166			var buf:ByteArray = new ByteArray;
167			key.sign(data, buf, data.length);
168			return buf;
169		}
170		
171		public function getPublicKey():RSAKey {
172			load();
173			var pk:ByteArray = _obj.signedCertificate.subjectPublicKeyInfo.subjectPublicKey as ByteArray;
174			pk.position = 0;
175			var rsaKey:Object = DER.parse(pk, [{name:"N"},{name:"E"}]);
176			return new RSAKey(rsaKey.N, rsaKey.E.valueOf());
177		}
178		
179		/**
180		 * Returns a subject principal, as an opaque base64 string.
181		 * This is only used as a hash key for known certificates.
182		 * 
183		 * Note that this assumes X509 DER-encoded certificates are uniquely encoded,
184		 * as we look for exact matches between Issuer and Subject fields.
185		 * 
186		 */
187		public function getSubjectPrincipal():String {
188			load();
189			return Base64.encodeByteArray(_obj.signedCertificate.subject_bin);
190		}
191		/**
192		 * Returns an issuer principal, as an opaque base64 string.
193		 * This is only used to quickly find matching parent certificates.
194		 * 
195		 * Note that this assumes X509 DER-encoded certificates are uniquely encoded,
196		 * as we look for exact matches between Issuer and Subject fields.
197		 * 
198		 */
199		public function getIssuerPrincipal():String {
200			load();
201			return Base64.encodeByteArray(_obj.signedCertificate.issuer_bin);
202		}
203		public function getAlgorithmIdentifier():String {
204			return _obj.algorithmIdentifier.algorithmId.toString();
205		}
206		public function getNotBefore():Date {
207			return _obj.signedCertificate.validity.notBefore.date;
208		}
209		public function getNotAfter():Date {
210			return _obj.signedCertificate.validity.notAfter.date;
211		}
212		
213		public function getCommonName():String {
214			var subject:Sequence = _obj.signedCertificate.subject;
215			return (subject.findAttributeValue(OID.COMMON_NAME) as PrintableString).getString();
216		}
217	}
218}