pomelo-client-node-ws
Version:
pomelo client written in node.js based ws
1,505 lines (1,366 loc) • 71 kB
JavaScript
/*
* RSA Encryption / Decryption with PKCS1 v2 Padding.
*
* Copyright (c) 2003-2005 Tom Wu
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
var BigInteger = require("./jsbn.js");
var SecureRandom = require("./rng.js");
var B64 = require("./b64.js");
var ASN1HEX = require("./asn1hex-1.1.js");
// convert a (hex) string to a bignum object
function parseBigInt(str, r) {
return new BigInteger(str, r);
}
// display a string with max n characters per line
// this is use to format the input for openssl
function linebrk(buf, n) {
var s = buf.toString('ascii');
var ret = "";
var i = 0;
while (i + n < s.length) {
ret += s.substring(i, i + n) + "\n";
i += n;
}
return ret + s.substring(i, s.length);
}
function byte2Hex(b) {
if (b < 0x10)
return "0" + b.toString(16);
else
return b.toString(16);
}
// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s, n) {
if (n < s.length + 11) { // TODO: fix for utf-8
throw new Error("Message too long for RSA (n=" + n + ", l=" + s.length +
")");
return null;
}
var ba = new Array();
var i = s.length - 1;
while (i >= 0 && n > 0) {
var c = s.charCodeAt(i--);
if (c < 128) { // encode using utf-8
ba[--n] = c;
} else if ((c > 127) && (c < 2048)) {
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;
} else {
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;
}
}
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
while (n > 2) { // random non-zero pad
x[0] = 0;
while (x[0] == 0)
rng.nextBytes(x);
ba[--n] = x[0];
}
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);
}
// "empty" RSA key constructor
function 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
function RSASetPublic(N, E) {
if (N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N, 16);
this.e = parseInt(E, 16);
} else
alert("Invalid RSA public key");
}
// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}
// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(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 the PKCS#1 RSA encryption of "text" as a Base64-encoded string
// function RSAEncryptB64(text) {
// var h = this.encrypt(text);
// if(h) return hex2b64(h); else return null;
// }
// 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;
ret.push(c);
//This will need to be tested more, but Node doesn't like all of this!
//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 new Buffer(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);
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)
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);
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.
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;
// }
// Added by @eschnou
function baToString(b) {
var ret = "";
for (var i = 0; i < b.length; i++) {
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;
}
/*! rsasign-1.2.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
*/
//
// rsa-sign.js - adding signing functions to RSAKey class.
//
//
// version: 1.2.1 (08 May 2012)
//
// Copyright (c) 2010-2012 Kenji Urushima (kenji.urushima@gmail.com)
//
// This software is licensed under the terms of the MIT License.
// http://kjur.github.com/jsrsasign/license/
//
// The above copyright and license notice shall be
// included in all copies or substantial portions of the Software.
//
// Depends on:
// function sha1.hex(s) of sha1.js
// jsbn.js
// jsbn2.js
// rsa.js
// rsa2.js
//
// keysize / pmstrlen
// 512 / 128
// 1024 / 256
// 2048 / 512
// 4096 / 1024
/**
* @property {Dictionary} _RSASIGN_DIHEAD
* @description Array of head part of hexadecimal DigestInfo value for hash algorithms.
* You can add any DigestInfo hash algorith for signing.
* See PKCS#1 v2.1 spec (p38).
*/
var _RSASIGN_DIHEAD = [];
_RSASIGN_DIHEAD['sha1'] = "3021300906052b0e03021a05000414";
_RSASIGN_DIHEAD['sha256'] = "3031300d060960864801650304020105000420";
_RSASIGN_DIHEAD['sha384'] = "3041300d060960864801650304020205000430";
_RSASIGN_DIHEAD['sha512'] = "3051300d060960864801650304020305000440";
_RSASIGN_DIHEAD['md2'] = "3020300c06082a864886f70d020205000410";
_RSASIGN_DIHEAD['md5'] = "3020300c06082a864886f70d020505000410";
_RSASIGN_DIHEAD['ripemd160'] = "3021300906052b2403020105000414";
/**
* @property {Dictionary} _RSASIGN_HASHHEXFUNC
* @description Array of functions which calculate hash and returns it as hexadecimal.
* You can add any hash algorithm implementations.
*/
/*
var _RSASIGN_HASHHEXFUNC = [];
_RSASIGN_HASHHEXFUNC['sha1'] = function(s){ var sha = crypto.createHash('sha1'); sha.update(s); var out = sha.digest('hex'); return out;};
_RSASIGN_HASHHEXFUNC['sha256'] = function(s){ var sha = crypto.createHash('sha256'); sha.update(s); var out = sha.digest('hex'); return out;};
_RSASIGN_HASHHEXFUNC['sha512'] = function(s){ var sha = crypto.createHash('sha512'); sha.update(s); var out = sha.digest('hex'); return out;};
_RSASIGN_HASHHEXFUNC['md5'] = function(s){ var sha = crypto.createHash('md5'); sha.update(s); var out = sha.digest('hex'); return out;};
_RSASIGN_HASHHEXFUNC['ripemd160'] = function(s){return hex_rmd160(s);}; // http://pajhome.org.uk/crypt/md5/md5.html
*/
var _RSASIGN_HASHHEXFUNC = [];
_RSASIGN_HASHHEXFUNC['sha1'] = function (s) {
return KJUR.crypto.Util.sha1(s);
};
_RSASIGN_HASHHEXFUNC['sha256'] = function (s) {
return KJUR.crypto.Util.sha256(s);
}
_RSASIGN_HASHHEXFUNC['sha512'] = function (s) {
return KJUR.crypto.Util.sha512(s);
}
_RSASIGN_HASHHEXFUNC['md5'] = function (s) {
return KJUR.crypto.Util.md5(s);
};
_RSASIGN_HASHHEXFUNC['ripemd160'] = function (s) {
return KJUR.crypto.Util.ripemd160(s);
};
//_RSASIGN_HASHHEXFUNC['sha1'] = function(s){return sha1.hex(s);} // http://user1.matsumoto.ne.jp/~goma/js/hash.html
//_RSASIGN_HASHHEXFUNC['sha256'] = function(s){return sha256.hex;} // http://user1.matsumoto.ne.jp/~goma/js/hash.html
var _RE_HEXDECONLY = new RegExp("");
_RE_HEXDECONLY.compile("[^0-9a-f]", "gi");
// ========================================================================
// Signature Generation
// ========================================================================
function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) {
var pmStrLen = keySize / 4;
var hashFunc = _RSASIGN_HASHHEXFUNC[hashAlg];
var sHashHex = hashFunc(s);
var sHead = "0001";
var sTail = "00" + _RSASIGN_DIHEAD[hashAlg] + sHashHex;
var sMid = "";
var fLen = pmStrLen - sHead.length - sTail.length;
for (var i = 0; i < fLen; i += 2) {
sMid += "ff";
}
sPaddedMessageHex = sHead + sMid + sTail;
return sPaddedMessageHex;
}
function _zeroPaddingOfSignature(hex, bitLength) {
var s = "";
var nZero = bitLength / 4 - hex.length;
for (var i = 0; i < nZero; i++) {
s = s + "0";
}
return s + hex;
}
/**
* sign for a message string with RSA private key.<br/>
* @name signString
* @memberOf RSAKey#
* @function
* @param {String} s message string to be signed.
* @param {String} hashAlg hash algorithm name for signing.<br/>
* @return returns hexadecimal string of signature value.
*/
function _rsasign_signString(s, hashAlg) {
//alert("this.n.bitLength() = " + this.n.bitLength());
var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), hashAlg);
var biPaddedMessage = parseBigInt(hPM, 16);
var biSign = this.doPrivate(biPaddedMessage);
var hexSign = biSign.toString(16);
return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
}
function _rsasign_signStringWithSHA1(s) {
return _rsasign_signString(s, 'sha1');
}
function _rsasign_signStringWithSHA256(s) {
return _rsasign_signString(s, 'sha256');
}
// ========================================================================
// Signature Verification
// ========================================================================
function _rsasign_getDecryptSignatureBI(biSig, hN, hE) {
var rsa = new RSAKey();
rsa.setPublic(hN, hE);
var biDecryptedSig = rsa.doPublic(biSig);
return biDecryptedSig;
}
function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) {
var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
return hDigestInfo;
}
function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) {
for (var algName in _RSASIGN_DIHEAD) {
var head = _RSASIGN_DIHEAD[algName];
var len = head.length;
if (hDigestInfo.substring(0, len) == head) {
var a = [algName, hDigestInfo.substring(len)];
return a;
}
}
return [];
}
function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) {
var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE);
var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
if (digestInfoAry.length == 0) return false;
var algName = digestInfoAry[0];
var diHashValue = digestInfoAry[1];
var ff = _RSASIGN_HASHHEXFUNC[algName];
var msgHashValue = ff(sMsg);
return (diHashValue == msgHashValue);
}
function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) {
var biSig = parseBigInt(hSig, 16);
var result = _rsasign_verifySignatureWithArgs(sMsg, biSig,
this.n.toString(16),
this.e.toString(16));
return result;
}
/**
* verifies a sigature for a message string with RSA public key.<br/>
* @name verifyString
* @memberOf RSAKey#
* @function
* @param {String} sMsg message string to be verified.
* @param {String} hSig hexadecimal string of siganture.<br/>
* non-hexadecimal charactors including new lines will be ignored.
* @return returns 1 if valid, otherwise 0
*/
function _rsasign_verifyString(sMsg, hSig) {
hSig = hSig.replace(_RE_HEXDECONLY, '');
if (hSig.length != Math.ceil(this.n.bitLength() / 4)) {
return 0;
}
hSig = hSig.replace(/[ \n]+/g, "");
var biSig = parseBigInt(hSig, 16);
var biDecryptedSig = this.doPublic(biSig);
var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
if (digestInfoAry.length == 0) return false;
var algName = digestInfoAry[0];
var diHashValue = digestInfoAry[1];
var ff = _RSASIGN_HASHHEXFUNC[algName];
var msgHashValue = ff(sMsg);
return (diHashValue == msgHashValue);
}
function _rsapem_pemToBase64(sPEMPrivateKey) {
var s = sPEMPrivateKey;
s = s.replace("-----BEGIN RSA PRIVATE KEY-----", "");
s = s.replace("-----END RSA PRIVATE KEY-----", "");
s = s.replace(/[ \n]+/g, "");
return s;
}
function _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey) {
var a = new Array();
var v1 = ASN1HEX.getStartPosOfV_AtObj(hPrivateKey, 0);
var n1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, v1);
var e1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, n1);
var d1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, e1);
var p1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, d1);
var q1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, p1);
var dp1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, q1);
var dq1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dp1);
var co1 = ASN1HEX.getPosOfNextSibling_AtObj(hPrivateKey, dq1);
a.push(v1, n1, e1, d1, p1, q1, dp1, dq1, co1);
return a;
}
function _rsapem_getHexValueArrayOfChildrenFromHex(hPrivateKey) {
var posArray = _rsapem_getPosArrayOfChildrenFromHex(hPrivateKey);
var v = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[0]);
var n = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[1]);
var e = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[2]);
var d = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[3]);
var p = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[4]);
var q = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[5]);
var dp = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[6]);
var dq = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[7]);
var co = ASN1HEX.getHexOfV_AtObj(hPrivateKey, posArray[8]);
var a = new Array();
a.push(v, n, e, d, p, q, dp, dq, co);
return a;
}
/**
* read RSA private key from a ASN.1 hexadecimal string
* @name readPrivateKeyFromASN1HexString
* @memberOf RSAKey#
* @function
* @param {String} keyHex ASN.1 hexadecimal string of PKCS#1 private key.
* @since 1.1.1
*/
function _rsapem_readPrivateKeyFromASN1HexString(keyHex) {
var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex);
this.setPrivateEx(a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
}
/**
* read PKCS#1 private key from a string
* @name readPrivateKeyFromPEMString
* @memberOf RSAKey#
* @function
* @param {String} keyPEM string of PKCS#1 private key.
*/
function _rsapem_readPrivateKeyFromPEMString(keyPEM) {
var keyB64 = _rsapem_pemToBase64(keyPEM);
var keyHex = B64.b64tohex(keyB64) // depends base64.js
var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex);
this.setPrivateEx(a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
}
// protected
RSAKey.prototype.doPrivate = RSADoPrivate;
RSAKey.prototype.doPublic = RSADoPublic;
// public
RSAKey.prototype.setPrivate = RSASetPrivate;
RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
RSAKey.prototype.generate = RSAGenerate;
RSAKey.prototype.decrypt = RSADecrypt;
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
// RSAKey.prototype.b64_decrypt = RSAB64Decrypt;
// RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
RSAKey.prototype.signString = _rsasign_signString;
RSAKey.prototype.signStringWithSHA1 = _rsasign_signStringWithSHA1;
RSAKey.prototype.signStringWithSHA256 = _rsasign_signStringWithSHA256;
RSAKey.prototype.sign = _rsasign_signString;
RSAKey.prototype.signWithSHA1 = _rsasign_signStringWithSHA1;
RSAKey.prototype.signWithSHA256 = _rsasign_signStringWithSHA256;
RSAKey.prototype.verifyString = _rsasign_verifyString;
RSAKey.prototype.verifyHexSignatureForMessage = _rsasign_verifyHexSignatureForMessage;
RSAKey.prototype.verify = _rsasign_verifyString;
RSAKey.prototype.verifyHexSignatureForByteArrayMessage = _rsasign_verifyHexSignatureForMessage;
RSAKey.prototype.linebrk = linebrk;
RSAKey.prototype.readPrivateKeyFromPEMString = _rsapem_readPrivateKeyFromPEMString;
RSAKey.prototype.readPrivateKeyFromASN1HexString = _rsapem_readPrivateKeyFromASN1HexString;
// exports
exports.Key = RSAKey;
exports.BigInteger = BigInteger;
exports.linebrk = linebrk;
exports.byte2Hex = byte2Hex;
exports.hex2b64 = B64.hex2b64;
exports.b64tohex = B64.b64tohex;
exports.b64toBA = B64.b64toBA;
exports.batoString = baToString;
/*! crypto-1.0.4.js (c) 2013 Kenji Urushima | kjur.github.com/jsrsasign/license
*/
/*
* crypto.js - Cryptographic Algorithm Provider class
*
* Copyright (c) 2013 Kenji Urushima (kenji.urushima@gmail.com)
*
* This software is licensed under the terms of the MIT License.
* http://kjur.github.com/jsrsasign/license
*
* The above copyright and license notice shall be
* included in all copies or substantial portions of the Software.
*/
/**
* @fileOverview
* @name crypto-1.0.js
* @author Kenji Urushima kenji.urushima@gmail.com
* @version 1.0.4 (2013-Mar-28)
* @since 2.2
* @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
*/
/**
* kjur's class library name space
* @name KJUR
* @namespace kjur's class library name space
*/
if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
/**
* kjur's cryptographic algorithm provider library name space
* <p>
* This namespace privides following crytpgrahic classes.
* <ul>
* <li>{@link KJUR.crypto.MessageDigest} - Java JCE(cryptograhic extension) style MessageDigest class</li>
* <li>{@link KJUR.crypto.Signature} - Java JCE(cryptograhic extension) style Signature class</li>
* <li>{@link KJUR.crypto.Util} - cryptographic utility functions and properties</li>
* </ul>
* NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.
* </p>
* @name KJUR.crypto
* @namespace
*/
if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {};
/**
* static object for cryptographic function utilities
* @name KJUR.crypto.Util
* @class static object for cryptographic function utilities
* @property {Array} DIGESTINFOHEAD PKCS#1 DigestInfo heading hexadecimal bytes for each hash algorithms
* @description
*/
KJUR.crypto.Util = new function () {
this.DIGESTINFOHEAD = {
'sha1': "3021300906052b0e03021a05000414",
'sha224': "302d300d06096086480165030402040500041c",
'sha256': "3031300d060960864801650304020105000420",
'sha384': "3041300d060960864801650304020205000430",
'sha512': "3051300d060960864801650304020305000440",
'md2': "3020300c06082a864886f70d020205000410",
'md5': "3020300c06082a864886f70d020505000410",
'ripemd160': "3021300906052b2403020105000414"
};
/**
* get hexadecimal DigestInfo
* @name getDigestInfoHex
* @memberOf KJUR.crypto.Util
* @function
* @param {String} hHash hexadecimal hash value
* @param {String} alg hash algorithm name (ex. 'sha1')
* @return {String} hexadecimal string DigestInfo ASN.1 structure
*/
this.getDigestInfoHex = function (hHash, alg) {
if (typeof this.DIGESTINFOHEAD[alg] == "undefined")
throw "alg not supported in Util.DIGESTINFOHEAD: " + alg;
return this.DIGESTINFOHEAD[alg] + hHash;
};
/**
* get PKCS#1 padded hexadecimal DigestInfo
* @name getPaddedDigestInfoHex
* @memberOf KJUR.crypto.Util
* @function
* @param {String} hHash hexadecimal hash value
* @param {String} alg hash algorithm name (ex. 'sha1')
* @param {Integer} keySize key bit length (ex. 1024)
* @return {String} hexadecimal string of PKCS#1 padded DigestInfo
*/
this.getPaddedDigestInfoHex = function (hHash, alg, keySize) {
var hDigestInfo = this.getDigestInfoHex(hHash, alg);
var pmStrLen = keySize / 4; // minimum PM length
if (hDigestInfo.length + 22 > pmStrLen) // len(0001+ff(*8)+00+hDigestInfo)=22
throw "key is too short for SigAlg: keylen=" + keySize + "," + alg;
var hHead = "0001";
var hTail = "00" + hDigestInfo;
var hMid = "";
var fLen = pmStrLen - hHead.length - hTail.length;
for (var i = 0; i < fLen; i += 2) {
hMid += "ff";
}
var hPaddedMessage = hHead + hMid + hTail;
return hPaddedMessage;
};
/**
* get hexadecimal SHA1 hash of string
* @name sha1
* @memberOf KJUR.crypto.Util
* @function
* @param {String} s input string to be hashed
* @return {String} hexadecimal string of hash value
* @since 1.0.3
*/
this.sha1 = function (s) {
var md = new KJUR.crypto.MessageDigest({
'alg': 'sha1',
'prov': 'cryptojs'
});
return md.digestString(s);
};
/**
* get hexadecimal SHA256 hash of string
* @name sha256
* @memberOf KJUR.crypto.Util
* @function
* @param {String} s input string to be hashed
* @return {String} hexadecimal string of hash value
* @since 1.0.3
*/
this.sha256 = function (s) {
var md = new KJUR.crypto.MessageDigest({
'alg': 'sha256',
'prov': 'cryptojs'
});
return md.digestString(s);
};
/**
* get hexadecimal SHA512 hash of string
* @name sha512
* @memberOf KJUR.crypto.Util
* @function
* @param {String} s input string to be hashed
* @return {String} hexadecimal string of hash value
* @since 1.0.3
*/
this.sha512 = function (s) {
var md = new KJUR.crypto.MessageDigest({
'alg': 'sha512',
'prov': 'cryptojs'
});
return md.digestString(s);
};
/**
* get hexadecimal MD5 hash of string
* @name md5
* @memberOf KJUR.crypto.Util
* @function
* @param {String} s input string to be hashed
* @return {String} hexadecimal string of hash value
* @since 1.0.3
*/
this.md5 = function (s) {
var md = new KJUR.crypto.MessageDigest({
'alg': 'md5',
'prov': 'cryptojs'
});
return md.digestString(s);
};
/**
* get hexadecimal RIPEMD160 hash of string
* @name ripemd160
* @memberOf KJUR.crypto.Util
* @function
* @param {String} s input string to be hashed
* @return {String} hexadecimal string of hash value
* @since 1.0.3
*/
this.ripemd160 = function (s) {
var md = new KJUR.crypto.MessageDigest({
'alg': 'ripemd160',
'prov': 'cryptojs'
});
return md.digestString(s);
};
};
/**
* MessageDigest class which is very similar to java.security.MessageDigest class
* @name KJUR.crypto.MessageDigest
* @class MessageDigest class which is very similar to java.security.MessageDigest class
* @param {Array} params parameters for constructor
* @description
* <br/>
* Currently this supports following algorithm and providers combination:
* <ul>
* <li>md5 - cryptojs</li>
* <li>sha1 - cryptojs</li>
* <li>sha224 - cryptojs</li>
* <li>sha256 - cryptojs</li>
* <li>sha384 - cryptojs</li>
* <li>sha512 - cryptojs</li>
* <li>ripemd160 - cryptojs</li>
* <li>sha256 - sjcl (NEW from crypto.js 1.0.4)</li>
* </ul>
* @example
* // CryptoJS provider sample
* <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/core.js"></script>
* <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/sha1.js"></script>
* <script src="crypto-1.0.js"></script>
* var md = new KJUR.crypto.MessageDigest({alg: "sha1", prov: "cryptojs"});
* md.updateString('aaa')
* var mdHex = md.digest()
*
* // SJCL(Stanford JavaScript Crypto Library) provider sample
* <script src="http://bitwiseshiftleft.github.io/sjcl/sjcl.js"></script>
* <script src="crypto-1.0.js"></script>
* var md = new KJUR.crypto.MessageDigest({alg: "sha256", prov: "sjcl"}); // sjcl supports sha256 only
* md.updateString('aaa')
* var mdHex = md.digest()
*/
KJUR.crypto.MessageDigest = function (params) {
var md = null;
var algName = null;
var provName = null;
var _CryptoJSMdName = {
'md5': 'CryptoJS.algo.MD5',
'sha1': 'CryptoJS.algo.SHA1',
'sha224': 'CryptoJS.algo.SHA224',
'sha256': 'CryptoJS.algo.SHA256',
'sha384': 'CryptoJS.algo.SHA384',
'sha512': 'CryptoJS.algo.SHA512',
'ripemd160': 'CryptoJS.algo.RIPEMD160'
};
/**
* set hash algorithm and provider
* @name setAlgAndProvider
* @memberOf KJUR.crypto.MessageDigest
* @function
* @param {String} alg hash algorithm name
* @param {String} prov provider name
* @description
* @example
* // for SHA1
* md.setAlgAndProvider('sha1', 'cryptojs');
* // for RIPEMD160
* md.setAlgAndProvider('ripemd160', 'cryptojs');
*/
this.setAlgAndProvider = function (alg, prov) {
if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(alg) != -1 &&
prov == 'cryptojs') {
try {
this.md = eval(_CryptoJSMdName[alg]).create();
} catch (ex) {
throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex;
}
this.updateString = function (str) {
this.md.update(str);
};
this.updateHex = function (hex) {
var wHex = CryptoJS.enc.Hex.parse(hex);
this.md.update(wHex);
};
this.digest = function () {
var hash = this.md.finalize();
return hash.toString(CryptoJS.enc.Hex);
};
this.digestString = function (str) {
this.updateString(str);
return this.digest();
};
this.digestHex = function (hex) {
this.updateHex(hex);
return this.digest();
};
}
if (':sha256:'.indexOf(alg) != -1 &&
prov == 'sjcl') {
try {
this.md = new sjcl.hash.sha256();
} catch (ex) {
throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex;
}
this.updateString = function (str) {
this.md.update(str);
};
this.updateHex = function (hex) {
var baHex = sjcl.codec.hex.toBits(hex);
this.md.update(baHex);
};
this.digest = function () {
var hash = this.md.finalize();
return sjcl.codec.hex.fromBits(hash);
};
this.digestString = function (str) {
this.updateString(str);
return this.digest();
};
this.digestHex = function (hex) {
this.updateHex(hex);
return this.digest();
};
}
};
/**
* update digest by specified string
* @name updateString
* @memberOf KJUR.crypto.MessageDigest
* @function
* @param {String} str string to update
* @description
* @example
* md.updateString('New York');
*/
this.updateString = function (str) {
throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
};
/**
* update digest by specified hexadecimal string
* @name updateHex
* @memberOf KJUR.crypto.MessageDigest
* @function
* @param {String} hex hexadecimal string to update
* @description
* @example
* md.updateHex('0afe36');
*/
this.updateHex = function (hex) {
throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
};
/**
* completes hash calculation and returns hash result
* @name digest
* @memberOf KJUR.crypto.MessageDigest
* @function
* @description
* @example
* md.digest()
*/
this.digest = function () {
throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName;
};
/**
* performs final update on the digest using string, then completes the digest computation
* @name digestString
* @memberOf KJUR.crypto.MessageDigest
* @function
* @param {String} str string to final update
* @description
* @example
* md.digestString('aaa')
*/
this.digestString = function (str) {
throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
};
/**
* performs final update on the digest using hexadecimal string, then completes the digest computation
* @name digestHex
* @memberOf KJUR.crypto.MessageDigest
* @function
* @param {String} hex hexadecimal string to final update
* @description
* @example
* md.digestHex('0f2abd')
*/
this.digestHex = function (hex) {
throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
};
if (typeof params != "undefined") {
if (typeof params['alg'] != "undefined") {
this.algName = params['alg'];
this.provName = params['prov'];
this.setAlgAndProvider(params['alg'], params['prov']);
}
}
};
/**
* Signature class which is very similar to java.security.Signature class
* @name KJUR.crypto.Signature
* @class Signature class which is very similar to java.security.Signature class
* @param {Array} params parameters for constructor
* @property {String} state Current state of this signature object whether 'SIGN', 'VERIFY' or null
* @description
* <br/>
* As for params of constructor's argument, it can be specify following attributes:
* <ul>
* <li>alg - signature algorithm name (ex. {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160}withRSA)</li>
* <li>provider - currently 'cryptojs/jsrsa' only</li>
* <li>prvkeypem - PEM string of signer's private key. If this specified, no need to call initSign(prvKey).</li>
* </ul>
* <h4>SUPPORTED ALGORITHMS AND PROVIDERS</h4>
* Signature class supports {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160}
* withRSA algorithm in 'cryptojs/jsrsa' provider.
* <h4>EXAMPLES</h4>
* @example
* // signature generation
* var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA", "prov": "cryptojs/jsrsa"});
* sig.initSign(prvKey);
* sig.updateString('aaa');
* var hSigVal = sig.sign();
*
* // signature validation
* var sig2 = new KJUR.crypto.Signature({"alg": "SHA1withRSA", "prov": "cryptojs/jsrsa"});
* sig2.initVerifyByCertificatePEM(cert)
* sig.updateString('aaa');
* var isValid = sig2.verify(hSigVal);
*/
KJUR.crypto.Signature = function (params) {
var prvKey = null; // RSAKey for signing
var pubKey = null; // RSAKey for verifying
var md = null; // KJUR.crypto.MessageDigest object
var sig = null;
var algName = null;
var provName = null;
var algProvName = null;
var mdAlgName = null;
var pubkeyAlgName = null;
var state = null;
var sHashHex = null; // hex hash value for hex
var hDigestInfo = null;
var hPaddedDigestInfo = null;
var hSign = null;
this._setAlgNames = function () {
if (this.algName.match(/^(.+)with(.+)$/)) {
this.mdAlgName = RegExp.$1.toLowerCase();
this.pubkeyAlgName = RegExp.$2.toLowerCase();
}
};
this._zeroPaddingOfSignature = function (hex, bitLength) {
var s = "";
var nZero = bitLength / 4 - hex.length;
for (var i = 0; i < nZero; i++) {
s = s + "0";
}
return s + hex;
};
/**
* set signature algorithm and provider
* @name setAlgAndProvider
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} alg signature algorithm name
* @param {String} prov provider name
* @description
* @example
* md.setAlgAndProvider('SHA1withRSA', 'cryptojs/jsrsa');
*/
this.setAlgAndProvider = function (alg, prov) {
this._setAlgNames();
if (prov != 'cryptojs/jsrsa')
throw "provider not supported: " + prov;
if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(this.mdAlgName) != -1) {
try {
this.md = new KJUR.crypto.MessageDigest({
'alg': this.mdAlgName,
'prov': 'cryptojs'
});
} catch (ex) {
throw "setAlgAndProvider hash alg set fail alg=" + this.mdAlgName + "/" + ex;
}
this.initSign = function (prvKey) {
this.prvKey = prvKey;
this.state = "SIGN";
};
this.initVerifyByPublicKey = function (rsaPubKey) {
this.pubKey = rsaPubKey;
this.state = "VERIFY";
};
this.initVerifyByCertificatePEM = function (certPEM) {
var x509 = new X509();
x509.readCertPEM(certPEM);
this.pubKey = x509.subjectPublicKeyRSA;
this.state = "VERIFY";
};
this.updateString = function (str) {
this.md.updateString(str);
};
this.updateHex = function (hex) {
this.md.updateHex(hex);
};
this.sign = function () {
var util = KJUR.crypto.Util;
var keyLen = this.prvKey.n.bitLength();
this.sHashHex = this.md.digest();
this.hDigestInfo = util.getDigestInfoHex(this.sHashHex, this.mdAlgName);
this.hPaddedDigestInfo =
util.getPaddedDigestInfoHex(this.sHashHex, this.mdAlgName, keyLen);
var biPaddedDigestInfo = parseBigInt(this.hPaddedDigestInfo, 16);
this.hoge = biPaddedDigestInfo.toString(16);
var biSign = this.prvKey.doPrivate(biPaddedDigestInfo);
this.hSign = this._zeroPaddingOfSignature(biSign.toString(16), keyLen);
return this.hSign;
};
this.signString = function (str) {
this.updateString(str);
this.sign();
};
this.signHex = function (hex) {
this.updateHex(hex);
this.sign();
};
this.verify = function (hSigVal) {
var util = KJUR.crypto.Util;
var keyLen = this.pubKey.n.bitLength();
this.sHashHex = this.md.digest();
var biSigVal = parseBigInt(hSigVal, 16);
var biPaddedDigestInfo = this.pubKey.doPublic(biSigVal);
this.hPaddedDigestInfo = biPaddedDigestInfo.toString(16);
var s = this.hPaddedDigestInfo;
s = s.replace(/^1ff+00/, '');
var hDIHEAD = KJUR.crypto.Util.DIGESTINFOHEAD[this.mdAlgName];
if (s.indexOf(hDIHEAD) != 0) {
return false;
}
var hHashFromDI = s.substr(hDIHEAD.length);
//alert(hHashFromDI + "\n" + this.sHashHex);
return (hHashFromDI == this.sHashHex);
};
}
};
/**
* Initialize this object for verifying with a public key
* @name initVerifyByPublicKey
* @memberOf KJUR.crypto.Signature
* @function
* @param {RSAKey} rsaPubKey RSAKey object of public key
* @since 1.0.2
* @description
* @example
* sig.initVerifyByPublicKey(prvKey)
*/
this.initVerifyByPublicKey = function (rsaPubKey) {
throw "initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov=" + this.algProvName;
};
/**
* Initialize this object for verifying with a certficate
* @name initVerifyByCertificatePEM
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} certPEM PEM formatted string of certificate
* @since 1.0.2
* @description
* @example
* sig.initVerifyByCertificatePEM(certPEM)
*/
this.initVerifyByCertificatePEM = function (certPEM) {
throw "initVerifyByCertificatePEM(certPEM) not supported for this alg:prov=" + this.algProvName;
};
/**
* Initialize this object for signing
* @name initSign
* @memberOf KJUR.crypto.Signature
* @function
* @param {RSAKey} prvKey RSAKey object of private key
* @description
* @example
* sig.initSign(prvKey)
*/
this.initSign = function (prvKey) {
throw "initSign(prvKey) not supported for this alg:prov=" + this.algProvName;
};
/**
* Updates the data to be signed or verified by a string
* @name updateString
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} str string to use for the update
* @description
* @example
* sig.updateString('aaa')
*/
this.updateString = function (str) {
throw "updateString(str) not supported for this alg:prov=" + this.algProvName;
};
/**
* Updates the data to be signed or verified by a hexadecimal string
* @name updateHex
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} hex hexadecimal string to use for the update
* @description
* @example
* sig.updateHex('1f2f3f')
*/
this.updateHex = function (hex) {
throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName;
};
/**
* Returns the signature bytes of all data updates as a hexadecimal string
* @name sign
* @memberOf KJUR.crypto.Signature
* @function
* @return the signature bytes as a hexadecimal string
* @description
* @example
* var hSigValue = sig.sign()
*/
this.sign = function () {
throw "sign() not supported for this alg:prov=" + this.algProvName;
};
/**
* performs final update on the sign using string, then returns the signature bytes of all data updates as a hexadecimal string
* @name signString
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} str string to final update
* @return the signature bytes of a hexadecimal string
* @description
* @example
* var hSigValue = sig.signString('aaa')
*/
this.signString = function (str) {
throw "digestString(str) not supported for this alg:prov=" + this.algProvName;
};
/**
* performs final update on the sign using hexadecimal string, then returns the signature bytes of all data updates as a hexadecimal string
* @name signHex
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} hex hexadecimal string to final update
* @return the signature bytes of a hexadecimal string
* @description
* @example
* var hSigValue = sig.signHex('1fdc33')
*/
this.signHex = function (hex) {
throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName;
};
/**
* verifies the passed-in signature.
* @name verify
* @memberOf KJUR.crypto.Signature
* @function
* @param {String} str string to final update
* @return {Boolean} true if the signature was verified, otherwise false
* @description
* @example
* var isValid = sig.verify('1fbcefdca4823a7(snip)')
*/
this.verify = function (hSigVal) {
throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName;
};
if (typeof params != "undefined") {
if (typeof params['alg'] != "undefined") {
this.algName = params['alg'];
this.provName = params['prov'];
this.algProvName = params['alg'] + ":" + params['prov'];
this.setAlgAndProvider(params['alg'], params['prov']);
this._setAlgNames();
}
if (typeof params['prvkeypem'] != "undefined") {
if (typeof params['prvkeypas'] != "undefined") {
throw "both prvkeypem and prvkeypas parameters not supported";
} else {
try {
var prvKey = new RSAKey();
prvKey.readPrivateKeyFromPEMString(params['prvkeypem']);
this.initSign(prvKey);
} catch (ex) {
throw "fatal error to load pem private key: " + ex;
}
}
}
}
};
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
/**
* CryptoJS core components.
*/
var CryptoJS = CryptoJS || (function (Math, undefined) {
/**
* CryptoJS namespace.
*/
var C = {};
/**
* Library namespace.
*/
var C_lib = C.lib = {};
/**
* Base object for prototypal inheritance.
*/
var Base = C_lib.Base = (function () {
function F() {}
return {
/**
* Creates a new object that inherits from this object.
*
* @param {Object} overrides Properties to copy into the new object.
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* field: 'value',
*
* method: function () {
* }
* });
*/
extend: function (overrides) {
// Spawn
F.prototype = this;
var subtype = new F();
// Augment
if (overrides) {
subtype.mixIn(overrides);
}
// Create default initializer
if (!subtype.hasOwnProperty('init')) {
subtype.init = function () {
subtype.$super.init.apply(this, arguments);
};
}
// Initializer's prototype is the subtype object
subtype.init.prototype = subtype;
// Reference supertype
subtype.$super = this;
return subtype;
},
/**
* Extends this object and runs the init method.
* Arguments to create() will be passed to init().
*
* @return {Object} The new object.
*
* @static
*
* @example
*
* var instance = MyType.create();
*/
create: function () {
var instance = this.extend();
instance.init.apply(instance, arguments);
return instance;
},
/**
* Initializes a newly created object.
* Override this method to add some logic when your objects are created.
*
* @example
*
* var MyType = CryptoJS.lib.Base.extend({
* init: function () {
* // ...
* }
* });
*/
init: function () {},
/**
* Copies properties into this object.
*
* @param {Object} properties The properties to mix in.
*
* @example
*
* MyType.mixIn({
* field: 'value'
* });
*/
mixIn: function (properties) {
for (var propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
this[propertyName] = properties[propertyName];
}
}
// IE won't copy toString using the loop above
if (properties.hasOwnProperty('toString')) {
this.toString = properties.toString;
}
},
/**
* Creates a copy of this object.
*
* @return {Object} The clone.
*
* @example
*
* var clone = instance.clone();
*/
clone: function () {
return this.init.prototype.extend(this);
}
};
}());
/**
* An array of 32-bit words.
*
* @property {Array} words The array of 32-bit words.
* @property {number} sigBytes The number of significant bytes in this word array.
*/
var WordArray = C_lib.WordArray = Base.extend({
/**
* Initializes a newly created word array.
*
* @param {Array} words (Optional) An array of 32-bit words.
* @param {number} sigBytes (Optional) The number of significant bytes in the words.
*
* @example
*
* var wordArray = CryptoJS.lib.WordArray.create();
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
* var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
*/
init: function (words, sigBytes) {
words = this.words = words || [];
if (sigBytes != undefined) {