RSAKey class
class RSAKey {
var n;
var e;
var d;
var p;
var q;
var dmp1;
var dmq1;
var coeff;
// "empty" RSA key constructor
RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
// Set the public key fields N and e from hex strings
setPublic(N,E) {
if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
//this.e = parseInt(E,16);
this.e = Fixnum.int32.parseHex(E).toInt();
//Mathx.parseInt("A");
}
else {
print("Invalid RSA public key");
}
}
// Perform raw public operation on "x": return x^e (mod n)
doPublic(x) {
return x.modPowInt(this.e, this.n);
}
// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
encrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) {
return h;
} else {
//return "0" + h;
return "0${h}";
}
}
// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
pkcs1unpad2(d,n) {
List<int> 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 = new String.fromCharCodes([b[i]]);
ret = "$ret${c}";
}
//ret += String.fromCharCode(b[i]);
return ret;
}
// Set the private key fields N, e, and d from hex strings
setPrivate(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.e = Fixnum.int32.parseHex(E).toInt();
this.d = parseBigInt(D,16);
}
else {
print("Invalid RSA private key");
}
}
// Set the private key fields N, e, d and CRT params from hex strings
setPrivateEx(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.e = Fixnum.int32.parseHex(E).toInt();
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 {
print("Invalid RSA private key");
}
}
// Generate a new random private key B bits long, using public expt E
generate(B,E) {
var rng = new SecureRandom();
var qs = B>>1;
//this.e = parseInt(E,16);
this.e = Fixnum.int32.parseHex(E).toInt();
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);
var q1 = this.q.subtract(BigInteger.ONE);
var phi = p1.multiply(q1);
if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
this.n = this.p.multiply(this.q);
this.d = ee.modInverse(phi);
this.dmp1 = this.d.mod(p1);
this.dmq1 = this.d.mod(q1);
this.coeff = this.q.modInverse(this.p);
break;
}
}
}
// Perform raw private operation on "x": return x^d (mod n)
doPrivate(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);
var xq = x.mod(this.q).modPow(this.dmq1, this.q);
while(xp.compareTo(xq) < 0) {
xp = xp.add(this.p);
}
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.
decrypt(ctext) {
var c = parseBigInt(ctext, 16);
var m = this.doPrivate(c);
if(m == null) return null;
return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
}
}
Constructors
new RSAKey() #
RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
Properties
var coeff #
coeff
var d #
d
var dmp1 #
dmp1
var dmq1 #
dmq1
var e #
e
var n #
n
var p #
p
var q #
q
Methods
decrypt(ctext) #
decrypt(ctext) {
var c = parseBigInt(ctext, 16);
var m = this.doPrivate(c);
if(m == null) return null;
return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
}
doPrivate(x) #
doPrivate(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);
var xq = x.mod(this.q).modPow(this.dmq1, this.q);
while(xp.compareTo(xq) < 0) {
xp = xp.add(this.p);
}
return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
}
doPublic(x) #
doPublic(x) {
return x.modPowInt(this.e, this.n);
}
encrypt(text) #
encrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) {
return h;
} else {
//return "0" + h;
return "0${h}";
}
}
generate(B, E) #
generate(B,E) {
var rng = new SecureRandom();
var qs = B>>1;
//this.e = parseInt(E,16);
this.e = Fixnum.int32.parseHex(E).toInt();
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);
var q1 = this.q.subtract(BigInteger.ONE);
var phi = p1.multiply(q1);
if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
this.n = this.p.multiply(this.q);
this.d = ee.modInverse(phi);
this.dmp1 = this.d.mod(p1);
this.dmq1 = this.d.mod(q1);
this.coeff = this.q.modInverse(this.p);
break;
}
}
}
pkcs1unpad2(d, n) #
pkcs1unpad2(d,n) {
List<int> 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 = new String.fromCharCodes([b[i]]);
ret = "$ret${c}";
}
//ret += String.fromCharCode(b[i]);
return ret;
}
setPrivate(N, E, D) #
setPrivate(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.e = Fixnum.int32.parseHex(E).toInt();
this.d = parseBigInt(D,16);
}
else {
print("Invalid RSA private key");
}
}
setPrivateEx(N, E, D, P, Q, DP, DQ, C) #
setPrivateEx(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.e = Fixnum.int32.parseHex(E).toInt();
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 {
print("Invalid RSA private key");
}
}
setPublic(N, E) #
setPublic(N,E) {
if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
//this.e = parseInt(E,16);
this.e = Fixnum.int32.parseHex(E).toInt();
//Mathx.parseInt("A");
}
else {
print("Invalid RSA public key");
}
}