832 lines
29 KiB
Plaintext
832 lines
29 KiB
Plaintext
|
/** @preserve
|
||
|
* jwsjs.js - JSON Web Signature JSON Serialization (JWSJS) Class
|
||
|
*
|
||
|
* version: 2.0.0 (2013 Jul 20)
|
||
|
*
|
||
|
* Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
|
||
|
*
|
||
|
* This software is licensed under the terms of the MIT License.
|
||
|
* http://kjur.github.com/jsjws/license/
|
||
|
*
|
||
|
* The above copyright and license notice shall be
|
||
|
* included in all copies or substantial portions of the Software.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @fileOverview
|
||
|
* @name jwsjs-2.0.js
|
||
|
* @author Kenji Urushima kenji.urushima@gmail.com
|
||
|
* @version 2.0.0 (2013 Jul 20)
|
||
|
* @since jsjws 1.2
|
||
|
* @license <a href="http://kjur.github.io/jsjws/license/">MIT License</a>
|
||
|
*/
|
||
|
|
||
|
if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
|
||
|
if (typeof KJUR.jws == "undefined" || !KJUR.jws) KJUR.jws = {};
|
||
|
|
||
|
/**
|
||
|
* JSON Web Signature JSON Serialization (JWSJS) class.<br/>
|
||
|
* @class JSON Web Signature JSON Serialization (JWSJS) class
|
||
|
* @name KJUR.jws.JWSJS
|
||
|
* @property {array of String} aHeader array of Encoded JWS Headers
|
||
|
* @property {String} sPayload Encoded JWS payload
|
||
|
* @property {array of String} aSignature array of Encoded JWS signature value
|
||
|
* @author Kenji Urushima
|
||
|
* @version 1.0 (18 May 2012)
|
||
|
* @requires base64x.js, json-sans-eval.js, jws.js and jsrsasign library
|
||
|
* @see <a href="http://kjur.github.com/jsjws/">'jwjws'(JWS JavaScript Library) home page http://kjur.github.com/jsjws/</a>
|
||
|
* @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a>
|
||
|
* @see <a href="http://tools.ietf.org/html/draft-jones-json-web-signature-json-serialization-01">IETF I-D JSON Web Signature JSON Serialization (JWS-JS) specification</a>
|
||
|
*/
|
||
|
KJUR.jws.JWSJS = function() {
|
||
|
this.aHeader = [];
|
||
|
this.sPayload = "";
|
||
|
this.aSignature = [];
|
||
|
|
||
|
// == initialize ===================================================================
|
||
|
/**
|
||
|
* (re-)initialize this object.<br/>
|
||
|
* @name init
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
*/
|
||
|
this.init = function() {
|
||
|
this.aHeader = [];
|
||
|
this.sPayload = "";
|
||
|
this.aSignature = [];
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* (re-)initialize and set first signature with JWS.<br/>
|
||
|
* @name initWithJWS
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @param {String} sJWS JWS signature to set
|
||
|
* @function
|
||
|
*/
|
||
|
this.initWithJWS = function(sJWS) {
|
||
|
this.init();
|
||
|
|
||
|
var jws = new KJUR.jws.JWS();
|
||
|
jws.parseJWS(sJWS);
|
||
|
|
||
|
this.aHeader.push(jws.parsedJWS.headB64U);
|
||
|
this.sPayload = jws.parsedJWS.payloadB64U;
|
||
|
this.aSignature.push(jws.parsedJWS.sigvalB64U);
|
||
|
};
|
||
|
|
||
|
// == add signature ===================================================================
|
||
|
/**
|
||
|
* add a signature to existing JWS-JS by Header and PKCS1 private key.<br/>
|
||
|
* @name addSignatureByHeaderKey
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
* @param {String} sHead JSON string of JWS Header for adding signature.
|
||
|
* @param {String} sPemPrvKey string of PKCS1 private key
|
||
|
*/
|
||
|
this.addSignatureByHeaderKey = function(sHead, sPemPrvKey) {
|
||
|
var sPayload = b64utoutf8(this.sPayload);
|
||
|
|
||
|
var jws = new KJUR.jws.JWS();
|
||
|
var sJWS = jws.generateJWSByP1PrvKey(sHead, sPayload, sPemPrvKey);
|
||
|
|
||
|
this.aHeader.push(jws.parsedJWS.headB64U);
|
||
|
this.aSignature.push(jws.parsedJWS.sigvalB64U);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* add a signature to existing JWS-JS by Header, Payload and PKCS1 private key.<br/>
|
||
|
* This is to add first signature to JWS-JS object.
|
||
|
* @name addSignatureByHeaderPayloadKey
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
* @param {String} sHead JSON string of JWS Header for adding signature.
|
||
|
* @param {String} sPayload string of JWS Payload for adding signature.
|
||
|
* @param {String} sPemPrvKey string of PKCS1 private key
|
||
|
*/
|
||
|
this.addSignatureByHeaderPayloadKey = function(sHead, sPayload, sPemPrvKey) {
|
||
|
var jws = new KJUR.jws.JWS();
|
||
|
var sJWS = jws.generateJWSByP1PrvKey(sHead, sPayload, sPemPrvKey);
|
||
|
|
||
|
this.aHeader.push(jws.parsedJWS.headB64U);
|
||
|
this.sPayload = jws.parsedJWS.payloadB64U;
|
||
|
this.aSignature.push(jws.parsedJWS.sigvalB64U);
|
||
|
};
|
||
|
|
||
|
// == verify signature ===================================================================
|
||
|
/**
|
||
|
* verify JWS-JS object with array of certificate string.<br/>
|
||
|
* @name verifyWithCerts
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
* @param {array of String} aCert array of string for X.509 PEM certificate.
|
||
|
* @return 1 if signature is valid.
|
||
|
* @throw if JWS-JS signature is invalid.
|
||
|
*/
|
||
|
this.verifyWithCerts = function(aCert) {
|
||
|
if (this.aHeader.length != aCert.length)
|
||
|
throw "num headers does not match with num certs";
|
||
|
if (this.aSignature.length != aCert.length)
|
||
|
throw "num signatures does not match with num certs";
|
||
|
|
||
|
var payload = this.sPayload;
|
||
|
var errMsg = "";
|
||
|
for (var i = 0; i < aCert.length; i++) {
|
||
|
var cert = aCert[i];
|
||
|
var header = this.aHeader[i];
|
||
|
var sig = this.aSignature[i];
|
||
|
var sJWS = header + "." + payload + "." + sig;
|
||
|
|
||
|
var jws = new KJUR.jws.JWS();
|
||
|
try {
|
||
|
var result = jws.verifyJWSByPemX509Cert(sJWS, cert);
|
||
|
if (result != 1) {
|
||
|
errMsg += (i + 1) + "th signature unmatch. ";
|
||
|
}
|
||
|
} catch (ex) {
|
||
|
errMsg += (i + 1) + "th signature fail(" + ex + "). ";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (errMsg == "") {
|
||
|
return 1;
|
||
|
} else {
|
||
|
throw errMsg;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* read JWS-JS string.<br/>
|
||
|
* @name raedJWSJS
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
* @param {String} string of JWS-JS to load.
|
||
|
* @throw if sJWSJS is malformed or not JSON string.
|
||
|
*/
|
||
|
this.readJWSJS = function(sJWSJS) {
|
||
|
var jws = new KJUR.jws.JWS();
|
||
|
var oJWSJS = jws.readSafeJSONString(sJWSJS);
|
||
|
if (oJWSJS == null) throw "argument is not JSON string: " + sJWSJS;
|
||
|
|
||
|
this.aHeader = oJWSJS.headers;
|
||
|
this.sPayload = oJWSJS.payload;
|
||
|
this.aSignature = oJWSJS.signatures;
|
||
|
};
|
||
|
|
||
|
// == utility ===================================================================
|
||
|
/**
|
||
|
* get JSON object for this JWS-JS object.<br/>
|
||
|
* @name getJSON
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
*/
|
||
|
this.getJSON = function() {
|
||
|
return { "headers": this.aHeader,
|
||
|
"payload": this.sPayload,
|
||
|
"signatures": this.aSignature };
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* check if this JWS-JS object is empty.<br/>
|
||
|
* @name isEmpty
|
||
|
* @memberOf KJUR.jws.JWSJS
|
||
|
* @function
|
||
|
* @return 1 if there is no signatures in this object, otherwise 0.
|
||
|
*/
|
||
|
this.isEmpty = function() {
|
||
|
if (this.aHeader.length == 0) return 1;
|
||
|
return 0;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
/*! jws-2.0.3 (c) 2012 Kenji Urushima | kjur.github.com/jsjws/license
|
||
|
*/
|
||
|
/*
|
||
|
* jws.js - JSON Web Signature Class
|
||
|
*
|
||
|
* version: 2.0.3 (2013 Jul 30)
|
||
|
*
|
||
|
* Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
|
||
|
*
|
||
|
* This software is licensed under the terms of the MIT License.
|
||
|
* http://kjur.github.com/jsjws/license/
|
||
|
*
|
||
|
* The above copyright and license notice shall be
|
||
|
* included in all copies or substantial portions of the Software.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @fileOverview
|
||
|
* @name jws-2.0.js
|
||
|
* @author Kenji Urushima kenji.urushima@gmail.com
|
||
|
* @version 2.0.3 (2013-Jul-30)
|
||
|
* @since jsjws 1.0
|
||
|
* @license <a href="http://kjur.github.io/jsjws/license/">MIT License</a>
|
||
|
*/
|
||
|
|
||
|
if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
|
||
|
if (typeof KJUR.jws == "undefined" || !KJUR.jws) KJUR.jws = {};
|
||
|
|
||
|
/**
|
||
|
* JSON Web Signature(JWS) class.<br/>
|
||
|
* @class JSON Web Signature(JWS) class
|
||
|
* @property {Dictionary} parsedJWS This property is set after JWS signature verification. <br/>
|
||
|
* Following "parsedJWS_*" properties can be accessed as "parsedJWS.*" because of
|
||
|
* JsDoc restriction.
|
||
|
* @property {String} parsedJWS_headB64U string of Encrypted JWS Header
|
||
|
* @property {String} parsedJWS_payloadB64U string of Encrypted JWS Payload
|
||
|
* @property {String} parsedJWS_sigvalB64U string of Encrypted JWS signature value
|
||
|
* @property {String} parsedJWS_si string of Signature Input
|
||
|
* @property {String} parsedJWS_sigvalH hexadecimal string of JWS signature value
|
||
|
* @property {String} parsedJWS_sigvalBI BigInteger(defined in jsbn.js) object of JWS signature value
|
||
|
* @property {String} parsedJWS_headS string of decoded JWS Header
|
||
|
* @property {String} parsedJWS_headS string of decoded JWS Payload
|
||
|
* @author Kenji Urushima
|
||
|
* @version 1.1 (07 May 2012)
|
||
|
* @requires base64x.js, json-sans-eval.js and jsrsasign library
|
||
|
* @see <a href="http://kjur.github.com/jsjws/">'jwjws'(JWS JavaScript Library) home page http://kjur.github.com/jsjws/</a>
|
||
|
* @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a>
|
||
|
*/
|
||
|
KJUR.jws.JWS = function() {
|
||
|
|
||
|
// === utility =============================================================
|
||
|
/**
|
||
|
* check whether a String "s" is a safe JSON string or not.<br/>
|
||
|
* If a String "s" is a malformed JSON string or an other object type
|
||
|
* this returns 0, otherwise this returns 1.
|
||
|
* @name isSafeJSONString
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} s JSON string
|
||
|
* @return {Number} 1 or 0
|
||
|
*/
|
||
|
this.isSafeJSONString = function(s, h, p) {
|
||
|
var o = null;
|
||
|
try {
|
||
|
o = jsonParse(s);
|
||
|
if (typeof o != "object") return 0;
|
||
|
if (o.constructor === Array) return 0;
|
||
|
if (h) h[p] = o;
|
||
|
return 1;
|
||
|
} catch (ex) {
|
||
|
return 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* read a String "s" as JSON object if it is safe.<br/>
|
||
|
* If a String "s" is a malformed JSON string or not JSON string,
|
||
|
* this returns null, otherwise returns JSON object.
|
||
|
* @name readSafeJSONString
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} s JSON string
|
||
|
* @return {Object} JSON object or null
|
||
|
* @since 1.1.1
|
||
|
*/
|
||
|
this.readSafeJSONString = function(s) {
|
||
|
var o = null;
|
||
|
try {
|
||
|
o = jsonParse(s);
|
||
|
if (typeof o != "object") return null;
|
||
|
if (o.constructor === Array) return null;
|
||
|
return o;
|
||
|
} catch (ex) {
|
||
|
return null;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* get Encoed Signature Value from JWS string.<br/>
|
||
|
* @name getEncodedSignatureValueFromJWS
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sJWS JWS signature string to be verified
|
||
|
* @return {String} string of Encoded Signature Value
|
||
|
* @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
|
||
|
*/
|
||
|
this.getEncodedSignatureValueFromJWS = function(sJWS) {
|
||
|
if (sJWS.match(/^[^.]+\.[^.]+\.([^.]+)$/) == null) {
|
||
|
throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
|
||
|
}
|
||
|
return RegExp.$1;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* parse JWS string and set public property 'parsedJWS' dictionary.<br/>
|
||
|
* @name parseJWS
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sJWS JWS signature string to be parsed.
|
||
|
* @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
|
||
|
* @throws if JWS Header is a malformed JSON string.
|
||
|
* @since 1.1
|
||
|
*/
|
||
|
this.parseJWS = function(sJWS, sigValNotNeeded) {
|
||
|
if ((this.parsedJWS !== undefined) &&
|
||
|
(sigValNotNeeded || (this.parsedJWS.sigvalH !== undefined))) {
|
||
|
return;
|
||
|
}
|
||
|
if (sJWS.match(/^([^.]+)\.([^.]+)\.([^.]+)$/) == null) {
|
||
|
throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
|
||
|
}
|
||
|
var b6Head = RegExp.$1;
|
||
|
var b6Payload = RegExp.$2;
|
||
|
var b6SigVal = RegExp.$3;
|
||
|
var sSI = b6Head + "." + b6Payload;
|
||
|
this.parsedJWS = {};
|
||
|
this.parsedJWS.headB64U = b6Head;
|
||
|
this.parsedJWS.payloadB64U = b6Payload;
|
||
|
this.parsedJWS.sigvalB64U = b6SigVal;
|
||
|
this.parsedJWS.si = sSI;
|
||
|
|
||
|
if (!sigValNotNeeded) {
|
||
|
var hSigVal = b64utohex(b6SigVal);
|
||
|
var biSigVal = parseBigInt(hSigVal, 16);
|
||
|
this.parsedJWS.sigvalH = hSigVal;
|
||
|
this.parsedJWS.sigvalBI = biSigVal;
|
||
|
}
|
||
|
|
||
|
var sHead = b64utoutf8(b6Head);
|
||
|
var sPayload = b64utoutf8(b6Payload);
|
||
|
this.parsedJWS.headS = sHead;
|
||
|
this.parsedJWS.payloadS = sPayload;
|
||
|
|
||
|
if (! this.isSafeJSONString(sHead, this.parsedJWS, 'headP'))
|
||
|
throw "malformed JSON string for JWS Head: " + sHead;
|
||
|
};
|
||
|
|
||
|
// ==== JWS Validation =========================================================
|
||
|
function _getSignatureInputByString(sHead, sPayload) {
|
||
|
return utf8tob64u(sHead) + "." + utf8tob64u(sPayload);
|
||
|
};
|
||
|
|
||
|
function _getHashBySignatureInput(sSignatureInput, sHashAlg) {
|
||
|
var hashfunc = function(s) { return KJUR.crypto.Util.hashString(s, sHashAlg); };
|
||
|
if (hashfunc == null) throw "hash function not defined in jsrsasign: " + sHashAlg;
|
||
|
return hashfunc(sSignatureInput);
|
||
|
};
|
||
|
|
||
|
function _jws_verifySignature(sHead, sPayload, hSig, hN, hE) {
|
||
|
var sSignatureInput = _getSignatureInputByString(sHead, sPayload);
|
||
|
var biSig = parseBigInt(hSig, 16);
|
||
|
return _rsasign_verifySignatureWithArgs(sSignatureInput, biSig, hN, hE);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* verify JWS signature with naked RSA public key.<br/>
|
||
|
* This only supports "RS256" and "RS512" algorithm.
|
||
|
* @name verifyJWSByNE
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sJWS JWS signature string to be verified
|
||
|
* @param {String} hN hexadecimal string for modulus of RSA public key
|
||
|
* @param {String} hE hexadecimal string for public exponent of RSA public key
|
||
|
* @return {String} returns 1 when JWS signature is valid, otherwise returns 0
|
||
|
* @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
|
||
|
* @throws if JWS Header is a malformed JSON string.
|
||
|
*/
|
||
|
this.verifyJWSByNE = function(sJWS, hN, hE) {
|
||
|
this.parseJWS(sJWS);
|
||
|
return _rsasign_verifySignatureWithArgs(this.parsedJWS.si, this.parsedJWS.sigvalBI, hN, hE);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* verify JWS signature with RSA public key.<br/>
|
||
|
* This only supports "RS256", "RS512", "PS256" and "PS512" algorithms.
|
||
|
* @name verifyJWSByKey
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sJWS JWS signature string to be verified
|
||
|
* @param {RSAKey} key RSA public key
|
||
|
* @return {Boolean} returns true when JWS signature is valid, otherwise returns false
|
||
|
* @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
|
||
|
* @throws if JWS Header is a malformed JSON string.
|
||
|
*/
|
||
|
this.verifyJWSByKey = function(sJWS, key) {
|
||
|
this.parseJWS(sJWS);
|
||
|
var hashAlg = _jws_getHashAlgFromParsedHead(this.parsedJWS.headP);
|
||
|
var isPSS = this.parsedJWS.headP['alg'].substr(0, 2) == "PS";
|
||
|
|
||
|
if (key.hashAndVerify) {
|
||
|
return key.hashAndVerify(hashAlg,
|
||
|
new Buffer(this.parsedJWS.si, 'utf8').toString('base64'),
|
||
|
b64utob64(this.parsedJWS.sigvalB64U),
|
||
|
'base64',
|
||
|
isPSS);
|
||
|
} else if (isPSS) {
|
||
|
return key.verifyStringPSS(this.parsedJWS.si,
|
||
|
this.parsedJWS.sigvalH, hashAlg);
|
||
|
} else {
|
||
|
return key.verifyString(this.parsedJWS.si,
|
||
|
this.parsedJWS.sigvalH);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* verify JWS signature by PEM formatted X.509 certificate.<br/>
|
||
|
* This only supports "RS256" and "RS512" algorithm.
|
||
|
* @name verifyJWSByPemX509Cert
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sJWS JWS signature string to be verified
|
||
|
* @param {String} sPemX509Cert string of PEM formatted X.509 certificate
|
||
|
* @return {String} returns 1 when JWS signature is valid, otherwise returns 0
|
||
|
* @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
|
||
|
* @throws if JWS Header is a malformed JSON string.
|
||
|
* @since 1.1
|
||
|
*/
|
||
|
this.verifyJWSByPemX509Cert = function(sJWS, sPemX509Cert) {
|
||
|
this.parseJWS(sJWS);
|
||
|
var x509 = new X509();
|
||
|
x509.readCertPEM(sPemX509Cert);
|
||
|
return x509.subjectPublicKeyRSA.verifyString(this.parsedJWS.si, this.parsedJWS.sigvalH);
|
||
|
};
|
||
|
|
||
|
// ==== JWS Generation =========================================================
|
||
|
function _jws_getHashAlgFromParsedHead(head) {
|
||
|
var sigAlg = head["alg"];
|
||
|
var hashAlg = "";
|
||
|
|
||
|
if (sigAlg != "RS256" && sigAlg != "RS512" &&
|
||
|
sigAlg != "PS256" && sigAlg != "PS512")
|
||
|
throw "JWS signature algorithm not supported: " + sigAlg;
|
||
|
if (sigAlg.substr(2) == "256") hashAlg = "sha256";
|
||
|
if (sigAlg.substr(2) == "512") hashAlg = "sha512";
|
||
|
return hashAlg;
|
||
|
};
|
||
|
|
||
|
function _jws_getHashAlgFromHead(sHead) {
|
||
|
return _jws_getHashAlgFromParsedHead(jsonParse(sHead));
|
||
|
};
|
||
|
|
||
|
function _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD) {
|
||
|
var rsa = new RSAKey();
|
||
|
rsa.setPrivate(hN, hE, hD);
|
||
|
|
||
|
var hashAlg = _jws_getHashAlgFromHead(sHead);
|
||
|
var sigValue = rsa.signString(sSI, hashAlg);
|
||
|
return sigValue;
|
||
|
};
|
||
|
|
||
|
function _jws_generateSignatureValueBySI_Key(sHead, sPayload, sSI, key, head) {
|
||
|
var hashAlg = null;
|
||
|
if (typeof head == "undefined") {
|
||
|
hashAlg = _jws_getHashAlgFromHead(sHead);
|
||
|
} else {
|
||
|
hashAlg = _jws_getHashAlgFromParsedHead(head);
|
||
|
}
|
||
|
|
||
|
var isPSS = head['alg'].substr(0, 2) == "PS";
|
||
|
|
||
|
if (key.hashAndSign) {
|
||
|
return b64tob64u(key.hashAndSign(hashAlg, sSI, 'binary', 'base64', isPSS));
|
||
|
} else if (isPSS) {
|
||
|
return hextob64u(key.signStringPSS(sSI, hashAlg));
|
||
|
} else {
|
||
|
return hextob64u(key.signString(sSI, hashAlg));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function _jws_generateSignatureValueByNED(sHead, sPayload, hN, hE, hD) {
|
||
|
var sSI = _getSignatureInputByString(sHead, sPayload);
|
||
|
return _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* generate JWS signature by Header, Payload and a naked RSA private key.<br/>
|
||
|
* This only supports "RS256" and "RS512" algorithm.
|
||
|
* @name generateJWSByNED
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sHead string of JWS Header
|
||
|
* @param {String} sPayload string of JWS Payload
|
||
|
* @param {String} hN hexadecimal string for modulus of RSA public key
|
||
|
* @param {String} hE hexadecimal string for public exponent of RSA public key
|
||
|
* @param {String} hD hexadecimal string for private exponent of RSA private key
|
||
|
* @return {String} JWS signature string
|
||
|
* @throws if sHead is a malformed JSON string.
|
||
|
* @throws if supported signature algorithm was not specified in JSON Header.
|
||
|
*/
|
||
|
this.generateJWSByNED = function(sHead, sPayload, hN, hE, hD) {
|
||
|
if (! this.isSafeJSONString(sHead)) throw "JWS Head is not safe JSON string: " + sHead;
|
||
|
var sSI = _getSignatureInputByString(sHead, sPayload);
|
||
|
var hSigValue = _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD);
|
||
|
var b64SigValue = hextob64u(hSigValue);
|
||
|
|
||
|
this.parsedJWS = {};
|
||
|
this.parsedJWS.headB64U = sSI.split(".")[0];
|
||
|
this.parsedJWS.payloadB64U = sSI.split(".")[1];
|
||
|
this.parsedJWS.sigvalB64U = b64SigValue;
|
||
|
|
||
|
return sSI + "." + b64SigValue;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* generate JWS signature by Header, Payload and a RSA private key.<br/>
|
||
|
* This only supports "RS256", "RS512", "PS256" and "PS512" algorithms.
|
||
|
* @name generateJWSByKey
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sHead string of JWS Header
|
||
|
* @param {String} sPayload string of JWS Payload
|
||
|
* @param {RSAKey} RSA private key
|
||
|
* @return {String} JWS signature string
|
||
|
* @throws if sHead is a malformed JSON string.
|
||
|
* @throws if supported signature algorithm was not specified in JSON Header.
|
||
|
*/
|
||
|
this.generateJWSByKey = function(sHead, sPayload, key) {
|
||
|
var obj = {};
|
||
|
if (!this.isSafeJSONString(sHead, obj, 'headP'))
|
||
|
throw "JWS Head is not safe JSON string: " + sHead;
|
||
|
var sSI = _getSignatureInputByString(sHead, sPayload);
|
||
|
var b64SigValue = _jws_generateSignatureValueBySI_Key(sHead, sPayload, sSI, key, obj.headP);
|
||
|
|
||
|
this.parsedJWS = {};
|
||
|
this.parsedJWS.headB64U = sSI.split(".")[0];
|
||
|
this.parsedJWS.payloadB64U = sSI.split(".")[1];
|
||
|
this.parsedJWS.sigvalB64U = b64SigValue;
|
||
|
|
||
|
return sSI + "." + b64SigValue;
|
||
|
};
|
||
|
|
||
|
// === sign with PKCS#1 RSA private key =====================================================
|
||
|
function _jws_generateSignatureValueBySI_PemPrvKey(sHead, sPayload, sSI, sPemPrvKey) {
|
||
|
var rsa = new RSAKey();
|
||
|
rsa.readPrivateKeyFromPEMString(sPemPrvKey);
|
||
|
var hashAlg = _jws_getHashAlgFromHead(sHead);
|
||
|
var sigValue = rsa.signString(sSI, hashAlg);
|
||
|
return sigValue;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* generate JWS signature by Header, Payload and a PEM formatted PKCS#1 RSA private key.<br/>
|
||
|
* This only supports "RS256" and "RS512" algorithm.
|
||
|
* @name generateJWSByP1PrvKey
|
||
|
* @memberOf KJUR.jws.JWS
|
||
|
* @function
|
||
|
* @param {String} sHead string of JWS Header
|
||
|
* @param {String} sPayload string of JWS Payload
|
||
|
* @param {String} string for sPemPrvKey PEM formatted PKCS#1 RSA private key<br/>
|
||
|
* Heading and trailing space characters in PEM key will be ignored.
|
||
|
* @return {String} JWS signature string
|
||
|
* @throws if sHead is a malformed JSON string.
|
||
|
* @throws if supported signature algorithm was not specified in JSON Header.
|
||
|
* @since 1.1
|
||
|
*/
|
||
|
this.generateJWSByP1PrvKey = function(sHead, sPayload, sPemPrvKey) {
|
||
|
if (! this.isSafeJSONString(sHead)) throw "JWS Head is not safe JSON string: " + sHead;
|
||
|
var sSI = _getSignatureInputByString(sHead, sPayload);
|
||
|
var hSigValue = _jws_generateSignatureValueBySI_PemPrvKey(sHead, sPayload, sSI, sPemPrvKey);
|
||
|
var b64SigValue = hextob64u(hSigValue);
|
||
|
|
||
|
this.parsedJWS = {};
|
||
|
this.parsedJWS.headB64U = sSI.split(".")[0];
|
||
|
this.parsedJWS.payloadB64U = sSI.split(".")[1];
|
||
|
this.parsedJWS.sigvalB64U = b64SigValue;
|
||
|
|
||
|
return sSI + "." + b64SigValue;
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
/*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval
|
||
|
*/
|
||
|
// This source code is free for use in the public domain.
|
||
|
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||
|
|
||
|
// http://code.google.com/p/json-sans-eval/
|
||
|
|
||
|
/**
|
||
|
* Parses a string of well-formed JSON text.
|
||
|
*
|
||
|
* If the input is not well-formed, then behavior is undefined, but it is
|
||
|
* deterministic and is guaranteed not to modify any object other than its
|
||
|
* return value.
|
||
|
*
|
||
|
* This does not use `eval` so is less likely to have obscure security bugs than
|
||
|
* json2.js.
|
||
|
* It is optimized for speed, so is much faster than json_parse.js.
|
||
|
*
|
||
|
* This library should be used whenever security is a concern (when JSON may
|
||
|
* come from an untrusted source), speed is a concern, and erroring on malformed
|
||
|
* JSON is *not* a concern.
|
||
|
*
|
||
|
* Pros Cons
|
||
|
* +-----------------------+-----------------------+
|
||
|
* json_sans_eval.js | Fast, secure | Not validating |
|
||
|
* +-----------------------+-----------------------+
|
||
|
* json_parse.js | Validating, secure | Slow |
|
||
|
* +-----------------------+-----------------------+
|
||
|
* json2.js | Fast, some validation | Potentially insecure |
|
||
|
* +-----------------------+-----------------------+
|
||
|
*
|
||
|
* json2.js is very fast, but potentially insecure since it calls `eval` to
|
||
|
* parse JSON data, so an attacker might be able to supply strange JS that
|
||
|
* looks like JSON, but that executes arbitrary javascript.
|
||
|
* If you do have to use json2.js with untrusted data, make sure you keep
|
||
|
* your version of json2.js up to date so that you get patches as they're
|
||
|
* released.
|
||
|
*
|
||
|
* @param {string} json per RFC 4627
|
||
|
* @param {function (this:Object, string, *):*} opt_reviver optional function
|
||
|
* that reworks JSON objects post-parse per Chapter 15.12 of EcmaScript3.1.
|
||
|
* If supplied, the function is called with a string key, and a value.
|
||
|
* The value is the property of 'this'. The reviver should return
|
||
|
* the value to use in its place. So if dates were serialized as
|
||
|
* {@code { "type": "Date", "time": 1234 }}, then a reviver might look like
|
||
|
* {@code
|
||
|
* function (key, value) {
|
||
|
* if (value && typeof value === 'object' && 'Date' === value.type) {
|
||
|
* return new Date(value.time);
|
||
|
* } else {
|
||
|
* return value;
|
||
|
* }
|
||
|
* }}.
|
||
|
* If the reviver returns {@code undefined} then the property named by key
|
||
|
* will be deleted from its container.
|
||
|
* {@code this} is bound to the object containing the specified property.
|
||
|
* @return {Object|Array}
|
||
|
* @author Mike Samuel <mikesamuel@gmail.com>
|
||
|
*/
|
||
|
var jsonParse = (function () {
|
||
|
var number
|
||
|
= '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)';
|
||
|
var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]'
|
||
|
+ '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';
|
||
|
var string = '(?:\"' + oneChar + '*\")';
|
||
|
|
||
|
// Will match a value in a well-formed JSON file.
|
||
|
// If the input is not well-formed, may match strangely, but not in an unsafe
|
||
|
// way.
|
||
|
// Since this only matches value tokens, it does not match whitespace, colons,
|
||
|
// or commas.
|
||
|
var jsonToken = new RegExp(
|
||
|
'(?:false|true|null|[\\{\\}\\[\\]]'
|
||
|
+ '|' + number
|
||
|
+ '|' + string
|
||
|
+ ')', 'g');
|
||
|
|
||
|
// Matches escape sequences in a string literal
|
||
|
var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g');
|
||
|
|
||
|
// Decodes escape sequences in object literals
|
||
|
var escapes = {
|
||
|
'"': '"',
|
||
|
'/': '/',
|
||
|
'\\': '\\',
|
||
|
'b': '\b',
|
||
|
'f': '\f',
|
||
|
'n': '\n',
|
||
|
'r': '\r',
|
||
|
't': '\t'
|
||
|
};
|
||
|
function unescapeOne(_, ch, hex) {
|
||
|
return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16));
|
||
|
}
|
||
|
|
||
|
// A non-falsy value that coerces to the empty string when used as a key.
|
||
|
var EMPTY_STRING = new String('');
|
||
|
var SLASH = '\\';
|
||
|
|
||
|
// Constructor to use based on an open token.
|
||
|
var firstTokenCtors = { '{': Object, '[': Array };
|
||
|
|
||
|
var hop = Object.hasOwnProperty;
|
||
|
|
||
|
return function (json, opt_reviver) {
|
||
|
// Split into tokens
|
||
|
var toks = json.match(jsonToken);
|
||
|
// Construct the object to return
|
||
|
var result;
|
||
|
var tok = toks[0];
|
||
|
var topLevelPrimitive = false;
|
||
|
if ('{' === tok) {
|
||
|
result = {};
|
||
|
} else if ('[' === tok) {
|
||
|
result = [];
|
||
|
} else {
|
||
|
// The RFC only allows arrays or objects at the top level, but the JSON.parse
|
||
|
// defined by the EcmaScript 5 draft does allow strings, booleans, numbers, and null
|
||
|
// at the top level.
|
||
|
result = [];
|
||
|
topLevelPrimitive = true;
|
||
|
}
|
||
|
|
||
|
// If undefined, the key in an object key/value record to use for the next
|
||
|
// value parsed.
|
||
|
var key;
|
||
|
// Loop over remaining tokens maintaining a stack of uncompleted objects and
|
||
|
// arrays.
|
||
|
var stack = [result];
|
||
|
for (var i = 1 - topLevelPrimitive, n = toks.length; i < n; ++i) {
|
||
|
tok = toks[i];
|
||
|
|
||
|
var cont;
|
||
|
switch (tok.charCodeAt(0)) {
|
||
|
default: // sign or digit
|
||
|
cont = stack[0];
|
||
|
cont[key || cont.length] = +(tok);
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x22: // '"'
|
||
|
tok = tok.substring(1, tok.length - 1);
|
||
|
if (tok.indexOf(SLASH) !== -1) {
|
||
|
tok = tok.replace(escapeSequence, unescapeOne);
|
||
|
}
|
||
|
cont = stack[0];
|
||
|
if (!key) {
|
||
|
if (cont instanceof Array) {
|
||
|
key = cont.length;
|
||
|
} else {
|
||
|
key = tok || EMPTY_STRING; // Use as key for next value seen.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
cont[key] = tok;
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x5b: // '['
|
||
|
cont = stack[0];
|
||
|
stack.unshift(cont[key || cont.length] = []);
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x5d: // ']'
|
||
|
stack.shift();
|
||
|
break;
|
||
|
case 0x66: // 'f'
|
||
|
cont = stack[0];
|
||
|
cont[key || cont.length] = false;
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x6e: // 'n'
|
||
|
cont = stack[0];
|
||
|
cont[key || cont.length] = null;
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x74: // 't'
|
||
|
cont = stack[0];
|
||
|
cont[key || cont.length] = true;
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x7b: // '{'
|
||
|
cont = stack[0];
|
||
|
stack.unshift(cont[key || cont.length] = {});
|
||
|
key = void 0;
|
||
|
break;
|
||
|
case 0x7d: // '}'
|
||
|
stack.shift();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// Fail if we've got an uncompleted object.
|
||
|
if (topLevelPrimitive) {
|
||
|
if (stack.length !== 1) { throw new Error(); }
|
||
|
result = result[0];
|
||
|
} else {
|
||
|
if (stack.length) { throw new Error(); }
|
||
|
}
|
||
|
|
||
|
if (opt_reviver) {
|
||
|
// Based on walk as implemented in http://www.json.org/json2.js
|
||
|
var walk = function (holder, key) {
|
||
|
var value = holder[key];
|
||
|
if (value && typeof value === 'object') {
|
||
|
var toDelete = null;
|
||
|
for (var k in value) {
|
||
|
if (hop.call(value, k) && value !== holder) {
|
||
|
// Recurse to properties first. This has the effect of causing
|
||
|
// the reviver to be called on the object graph depth-first.
|
||
|
|
||
|
// Since 'this' is bound to the holder of the property, the
|
||
|
// reviver can access sibling properties of k including ones
|
||
|
// that have not yet been revived.
|
||
|
|
||
|
// The value returned by the reviver is used in place of the
|
||
|
// current value of property k.
|
||
|
// If it returns undefined then the property is deleted.
|
||
|
var v = walk(value, k);
|
||
|
if (v !== void 0) {
|
||
|
value[k] = v;
|
||
|
} else {
|
||
|
// Deleting properties inside the loop has vaguely defined
|
||
|
// semantics in ES3 and ES3.1.
|
||
|
if (!toDelete) { toDelete = []; }
|
||
|
toDelete.push(k);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (toDelete) {
|
||
|
for (var i = toDelete.length; --i >= 0;) {
|
||
|
delete value[toDelete[i]];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return opt_reviver.call(holder, key, value);
|
||
|
};
|
||
|
result = walk({ '': result }, '');
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
})();
|