PageRenderTime 36ms CodeModel.GetById 15ms app.highlight 16ms RepoModel.GetById 2ms app.codeStats 0ms

/flash-src/third-party/com/hurlant/crypto/rsa/RSAKey.as

http://github.com/gimite/web-socket-js
ActionScript | 339 lines | 256 code | 31 blank | 52 comment | 42 complexity | d7a68d698220691be6776b1ca97e1049 MD5 | raw file
  1/**
  2 * RSAKey
  3 * 
  4 * An ActionScript 3 implementation of RSA + PKCS#1 (light version)
  5 * Copyright (c) 2007 Henri Torgemane
  6 * 
  7 * Derived from:
  8 * 		The jsbn library, Copyright (c) 2003-2005 Tom Wu
  9 * 
 10 * See LICENSE.txt for full license information.
 11 */
 12package com.hurlant.crypto.rsa
 13{
 14	import com.hurlant.crypto.prng.Random;
 15	import com.hurlant.math.BigInteger;
 16	import com.hurlant.util.Memory;
 17	
 18	import flash.utils.ByteArray;
 19	import com.hurlant.crypto.hash.IHash;
 20	import com.hurlant.util.Hex;
 21	import com.hurlant.util.der.DER;
 22	import com.hurlant.util.der.OID;
 23	import com.hurlant.util.ArrayUtil;
 24	import com.hurlant.util.der.Type;
 25	import com.hurlant.util.der.Sequence;
 26	import com.hurlant.util.der.ObjectIdentifier;
 27	import com.hurlant.util.der.ByteString;
 28	import com.hurlant.crypto.tls.TLSError;
 29	
 30	/**
 31	 * Current limitations:
 32	 * exponent must be smaller than 2^31.
 33	 */
 34	public class RSAKey
 35	{
 36		// public key
 37		public var e:int;              // public exponent. must be <2^31
 38		public var n:BigInteger; // modulus
 39		// private key
 40		public var d:BigInteger;
 41		// extended private key
 42		public var p:BigInteger;
 43		public var q:BigInteger;
 44		public var dmp1:BigInteger
 45		public var dmq1:BigInteger;
 46		public var coeff:BigInteger;
 47		// flags. flags are cool.
 48		protected var canDecrypt:Boolean;
 49		protected var canEncrypt:Boolean;
 50		
 51		public function RSAKey(N:BigInteger, E:int, 
 52			D:BigInteger=null,
 53			P:BigInteger = null, Q:BigInteger=null,
 54			DP:BigInteger=null, DQ:BigInteger=null,
 55			C:BigInteger=null) {
 56				
 57			this.n = N;
 58			this.e = E;
 59			this.d = D;
 60			this.p = P;
 61			this.q = Q;
 62			this.dmp1 = DP;
 63			this.dmq1 = DQ;
 64			this.coeff = C;
 65			
 66			// adjust a few flags.
 67			canEncrypt = (n!=null&&e!=0);
 68			canDecrypt = (canEncrypt&&d!=null);
 69			
 70			
 71		}
 72

 73		public static function parsePublicKey(N:String, E:String):RSAKey {
 74			return new RSAKey(new BigInteger(N, 16, true), parseInt(E,16));
 75		}
 76		public static function parsePrivateKey(N:String, E:String, D:String, 
 77			P:String=null,Q:String=null, DMP1:String=null, DMQ1:String=null, IQMP:String=null):RSAKey {
 78			if (P==null) {
 79				return new RSAKey(new BigInteger(N,16, true), parseInt(E,16), new BigInteger(D,16, true));
 80			} else {
 81				return new RSAKey(new BigInteger(N,16, true), parseInt(E,16), new BigInteger(D,16, true),
 82					new BigInteger(P,16, true), new BigInteger(Q,16, true),
 83					new BigInteger(DMP1,16, true), new BigInteger(DMQ1, 16, true),
 84					new BigInteger(IQMP, 16, true));
 85			}
 86		}
 87		
 88		public function getBlockSize():uint {
 89			return (n.bitLength()+7)/8;
 90		}
 91		public function dispose():void {
 92			e = 0;
 93			n.dispose();
 94			n = null;
 95			Memory.gc();
 96		}
 97

 98		public function encrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):void {
 99			_encrypt(doPublic, src, dst, length, pad, 0x02);
100		}
101		public function decrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):void {
102			_decrypt(doPrivate2, src, dst, length, pad, 0x02);
103		}
104

105		public function sign(src:ByteArray, dst:ByteArray, length:uint, pad:Function = null):void {
106			_encrypt(doPrivate2, src, dst, length, pad, 0x01);
107		}
108		public function verify(src:ByteArray, dst:ByteArray, length:uint, pad:Function = null):void {
109			_decrypt(doPublic, src, dst, length, pad, 0x01);
110		}
111		
112

113		private function _encrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
114			// adjust pad if needed
115			if (pad==null) pad = pkcs1pad;
116			// convert src to BigInteger
117			if (src.position >= src.length) {
118				src.position = 0;
119			}
120			var bl:uint = getBlockSize();
121			var end:int = src.position + length;
122			while (src.position<end) {
123				var block:BigInteger = new BigInteger(pad(src, end, bl, padType), bl, true);
124				var chunk:BigInteger = op(block);
125				chunk.toArray(dst);
126			}
127		}
128		private function _decrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
129			// adjust pad if needed
130			if (pad==null) pad = pkcs1unpad;
131			
132			// convert src to BigInteger
133			if (src.position >= src.length) {
134				src.position = 0;
135			}
136			var bl:uint = getBlockSize();
137			var end:int = src.position + length;
138			while (src.position<end) {
139				var block:BigInteger = new BigInteger(src, bl, true);
140				var chunk:BigInteger = op(block);
141				var b:ByteArray = pad(chunk, bl, padType);
142				if (b == null) 
143					 throw new TLSError( "Decrypt error - padding function returned null!", TLSError.decode_error );
144				// if (b != null)
145				dst.writeBytes(b);
146			}
147		}
148		
149		/**
150		 * PKCS#1 pad. type 1 (0xff) or 2, random.
151		 * puts as much data from src into it, leaves what doesn't fit alone.
152		 */
153		private function pkcs1pad(src:ByteArray, end:int, n:uint, type:uint = 0x02):ByteArray {
154			var out:ByteArray = new ByteArray;
155			var p:uint = src.position;
156			end = Math.min(end, src.length, p+n-11);
157			src.position = end;
158			var i:int = end-1;
159			while (i>=p && n>11) {
160				out[--n] = src[i--];
161			}
162			out[--n] = 0;
163			if (type==0x02) { // type 2
164				var rng:Random = new Random;
165				var x:int = 0;
166				while (n>2) {
167					do {
168						x = rng.nextByte();
169					} while (x==0);
170					out[--n] = x;
171				}
172			} else { // type 1
173				while (n>2) {
174					out[--n] = 0xFF;
175				}
176			}
177			out[--n] = type;
178			out[--n] = 0;
179			return out;
180		}
181		
182		/**
183		 * 
184		 * @param src
185		 * @param n
186		 * @param type Not used.
187		 * @return 
188		 * 
189		 */
190		private function pkcs1unpad(src:BigInteger, n:uint, type:uint = 0x02):ByteArray {
191			var b:ByteArray = src.toByteArray();
192			var out:ByteArray = new ByteArray;
193			
194			b.position = 0;
195			var i:int = 0;
196			while (i<b.length && b[i]==0) ++i;
197			if (b.length-i != n-1 || b[i]!=type) {
198				trace("PKCS#1 unpad: i="+i+", expected b[i]=="+type+", got b[i]="+b[i].toString(16));
199				return null;
200			}
201			++i;
202			while (b[i]!=0) {
203				if (++i>=b.length) {
204					trace("PKCS#1 unpad: i="+i+", b[i-1]!=0 (="+b[i-1].toString(16)+")");
205					return null;
206				}
207			}
208			while (++i < b.length) {
209				out.writeByte(b[i]);
210			}
211			out.position = 0;
212			return out;
213		}
214		/**
215		 * Raw pad.
216		 */
217		public function rawpad(src:ByteArray, end:int, n:uint, type:uint = 0):ByteArray {
218			return src;
219		}
220		public function rawunpad(src:BigInteger, n:uint, type:uint = 0):ByteArray {
221			return src.toByteArray();
222		}
223		
224		public function toString():String {
225			return "rsa";
226		}
227		
228		public function dump():String {
229			var s:String= "N="+n.toString(16)+"\n"+
230			"E="+e.toString(16)+"\n";
231			if (canDecrypt) {
232				s+="D="+d.toString(16)+"\n";
233				if (p!=null && q!=null) {
234					s+="P="+p.toString(16)+"\n";
235					s+="Q="+q.toString(16)+"\n";
236					s+="DMP1="+dmp1.toString(16)+"\n";
237					s+="DMQ1="+dmq1.toString(16)+"\n";
238					s+="IQMP="+coeff.toString(16)+"\n";
239				}
240			}
241			return s;
242		}
243		
244		
245		/**
246		 * 
247		 * note: We should have a "nice" variant of this function that takes a callback,
248		 * 		and perform the computation is small fragments, to keep the web browser
249		 * 		usable.
250		 * 
251		 * @param B
252		 * @param E
253		 * @return a new random private key B bits long, using public expt E
254		 * 
255		 */
256		public static function generate(B:uint, E:String):RSAKey {
257			var rng:Random = new Random;
258			var qs:uint = B>>1;
259			var key:RSAKey = new RSAKey(null,0,null);
260			key.e = parseInt(E, 16);
261			var ee:BigInteger = new BigInteger(E,16, true);
262			for (;;) {
263				for (;;) {
264					key.p = bigRandom(B-qs, rng);
265					if (key.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE)==0 &&
266						key.p.isProbablePrime(10)) break;
267				}
268				for (;;) {
269					key.q = bigRandom(qs, rng);
270					if (key.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE)==0 &&
271						key.q.isProbablePrime(10)) break;
272				}
273				if (key.p.compareTo(key.q)<=0) {
274					var t:BigInteger = key.p;
275					key.p = key.q;
276					key.q = t;
277				}
278				var p1:BigInteger = key.p.subtract(BigInteger.ONE);
279				var q1:BigInteger = key.q.subtract(BigInteger.ONE);
280				var phi:BigInteger = p1.multiply(q1);
281				if (phi.gcd(ee).compareTo(BigInteger.ONE)==0) {
282					key.n = key.p.multiply(key.q);
283					key.d = ee.modInverse(phi);
284					key.dmp1 = key.d.mod(p1);
285					key.dmq1 = key.d.mod(q1);
286					key.coeff = key.q.modInverse(key.p);
287					break;
288				}
289			}
290			return key;
291		}
292		
293		protected static function bigRandom(bits:int, rnd:Random):BigInteger {
294			if (bits<2) return BigInteger.nbv(1);
295			var x:ByteArray = new ByteArray;
296			rnd.nextBytes(x, (bits>>3));
297			x.position = 0;
298			var b:BigInteger = new BigInteger(x,0,true);
299			b.primify(bits, 1);
300			return b;
301		}
302		
303		protected function doPublic(x:BigInteger):BigInteger {
304			return x.modPowInt(e, n);
305		}
306		
307		protected function doPrivate2(x:BigInteger):BigInteger {
308			if (p==null && q==null) {
309				return x.modPow(d,n);
310			}
311			
312			var xp:BigInteger = x.mod(p).modPow(dmp1, p);
313			var xq:BigInteger = x.mod(q).modPow(dmq1, q);
314			
315			while (xp.compareTo(xq)<0) {
316				xp = xp.add(p);
317			}
318			var r:BigInteger = xp.subtract(xq).multiply(coeff).mod(p).multiply(q).add(xq);
319			
320			return r;
321		}
322		
323		protected function doPrivate(x:BigInteger):BigInteger {
324			if (p==null || q==null) {
325				return x.modPow(d, n);
326			}
327			// TODO: re-calculate any missing CRT params
328			var xp:BigInteger = x.mod(p).modPow(dmp1, p);
329			var xq:BigInteger = x.mod(q).modPow(dmq1, q);
330			
331			while (xp.compareTo(xq)<0) {
332				xp = xp.add(p);
333			}
334			return xp.subtract(xq).multiply(coeff).mod(p).multiply(q).add(xq);
335		}
336		
337		
338	}
339}