/** * 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= src.length) { src.position = 0; } var bl:uint = getBlockSize(); var end:int = src.position + length; while (src.position=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>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); } } }