/js/lib/Socket.IO-node/support/socket.io-client/lib/vendor/web-socket-js/flash-src/com/hurlant/crypto/rsa/RSAKey.as
ActionScript | 339 lines | 256 code | 31 blank | 52 comment | 42 complexity | d7a68d698220691be6776b1ca97e1049 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
- /**
- * RSAKey
- *
- * An ActionScript 3 implementation of RSA + PKCS#1 (light version)
- * Copyright (c) 2007 Henri Torgemane
- *
- * Derived from:
- * The jsbn library, Copyright (c) 2003-2005 Tom Wu
- *
- * See LICENSE.txt for full license information.
- */
- package com.hurlant.crypto.rsa
- {
- import com.hurlant.crypto.prng.Random;
- import com.hurlant.math.BigInteger;
- import com.hurlant.util.Memory;
-
- import flash.utils.ByteArray;
- import com.hurlant.crypto.hash.IHash;
- import com.hurlant.util.Hex;
- import com.hurlant.util.der.DER;
- import com.hurlant.util.der.OID;
- import com.hurlant.util.ArrayUtil;
- import com.hurlant.util.der.Type;
- import com.hurlant.util.der.Sequence;
- import com.hurlant.util.der.ObjectIdentifier;
- import com.hurlant.util.der.ByteString;
- import com.hurlant.crypto.tls.TLSError;
-
- /**
- * Current limitations:
- * exponent must be smaller than 2^31.
- */
- public class RSAKey
- {
- // public key
- public var e:int; // public exponent. must be <2^31
- public var n:BigInteger; // modulus
- // private key
- public var d:BigInteger;
- // extended private key
- public var p:BigInteger;
- public var q:BigInteger;
- public var dmp1:BigInteger
- public var dmq1:BigInteger;
- public var coeff:BigInteger;
- // flags. flags are cool.
- protected var canDecrypt:Boolean;
- protected var canEncrypt:Boolean;
-
- public function RSAKey(N:BigInteger, E:int,
- D:BigInteger=null,
- P:BigInteger = null, Q:BigInteger=null,
- DP:BigInteger=null, DQ:BigInteger=null,
- C:BigInteger=null) {
-
- this.n = N;
- this.e = E;
- this.d = D;
- this.p = P;
- this.q = Q;
- this.dmp1 = DP;
- this.dmq1 = DQ;
- this.coeff = C;
-
- // adjust a few flags.
- canEncrypt = (n!=null&&e!=0);
- canDecrypt = (canEncrypt&&d!=null);
-
-
- }
-
- public static function parsePublicKey(N:String, E:String):RSAKey {
- return new RSAKey(new BigInteger(N, 16, true), parseInt(E,16));
- }
- public static function parsePrivateKey(N:String, E:String, D:String,
- P:String=null,Q:String=null, DMP1:String=null, DMQ1:String=null, IQMP:String=null):RSAKey {
- if (P==null) {
- return new RSAKey(new BigInteger(N,16, true), parseInt(E,16), new BigInteger(D,16, true));
- } else {
- return new RSAKey(new BigInteger(N,16, true), parseInt(E,16), new BigInteger(D,16, true),
- new BigInteger(P,16, true), new BigInteger(Q,16, true),
- new BigInteger(DMP1,16, true), new BigInteger(DMQ1, 16, true),
- new BigInteger(IQMP, 16, true));
- }
- }
-
- public function getBlockSize():uint {
- return (n.bitLength()+7)/8;
- }
- public function dispose():void {
- e = 0;
- n.dispose();
- n = null;
- Memory.gc();
- }
-
- public function encrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):void {
- _encrypt(doPublic, src, dst, length, pad, 0x02);
- }
- public function decrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):void {
- _decrypt(doPrivate2, src, dst, length, pad, 0x02);
- }
-
- public function sign(src:ByteArray, dst:ByteArray, length:uint, pad:Function = null):void {
- _encrypt(doPrivate2, src, dst, length, pad, 0x01);
- }
- public function verify(src:ByteArray, dst:ByteArray, length:uint, pad:Function = null):void {
- _decrypt(doPublic, src, dst, length, pad, 0x01);
- }
-
-
- private function _encrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
- // adjust pad if needed
- if (pad==null) pad = pkcs1pad;
- // convert src to BigInteger
- if (src.position >= src.length) {
- src.position = 0;
- }
- var bl:uint = getBlockSize();
- var end:int = src.position + length;
- while (src.position<end) {
- var block:BigInteger = new BigInteger(pad(src, end, bl, padType), bl, true);
- var chunk:BigInteger = op(block);
- chunk.toArray(dst);
- }
- }
- private function _decrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
- // adjust pad if needed
- if (pad==null) pad = pkcs1unpad;
-
- // convert src to BigInteger
- if (src.position >= src.length) {
- src.position = 0;
- }
- var bl:uint = getBlockSize();
- var end:int = src.position + length;
- while (src.position<end) {
- var block:BigInteger = new BigInteger(src, bl, true);
- var chunk:BigInteger = op(block);
- var b:ByteArray = pad(chunk, bl, padType);
- if (b == null)
- throw new TLSError( "Decrypt error - padding function returned null!", TLSError.decode_error );
- // if (b != null)
- dst.writeBytes(b);
- }
- }
-
- /**
- * PKCS#1 pad. type 1 (0xff) or 2, random.
- * puts as much data from src into it, leaves what doesn't fit alone.
- */
- private function pkcs1pad(src:ByteArray, end:int, n:uint, type:uint = 0x02):ByteArray {
- var out:ByteArray = new ByteArray;
- var p:uint = src.position;
- end = Math.min(end, src.length, p+n-11);
- src.position = end;
- var i:int = end-1;
- while (i>=p && n>11) {
- out[--n] = src[i--];
- }
- out[--n] = 0;
- if (type==0x02) { // type 2
- var rng:Random = new Random;
- var x:int = 0;
- while (n>2) {
- do {
- x = rng.nextByte();
- } while (x==0);
- out[--n] = x;
- }
- } else { // type 1
- while (n>2) {
- out[--n] = 0xFF;
- }
- }
- out[--n] = type;
- out[--n] = 0;
- return out;
- }
-
- /**
- *
- * @param src
- * @param n
- * @param type Not used.
- * @return
- *
- */
- private function pkcs1unpad(src:BigInteger, n:uint, type:uint = 0x02):ByteArray {
- var b:ByteArray = src.toByteArray();
- var out:ByteArray = new ByteArray;
-
- b.position = 0;
- var i:int = 0;
- while (i<b.length && b[i]==0) ++i;
- if (b.length-i != n-1 || b[i]!=type) {
- trace("PKCS#1 unpad: i="+i+", expected b[i]=="+type+", got b[i]="+b[i].toString(16));
- return null;
- }
- ++i;
- while (b[i]!=0) {
- if (++i>=b.length) {
- trace("PKCS#1 unpad: i="+i+", b[i-1]!=0 (="+b[i-1].toString(16)+")");
- return null;
- }
- }
- while (++i < b.length) {
- out.writeByte(b[i]);
- }
- out.position = 0;
- return out;
- }
- /**
- * Raw pad.
- */
- public function rawpad(src:ByteArray, end:int, n:uint, type:uint = 0):ByteArray {
- return src;
- }
- public function rawunpad(src:BigInteger, n:uint, type:uint = 0):ByteArray {
- return src.toByteArray();
- }
-
- public function toString():String {
- return "rsa";
- }
-
- public function dump():String {
- var s:String= "N="+n.toString(16)+"\n"+
- "E="+e.toString(16)+"\n";
- if (canDecrypt) {
- s+="D="+d.toString(16)+"\n";
- if (p!=null && q!=null) {
- s+="P="+p.toString(16)+"\n";
- s+="Q="+q.toString(16)+"\n";
- s+="DMP1="+dmp1.toString(16)+"\n";
- s+="DMQ1="+dmq1.toString(16)+"\n";
- s+="IQMP="+coeff.toString(16)+"\n";
- }
- }
- return s;
- }
-
-
- /**
- *
- * note: We should have a "nice" variant of this function that takes a callback,
- * and perform the computation is small fragments, to keep the web browser
- * usable.
- *
- * @param B
- * @param E
- * @return a new random private key B bits long, using public expt E
- *
- */
- public static function generate(B:uint, E:String):RSAKey {
- var rng:Random = new Random;
- var qs:uint = B>>1;
- var key:RSAKey = new RSAKey(null,0,null);
- key.e = parseInt(E, 16);
- var ee:BigInteger = new BigInteger(E,16, true);
- for (;;) {
- for (;;) {
- key.p = bigRandom(B-qs, rng);
- if (key.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE)==0 &&
- key.p.isProbablePrime(10)) break;
- }
- for (;;) {
- key.q = bigRandom(qs, rng);
- if (key.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE)==0 &&
- key.q.isProbablePrime(10)) break;
- }
- if (key.p.compareTo(key.q)<=0) {
- var t:BigInteger = key.p;
- key.p = key.q;
- key.q = t;
- }
- var p1:BigInteger = key.p.subtract(BigInteger.ONE);
- var q1:BigInteger = key.q.subtract(BigInteger.ONE);
- var phi:BigInteger = p1.multiply(q1);
- if (phi.gcd(ee).compareTo(BigInteger.ONE)==0) {
- key.n = key.p.multiply(key.q);
- key.d = ee.modInverse(phi);
- key.dmp1 = key.d.mod(p1);
- key.dmq1 = key.d.mod(q1);
- key.coeff = key.q.modInverse(key.p);
- break;
- }
- }
- return key;
- }
-
- protected static function bigRandom(bits:int, rnd:Random):BigInteger {
- if (bits<2) return BigInteger.nbv(1);
- var x:ByteArray = new ByteArray;
- rnd.nextBytes(x, (bits>>3));
- x.position = 0;
- var b:BigInteger = new BigInteger(x,0,true);
- b.primify(bits, 1);
- return b;
- }
-
- protected function doPublic(x:BigInteger):BigInteger {
- return x.modPowInt(e, n);
- }
-
- protected function doPrivate2(x:BigInteger):BigInteger {
- if (p==null && q==null) {
- return x.modPow(d,n);
- }
-
- var xp:BigInteger = x.mod(p).modPow(dmp1, p);
- var xq:BigInteger = x.mod(q).modPow(dmq1, q);
-
- while (xp.compareTo(xq)<0) {
- xp = xp.add(p);
- }
- var r:BigInteger = xp.subtract(xq).multiply(coeff).mod(p).multiply(q).add(xq);
-
- return r;
- }
-
- protected function doPrivate(x:BigInteger):BigInteger {
- if (p==null || q==null) {
- return x.modPow(d, n);
- }
- // TODO: re-calculate any missing CRT params
- var xp:BigInteger = x.mod(p).modPow(dmp1, p);
- var xq:BigInteger = x.mod(q).modPow(dmq1, q);
-
- while (xp.compareTo(xq)<0) {
- xp = xp.add(p);
- }
- return xp.subtract(xq).multiply(coeff).mod(p).multiply(q).add(xq);
- }
-
-
- }
- }