Meki Cherkaoui | 88d59cd | 2012-05-14 07:34:58 -0700 | [diff] [blame^] | 1 | // Depends on rsa.js and jsbn2.js
|
| 2 |
|
| 3 | // Version 1.1: support utf-8 decoding in pkcs1unpad2
|
| 4 |
|
| 5 | // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
|
| 6 | function pkcs1unpad2(d,n) {
|
| 7 | var b = d.toByteArray();
|
| 8 | var i = 0;
|
| 9 | while(i < b.length && b[i] == 0) ++i;
|
| 10 | if(b.length-i != n-1 || b[i] != 2)
|
| 11 | return null;
|
| 12 | ++i;
|
| 13 | while(b[i] != 0)
|
| 14 | if(++i >= b.length) return null;
|
| 15 | var ret = "";
|
| 16 | while(++i < b.length) {
|
| 17 | var c = b[i] & 255;
|
| 18 | if(c < 128) { // utf-8 decode
|
| 19 | ret += String.fromCharCode(c);
|
| 20 | }
|
| 21 | else if((c > 191) && (c < 224)) {
|
| 22 | ret += String.fromCharCode(((c & 31) << 6) | (b[i+1] & 63));
|
| 23 | ++i;
|
| 24 | }
|
| 25 | else {
|
| 26 | ret += String.fromCharCode(((c & 15) << 12) | ((b[i+1] & 63) << 6) | (b[i+2] & 63));
|
| 27 | i += 2;
|
| 28 | }
|
| 29 | }
|
| 30 | return ret;
|
| 31 | }
|
| 32 |
|
| 33 | // Set the private key fields N, e, and d from hex strings
|
| 34 | function RSASetPrivate(N,E,D) {
|
| 35 | if(N != null && E != null && N.length > 0 && E.length > 0) {
|
| 36 | this.n = parseBigInt(N,16);
|
| 37 | this.e = parseInt(E,16);
|
| 38 | this.d = parseBigInt(D,16);
|
| 39 | }
|
| 40 | else
|
| 41 | alert("Invalid RSA private key");
|
| 42 | }
|
| 43 |
|
| 44 | // Set the private key fields N, e, d and CRT params from hex strings
|
| 45 | function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) {
|
| 46 | if(N != null && E != null && N.length > 0 && E.length > 0) {
|
| 47 | this.n = parseBigInt(N,16);
|
| 48 | this.e = parseInt(E,16);
|
| 49 | this.d = parseBigInt(D,16);
|
| 50 | this.p = parseBigInt(P,16);
|
| 51 | this.q = parseBigInt(Q,16);
|
| 52 | this.dmp1 = parseBigInt(DP,16);
|
| 53 | this.dmq1 = parseBigInt(DQ,16);
|
| 54 | this.coeff = parseBigInt(C,16);
|
| 55 | }
|
| 56 | else
|
| 57 | alert("Invalid RSA private key");
|
| 58 | }
|
| 59 |
|
| 60 | // Generate a new random private key B bits long, using public expt E
|
| 61 | function RSAGenerate(B,E) {
|
| 62 | var rng = new SecureRandom();
|
| 63 | var qs = B>>1;
|
| 64 | this.e = parseInt(E,16);
|
| 65 | var ee = new BigInteger(E,16);
|
| 66 | for(;;) {
|
| 67 | for(;;) {
|
| 68 | this.p = new BigInteger(B-qs,1,rng);
|
| 69 | if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;
|
| 70 | }
|
| 71 | for(;;) {
|
| 72 | this.q = new BigInteger(qs,1,rng);
|
| 73 | if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break;
|
| 74 | }
|
| 75 | if(this.p.compareTo(this.q) <= 0) {
|
| 76 | var t = this.p;
|
| 77 | this.p = this.q;
|
| 78 | this.q = t;
|
| 79 | }
|
| 80 | var p1 = this.p.subtract(BigInteger.ONE); // p1 = p - 1
|
| 81 | var q1 = this.q.subtract(BigInteger.ONE); // q1 = q - 1
|
| 82 | var phi = p1.multiply(q1);
|
| 83 | if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
|
| 84 | this.n = this.p.multiply(this.q); // this.n = p * q
|
| 85 | this.d = ee.modInverse(phi); // this.d =
|
| 86 | this.dmp1 = this.d.mod(p1); // this.dmp1 = d mod (p - 1)
|
| 87 | this.dmq1 = this.d.mod(q1); // this.dmq1 = d mod (q - 1)
|
| 88 | this.coeff = this.q.modInverse(this.p); // this.coeff = (q ^ -1) mod p
|
| 89 | break;
|
| 90 | }
|
| 91 | }
|
| 92 | }
|
| 93 |
|
| 94 | // Perform raw private operation on "x": return x^d (mod n)
|
| 95 | function RSADoPrivate(x) {
|
| 96 | if(this.p == null || this.q == null)
|
| 97 | return x.modPow(this.d, this.n);
|
| 98 |
|
| 99 | // TODO: re-calculate any missing CRT params
|
| 100 | var xp = x.mod(this.p).modPow(this.dmp1, this.p); // xp=cp?
|
| 101 | var xq = x.mod(this.q).modPow(this.dmq1, this.q); // xq=cq?
|
| 102 |
|
| 103 | while(xp.compareTo(xq) < 0)
|
| 104 | xp = xp.add(this.p);
|
| 105 | // NOTE:
|
| 106 | // xp.subtract(xq) => cp -cq
|
| 107 | // xp.subtract(xq).multiply(this.coeff).mod(this.p) => (cp - cq) * u mod p = h
|
| 108 | // xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq) => cq + (h * q) = M
|
| 109 | return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
|
| 110 | }
|
| 111 |
|
| 112 | // Return the PKCS#1 RSA decryption of "ctext".
|
| 113 | // "ctext" is an even-length hex string and the output is a plain string.
|
| 114 | function RSADecrypt(ctext) {
|
| 115 | var c = parseBigInt(ctext, 16);
|
| 116 | var m = this.doPrivate(c);
|
| 117 | if(m == null) return null;
|
| 118 | return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
|
| 119 | }
|
| 120 |
|
| 121 | // Return the PKCS#1 RSA decryption of "ctext".
|
| 122 | // "ctext" is a Base64-encoded string and the output is a plain string.
|
| 123 | //function RSAB64Decrypt(ctext) {
|
| 124 | // var h = b64tohex(ctext);
|
| 125 | // if(h) return this.decrypt(h); else return null;
|
| 126 | //}
|
| 127 |
|
| 128 | // protected
|
| 129 | RSAKey.prototype.doPrivate = RSADoPrivate;
|
| 130 |
|
| 131 | // public
|
| 132 | RSAKey.prototype.setPrivate = RSASetPrivate;
|
| 133 | RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
|
| 134 | RSAKey.prototype.generate = RSAGenerate;
|
| 135 | RSAKey.prototype.decrypt = RSADecrypt;
|
| 136 | //RSAKey.prototype.b64_decrypt = RSAB64Decrypt;
|