// Depends on rsa.js and jsbn2.js | |
// Version 1.1: support utf-8 decoding in pkcs1unpad2 | |
// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext | |
function pkcs1unpad2(d,n) { | |
var b = d.toByteArray(); | |
var i = 0; | |
while(i < b.length && b[i] == 0) ++i; | |
if(b.length-i != n-1 || b[i] != 2) | |
return null; | |
++i; | |
while(b[i] != 0) | |
if(++i >= b.length) return null; | |
var ret = ""; | |
while(++i < b.length) { | |
var c = b[i] & 255; | |
if(c < 128) { // utf-8 decode | |
ret += String.fromCharCode(c); | |
} | |
else if((c > 191) && (c < 224)) { | |
ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63)); | |
++i; | |
} | |
else { | |
ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63)); | |
i += 2; | |
} | |
} | |
return ret; | |
} | |
// Set the private key fields N, e, and d from hex strings | |
function RSASetPrivate(N,E,D) { | |
if(N != null && E != null && N.length > 0 && E.length > 0) { | |
this.n = parseBigInt(N,16); | |
this.e = parseInt(E,16); | |
this.d = parseBigInt(D,16); | |
} | |
else | |
alert("Invalid RSA private key"); | |
} | |
// Set the private key fields N, e, d and CRT params from hex strings | |
function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { | |
if(N != null && E != null && N.length > 0 && E.length > 0) { | |
this.n = parseBigInt(N,16); | |
this.e = parseInt(E,16); | |
this.d = parseBigInt(D,16); | |
this.p = parseBigInt(P,16); | |
this.q = parseBigInt(Q,16); | |
this.dmp1 = parseBigInt(DP,16); | |
this.dmq1 = parseBigInt(DQ,16); | |
this.coeff = parseBigInt(C,16); | |
} | |
else | |
alert("Invalid RSA private key"); | |
} | |
// Generate a new random private key B bits long, using public expt E | |
function RSAGenerate(B,E) { | |
var rng = new SecureRandom(); | |
var qs = B>>1; | |
this.e = parseInt(E,16); | |
var ee = new BigInteger(E,16); | |
for(;;) { | |
for(;;) { | |
this.p = new BigInteger(B-qs,1,rng); | |
if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; | |
} | |
for(;;) { | |
this.q = new BigInteger(qs,1,rng); | |
if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; | |
} | |
if(this.p.compareTo(this.q) <= 0) { | |
var t = this.p; | |
this.p = this.q; | |
this.q = t; | |
} | |
var p1 = this.p.subtract(BigInteger.ONE); // p1 = p - 1 | |
var q1 = this.q.subtract(BigInteger.ONE); // q1 = q - 1 | |
var phi = p1.multiply(q1); | |
if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { | |
this.n = this.p.multiply(this.q); // this.n = p * q | |
this.d = ee.modInverse(phi); // this.d = | |
this.dmp1 = this.d.mod(p1); // this.dmp1 = d mod (p - 1) | |
this.dmq1 = this.d.mod(q1); // this.dmq1 = d mod (q - 1) | |
this.coeff = this.q.modInverse(this.p); // this.coeff = (q ^ -1) mod p | |
break; | |
} | |
} | |
} | |
// Perform raw private operation on "x": return x^d (mod n) | |
function RSADoPrivate(x) { | |
if(this.p == null || this.q == null) | |
return x.modPow(this.d, this.n); | |
// TODO: re-calculate any missing CRT params | |
var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp? | |
var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq? | |
while(xp.compareTo(xq) < 0) | |
xp = xp.add(this.p); | |
// NOTE: | |
// xp.subtract(xq) => cp -cq | |
// xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h | |
// xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M | |
return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); | |
} | |
// Return the PKCS#1 RSA decryption of "ctext". | |
// "ctext" is an even-length hex string and the output is a plain string. | |
function RSADecrypt(ctext) { | |
var c = parseBigInt(ctext, 16); | |
var m = this.doPrivate(c); | |
if(m == null) return null; | |
return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); | |
} | |
// Return the PKCS#1 RSA decryption of "ctext". | |
// "ctext" is a Base64-encoded string and the output is a plain string. | |
//function RSAB64Decrypt(ctext) { | |
// var h = b64tohex(ctext); | |
// if(h) return this.decrypt(h); else return null; | |
//} | |
// protected | |
RSAKey.prototype.doPrivate = RSADoPrivate; | |
// public | |
RSAKey.prototype.setPrivate = RSASetPrivate; | |
RSAKey.prototype.setPrivateEx = RSASetPrivateEx; | |
RSAKey.prototype.generate = RSAGenerate; | |
RSAKey.prototype.decrypt = RSADecrypt; | |
//RSAKey.prototype.b64_decrypt = RSAB64Decrypt; |