no commit message
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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), 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), parseInt(E,16), new BigInteger(D,16));
|
||||
} else {
|
||||
return new RSAKey(new BigInteger(N,16), parseInt(E,16), new BigInteger(D,16),
|
||||
new BigInteger(P,16), new BigInteger(Q,16),
|
||||
new BigInteger(DMP1,16), new BigInteger(DMQ1),
|
||||
new BigInteger(IQMP));
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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), bl);
|
||||
var chunk:BigInteger = doPublic(block);
|
||||
chunk.toArray(dst);
|
||||
}
|
||||
}
|
||||
public function decrypt(src:ByteArray, dst:ByteArray, length:uint, pad:Function=null):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, length);
|
||||
var chunk:BigInteger = doPrivate2(block);
|
||||
var b:ByteArray = pad(chunk, bl);
|
||||
dst.writeBytes(b);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* PKCS#1 pad. type 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):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;
|
||||
var rng:Random = new Random;
|
||||
while (n>2) {
|
||||
var x:int = 0;
|
||||
while (x==0) x = rng.nextByte();
|
||||
out[--n] = x;
|
||||
}
|
||||
out[--n] = 2;
|
||||
out[--n] = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
private function pkcs1unpad(src:BigInteger, n:uint):ByteArray {
|
||||
var b:ByteArray = src.toByteArray();
|
||||
var out:ByteArray = new ByteArray;
|
||||
var i:int = 0;
|
||||
while (i<b.length && b[i]==0) ++i;
|
||||
if (b.length-i != n-1 || b[i]!=2) {
|
||||
trace("PKCS#1 unpad: i="+i+", expected b[i]==2, got b[i]="+b[i]);
|
||||
return null;
|
||||
}
|
||||
++i;
|
||||
while (b[i]!=0) {
|
||||
if (++i>=b.length) {
|
||||
trace("PKCS#1 unpad: i="+i+", b[i-1]!=0 (="+b[i-1]+")");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
while (++i < b.length) {
|
||||
out.writeByte(b[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Raw pad.
|
||||
*/
|
||||
private function rawpad(src:ByteArray, end:int, n:uint):ByteArray {
|
||||
return src;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user