/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ */ // 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; } // PKCS#1 (OAEP) mask generation function function oaep_mgf1_str(seed, len, hash) { var mask = '', i = 0; while (mask.length < len) { mask += hash(seed + String.fromCharCode.apply(String, [ (i & 0xff000000) >> 24, (i & 0x00ff0000) >> 16, (i & 0x0000ff00) >> 8, i & 0x000000ff])); i += 1; } return mask; } /** * Undo PKCS#1 (OAEP) padding and, if valid, return the plaintext * @name oaep_unpad * @param {BigInteger} d BigInteger object of OAEP padded message * @param n byte length of RSA key (i.e. 128 when RSA 1024bit) * @param hash JavaScript function to calculate raw hash value from raw string or algorithm name (ex. "SHA1") * @param hashLen byte length of resulted hash value (i.e. 20 for SHA1) * @return {String} raw string of OAEP unpadded message * @description * This function do unpadding OAEP padded message then returns an original message.
* NOTE: Since jsrsasign 6.2.0, 'hash' argument can accept an algorithm name such as "sha1". * @example * // DEFAULT(SHA1) * bi1 = oaep_pad("aaa", 128); * oaep_unpad(bi1, 128) → "aaa" // SHA-1 by default */ function oaep_unpad(d, n, hash, hashLen) { var MD = KJUR.crypto.MessageDigest; var Util = KJUR.crypto.Util; var algName = null; if (!hash) hash = "sha1"; if (typeof hash === "string") { algName = MD.getCanonicalAlgName(hash); hashLen = MD.getHashLength(algName); hash = function(s) { return hextorstr(Util.hashHex(rstrtohex(s), algName)); }; } d = d.toByteArray(); var i; for (i = 0; i < d.length; i += 1) { d[i] &= 0xff; } while (d.length < n) { d.unshift(0); } d = String.fromCharCode.apply(String, d); if (d.length < 2 * hashLen + 2) { throw "Cipher too short"; } var maskedSeed = d.substr(1, hashLen) var maskedDB = d.substr(hashLen + 1); var seedMask = oaep_mgf1_str(maskedDB, hashLen, hash); var seed = [], i; for (i = 0; i < maskedSeed.length; i += 1) { seed[i] = maskedSeed.charCodeAt(i) ^ seedMask.charCodeAt(i); } var dbMask = oaep_mgf1_str(String.fromCharCode.apply(String, seed), d.length - hashLen, hash); var DB = []; for (i = 0; i < maskedDB.length; i += 1) { DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i); } DB = String.fromCharCode.apply(String, DB); if (DB.substr(0, hashLen) !== hash('')) { throw "Hash mismatch"; } DB = DB.substr(hashLen); var first_one = DB.indexOf('\x01'); var last_zero = (first_one != -1) ? DB.substr(0, first_one).lastIndexOf('\x00') : -1; if (last_zero + 1 != first_one) { throw "Malformed data"; } return DB.substr(first_one + 1); } // Set the private key fields N, e, and d from hex strings function RSASetPrivate(N,E,D) { this.isPrivate = true; if (typeof N !== "string") { this.n = N; this.e = E; this.d = D; } else 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 throw "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) { this.isPrivate = true; this.isPublic = false; if (N == null) throw "RSASetPrivateEx N == null"; if (E == null) throw "RSASetPrivateEx E == null"; if (N.length == 0) throw "RSASetPrivateEx N.length == 0"; if (E.length == 0) throw "RSASetPrivateEx E.length == 0"; 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 { throw "Invalid RSA private key in RSASetPrivateEx"; } } // 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 if (this.n.bitLength() == B) { 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; } } } this.isPrivate = true; } // 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) { if (ctext.length != Math.ceil(this.n.bitLength() / 4.0)) { throw new Error("wrong ctext length"); } 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 OAEP RSA decryption of "ctext". // "ctext" is an even-length hex string and the output is a plain string. function RSADecryptOAEP(ctext, hash, hashLen) { if (ctext.length != Math.ceil(this.n.bitLength() / 4.0)) { throw new Error("wrong ctext length"); } var c = parseBigInt(ctext, 16); var m = this.doPrivate(c); if(m == null) return null; return oaep_unpad(m, (this.n.bitLength()+7)>>3, hash, hashLen); } // 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.decryptOAEP = RSADecryptOAEP; //RSAKey.prototype.b64_decrypt = RSAB64Decrypt;