1 /*! jws-3.3.4 (c) 2013-2016 Kenji Urushima | kjur.github.com/jsrsasign/license
  2  */
  3 /*
  4  * jws.js - JSON Web Signature(JWS) and JSON Web Token(JWT) Class
  5  *
  6  * version: 3.3.4 (2016 May 17)
  7  *
  8  * Copyright (c) 2010-2016 Kenji Urushima (kenji.urushima@gmail.com)
  9  *
 10  * This software is licensed under the terms of the MIT License.
 11  * http://kjur.github.com/jsrsasign/license/
 12  *
 13  * The above copyright and license notice shall be 
 14  * included in all copies or substantial portions of the Software.
 15  */
 16 
 17 /**
 18  * @fileOverview
 19  * @name jws-3.3.js
 20  * @author Kenji Urushima kenji.urushima@gmail.com
 21  * @version 3.3.4 (2016-May-17)
 22  * @since jsjws 1.0, jsrsasign 4.8.0
 23  * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
 24  */
 25 
 26 if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
 27 
 28 /**
 29  * kjur's JSON Web Signature/Token(JWS/JWT) library name space
 30  * <p>
 31  * This namespace privides following JWS/JWS related classes.
 32  * <ul>
 33  * <li>{@link KJUR.jws.JWS} - JSON Web Signature/Token(JWS/JWT) class</li>
 34  * <li>{@link KJUR.jws.JWSJS} - JWS JSON Serialization(JWSJS) class</li>
 35  * <li>{@link KJUR.jws.IntDate} - UNIX origin time utility class</li>
 36  * </ul>
 37  * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.
 38  * </p>
 39  * @name KJUR.jws
 40  * @namespace
 41  */
 42 if (typeof KJUR.jws == "undefined" || !KJUR.jws) KJUR.jws = {};
 43 
 44 /**
 45  * JSON Web Signature(JWS) class.<br/>
 46  * @name KJUR.jws.JWS
 47  * @class JSON Web Signature(JWS) class
 48  * @see <a href="http://kjur.github.com/jsjws/">'jwjws'(JWS JavaScript Library) home page http://kjur.github.com/jsjws/</a>
 49  * @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a>
 50  * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-14">IETF I-D JSON Web Algorithms (JWA)</a>
 51  * @since jsjws 1.0
 52  * @description
 53  * This class provides JSON Web Signature(JWS)/JSON Web Token(JWT) signing and validation.
 54  * <h4>Supported Algorithms</h4>
 55  * Here is supported algorithm names for {@link KJUR.jws.JWS.sign} and {@link KJUR.jws.JWS.verify}
 56  * methods.
 57  * <table>
 58  * <tr><th>alg value</th><th>spec requirement</th><th>jsjws support</th></tr>
 59  * <tr><td>HS256</td><td>REQUIRED</td><td>SUPPORTED</td></tr>
 60  * <tr><td>HS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 61  * <tr><td>HS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 62  * <tr><td>RS256</td><td>RECOMMENDED</td><td>SUPPORTED</td></tr>
 63  * <tr><td>RS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 64  * <tr><td>RS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 65  * <tr><td>ES256</td><td>RECOMMENDED+</td><td>SUPPORTED</td></tr>
 66  * <tr><td>ES384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 67  * <tr><td>ES512</td><td>OPTIONAL</td><td>-</td></tr>
 68  * <tr><td>PS256</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 69  * <tr><td>PS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 70  * <tr><td>PS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 71  * <tr><td>none</td><td>REQUIRED</td><td>SUPPORTED(signature generation only)</td></tr>
 72  * </table>
 73  * <dl>
 74  * <dt><b>NOTE1</b>
 75  * <dd>HS384 is supported since jsjws 3.0.2 with jsrsasign 4.1.4.
 76  * <dt><b>NOTE2</b>
 77  * <dd>Some deprecated methods have been removed since jws 3.3 of jsrsasign 4.10.0.
 78  * Removed methods are following:
 79  * <ul>
 80  * <li>JWS.verifyJWSByNE</li>
 81  * <li>JWS.verifyJWSByKey</li>
 82  * <li>JWS.generateJWSByNED</li>
 83  * <li>JWS.generateJWSByKey</li>
 84  * <li>JWS.generateJWSByP1PrvKey</li>
 85  * </ul>
 86  * </dl>
 87  * <b>EXAMPLE</b><br/>
 88  * @example
 89  * // JWS signing 
 90  * sJWS = KJUR.jws.JWS.sign(null, '{"alg":"HS256", "cty":"JWT"}', '{"age": 21}', "password");
 91  * // JWS validation
 92  * isValid = KJUR.jws.JWS.verify('eyJjdHkiOiJKV1QiLCJhbGc...', "password");
 93  * // JWT validation
 94  * isValid = KJUR.jws.JWS.verifyJWT('eyJh...', "password", {
 95  *   alg: ['HS256', 'HS384'],
 96  *   iss: ['http://foo.com']
 97  * });
 98  */
 99 KJUR.jws.JWS = function() {
100     var ns1 = KJUR.jws.JWS;
101 
102     // === utility =============================================================
103 
104     /**
105      * parse JWS string and set public property 'parsedJWS' dictionary.<br/>
106      * @name parseJWS
107      * @memberOf KJUR.jws.JWS
108      * @function
109      * @param {String} sJWS JWS signature string to be parsed.
110      * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
111      * @throws if JWS Header is a malformed JSON string.
112      * @since jws 1.1
113      */
114     this.parseJWS = function(sJWS, sigValNotNeeded) {
115 	if ((this.parsedJWS !== undefined) &&
116 	    (sigValNotNeeded || (this.parsedJWS.sigvalH !== undefined))) {
117 	    return;
118 	}
119 	if (sJWS.match(/^([^.]+)\.([^.]+)\.([^.]+)$/) == null) {
120 	    throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
121 	}
122 	var b6Head = RegExp.$1;
123 	var b6Payload = RegExp.$2;
124 	var b6SigVal = RegExp.$3;
125 	var sSI = b6Head + "." + b6Payload;
126 	this.parsedJWS = {};
127 	this.parsedJWS.headB64U = b6Head;
128 	this.parsedJWS.payloadB64U = b6Payload;
129 	this.parsedJWS.sigvalB64U = b6SigVal;
130 	this.parsedJWS.si = sSI;
131 
132 	if (!sigValNotNeeded) {
133 	    var hSigVal = b64utohex(b6SigVal);
134 	    var biSigVal = parseBigInt(hSigVal, 16);
135 	    this.parsedJWS.sigvalH = hSigVal;
136 	    this.parsedJWS.sigvalBI = biSigVal;
137 	}
138 
139 	var sHead = b64utoutf8(b6Head);
140 	var sPayload = b64utoutf8(b6Payload);
141 	this.parsedJWS.headS = sHead;
142 	this.parsedJWS.payloadS = sPayload;
143 
144 	if (! ns1.isSafeJSONString(sHead, this.parsedJWS, 'headP'))
145 	    throw "malformed JSON string for JWS Head: " + sHead;
146     };
147 };
148 
149 // === major static method ========================================================
150 
151 /**
152  * generate JWS signature by specified key<br/>
153  * @name sign
154  * @memberOf KJUR.jws.JWS
155  * @function
156  * @static
157  * @param {String} alg JWS algorithm name to sign and force set to sHead or null 
158  * @param {String} spHead string or object of JWS Header
159  * @param {String} spPayload string or object of JWS Payload
160  * @param {String} key string of private key or mac key object to sign
161  * @param {String} pass (OPTION)passcode to use encrypted asymmetric private key 
162  * @return {String} JWS signature string
163  * @since jws 3.0.0
164  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Signature.html">jsrsasign KJUR.crypto.Signature method</a>
165  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Mac.html">jsrsasign KJUR.crypto.Mac method</a>
166  * @description
167  * This method supports following algorithms.
168  * <table>
169  * <tr><th>alg value</th><th>spec requirement</th><th>jsjws support</th></tr>
170  * <tr><td>HS256</td><td>REQUIRED</td><td>SUPPORTED</td></tr>
171  * <tr><td>HS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
172  * <tr><td>HS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
173  * <tr><td>RS256</td><td>RECOMMENDED</td><td>SUPPORTED</td></tr>
174  * <tr><td>RS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
175  * <tr><td>RS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
176  * <tr><td>ES256</td><td>RECOMMENDED+</td><td>SUPPORTED</td></tr>
177  * <tr><td>ES384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
178  * <tr><td>ES512</td><td>OPTIONAL</td><td>-</td></tr>
179  * <tr><td>PS256</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
180  * <tr><td>PS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
181  * <tr><td>PS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
182  * <tr><td>none</td><td>REQUIRED</td><td>SUPPORTED(signature generation only)</td></tr>
183  * </table>
184  * <dl>
185  * <dt>NOTE1:
186  * <dd>salt length of RSAPSS signature is the same as the hash algorithm length
187  * because of <a href="http://www.ietf.org/mail-archive/web/jose/current/msg02901.html">IETF JOSE ML discussion</a>.
188  * <dt>NOTE2:
189  * <dd>To support HS384, patched version of CryptoJS is used.
190  * <a href="https://code.google.com/p/crypto-js/issues/detail?id=84">See here for detail</a>.
191  * <dt>NOTE3:
192  * From jsrsasign 4.10.0 jws 3.3.0, Way to provide password
193  * for HS* algorithm is changed. The 'key' attribute value is
194  * passed to {@link KJUR.crypto.Mac.setPassword} so please see
195  * {@link KJUR.crypto.Mac.setPassword} for detail.
196  * As for backword compatibility, if key is a string, has even length and
197  * 0..9, A-F or a-f characters, key string is treated as a hexadecimal
198  * otherwise it is treated as a raw string.
199  * <dd>
200  * </dl>
201  * <b>EXAMPLE</b><br/>
202  * @example
203  * // sign HS256 signature with password "aaa" implicitly handled as string
204  * sJWS = KJUR.jws.JWS.sign(null, {alg: "HS256", cty: "JWT"}, {age: 21}, "aaa");
205  * // sign HS256 signature with password "6161" implicitly handled as hex
206  * sJWS = KJUR.jws.JWS.sign(null, {alg: "HS256", cty: "JWT"}, {age: 21}, "6161");
207  * // sign HS256 signature with base64 password
208  * sJWS = KJUR.jws.JWS.sign(null, {alg: "HS256"}, {age: 21}, {b64: "Mi/8..a="});
209  * // sign RS256 signature with PKCS#8 PEM RSA private key
210  * sJWS = KJUR.jws.JWS.sign(null, {alg: "RS256"}, {age: 21}, "-----BEGIN PRIVATE KEY...");
211  * // sign RS256 signature with PKCS#8 PEM ECC private key with passcode
212  * sJWS = KJUR.jws.JWS.sign(null, {alg: "ES256"}, {age: 21}, 
213  *                          "-----BEGIN PRIVATE KEY...", "keypass");
214  * // header and payload can be passed by both string and object
215  * sJWS = KJUR.jws.JWS.sign(null, '{alg:"HS256",cty:"JWT"}', '{age:21}', "aaa");
216  */
217 KJUR.jws.JWS.sign = function(alg, spHeader, spPayload, key, pass) {
218     var ns1 = KJUR.jws.JWS;
219     var sHeader, pHeader, sPayload;
220 
221     // 1. check signatureInput(Header, Payload) is string or object
222     if (typeof spHeader != 'string' && typeof spHeader != 'object')
223 	throw "spHeader must be JSON string or object: " + spHeader;
224 
225     if (typeof spHeader == 'object') {
226 	pHeader = spHeader;
227 	sHeader = JSON.stringify(pHeader);
228     }
229 
230     if (typeof spHeader == 'string') {
231 	sHeader = spHeader;
232 	if (! ns1.isSafeJSONString(sHeader))
233 	    throw "JWS Head is not safe JSON string: " + sHeader;
234 	pHeader = ns1.readSafeJSONString(sHeader);
235 
236     }
237 
238     sPayload = spPayload;
239     if (typeof spPayload == 'object') sPayload = JSON.stringify(spPayload);
240 
241     // 2. use alg if defined in sHeader
242     if ((alg == '' || alg == null) &&
243 	pHeader['alg'] !== undefined) {
244 	alg = pHeader['alg'];
245     }
246 
247     // 3. update sHeader to add alg if alg undefined
248     if ((alg != '' && alg != null) &&
249 	pHeader['alg'] === undefined) {
250 	pHeader['alg'] = alg;
251 	sHeader = JSON.stringify(pHeader);
252     }
253 
254     // 4. check explicit algorithm doesn't match with JWS header.
255     if (alg !== pHeader.alg)
256 	throw "alg and sHeader.alg doesn't match: " + alg + "!=" + pHeader.alg;
257 
258     // 5. set signature algorithm like SHA1withRSA
259     var sigAlg = null;
260     if (ns1.jwsalg2sigalg[alg] === undefined) {
261 	throw "unsupported alg name: " + alg;
262     } else {
263 	sigAlg = ns1.jwsalg2sigalg[alg];
264     }
265     
266     var uHeader = utf8tob64u(sHeader);
267     var uPayload = utf8tob64u(sPayload);
268     var uSignatureInput = uHeader + "." + uPayload
269     // 6. sign
270     var hSig = "";
271     if (sigAlg.substr(0, 4) == "Hmac") {
272 	if (key === undefined)
273 	    throw "mac key shall be specified for HS* alg";
274 	//alert("sigAlg=" + sigAlg);
275 	var mac = new KJUR.crypto.Mac({'alg': sigAlg, 'prov': 'cryptojs', 'pass': key});
276 	mac.updateString(uSignatureInput);
277 	hSig = mac.doFinal();
278     } else if (sigAlg.indexOf("withECDSA") != -1) {
279 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
280 	sig.init(key, pass);
281 	sig.updateString(uSignatureInput);
282 	hASN1Sig = sig.sign();
283 	hSig = KJUR.crypto.ECDSA.asn1SigToConcatSig(hASN1Sig);
284     } else if (sigAlg != "none") {
285 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
286 	sig.init(key, pass);
287 	sig.updateString(uSignatureInput);
288 	hSig = sig.sign();
289     }
290 
291     var uSig = hextob64u(hSig);
292     return uSignatureInput + "." + uSig;
293 };
294 
295 /**
296  * verify JWS signature by specified key or certificate<br/>
297  * @name verify
298  * @memberOf KJUR.jws.JWS
299  * @function
300  * @static
301  * @param {String} sJWS string of JWS signature to verify
302  * @param {Object} key string of public key, certificate or key object to verify
303  * @param {String} acceptAlgs array of algorithm name strings (OPTION)
304  * @return {Boolean} true if the signature is valid otherwise false
305  * @since jws 3.0.0
306  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Signature.html">jsrsasign KJUR.crypto.Signature method</a>
307  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Mac.html">jsrsasign KJUR.crypto.Mac method</a>
308  * @description
309  * <p>
310  * This method verifies a JSON Web Signature Compact Serialization string by the validation 
311  * algorithm as described in 
312  * <a href="http://self-issued.info/docs/draft-jones-json-web-signature-04.html#anchor5">
313  * the section 5 of Internet Draft draft-jones-json-web-signature-04.</a>
314  * </p>
315  * <p>
316  * Since 3.2.0 strict key checking has been provided against a JWS algorithm
317  * in a JWS header.
318  * <ul>
319  * <li>In case 'alg' is 'HS*' in the JWS header,
320  * 'key' shall be hexadecimal string for Hmac{256,384,512} shared secret key.
321  * Otherwise it raise an error.</li>
322  * <li>In case 'alg' is 'RS*' or 'PS*' in the JWS header,
323  * 'key' shall be a RSAKey object or a PEM string of
324  * X.509 RSA public key certificate or PKCS#8 RSA public key.
325  * Otherwise it raise an error.</li>
326  * <li>In case 'alg' is 'ES*' in the JWS header,
327  * 'key' shall be a KJUR.crypto.ECDSA object or a PEM string of
328  * X.509 ECC public key certificate or PKCS#8 ECC public key.
329  * Otherwise it raise an error.</li>
330  * <li>In case 'alg' is 'none' in the JWS header,
331  * validation not supported after jsjws 3.1.0.</li>
332  * </ul>
333  * </p>
334  * <p>
335  * NOTE1: The argument 'acceptAlgs' is supported since 3.2.0.
336  * Strongly recommended to provide acceptAlgs to mitigate
337  * signature replacement attacks.<br/>
338  * </p>
339  * <p>
340  * NOTE2: From jsrsasign 4.9.0 jws 3.2.5, Way to provide password
341  * for HS* algorithm is changed. The 'key' attribute value is
342  * passed to {@link KJUR.crypto.Mac.setPassword} so please see
343  * {@link KJUR.crypto.Mac.setPassword} for detail.
344  * As for backword compatibility, if key is a string, has even length and
345  * 0..9, A-F or a-f characters, key string is treated as a hexadecimal
346  * otherwise it is treated as a raw string.
347  * </p>
348  * @example
349  * // 1) verify a RS256 JWS signature by a certificate string.
350  * isValid = KJUR.jws.JWS.verify('eyJh...', '-----BEGIN...', ['RS256']);
351  * 
352  * // 2) verify a HS256 JWS signature by a certificate string.
353  * isValid = KJUR.jws.JWS.verify('eyJh...', {hex: '6f62ad...'}, ['HS256']);
354  * isValid = KJUR.jws.JWS.verify('eyJh...', {b64: 'Mi/ab8...a=='}, ['HS256']);
355  * isValid = KJUR.jws.JWS.verify('eyJh...', {utf8: 'Secret秘密'}, ['HS256']);
356  * isValid = KJUR.jws.JWS.verify('eyJh...', '6f62ad', ['HS256']); // implicit hex
357  * isValid = KJUR.jws.JWS.verify('eyJh...', '6f62ada', ['HS256']); // implicit raw string
358  *
359  * // 3) verify a ES256 JWS signature by a KJUR.crypto.ECDSA key object.
360  * var pubkey = KEYUTIL.getKey('-----BEGIN CERT...');
361  * var isValid = KJUR.jws.JWS.verify('eyJh...', pubkey);
362  */
363 KJUR.jws.JWS.verify = function(sJWS, key, acceptAlgs) {
364     var jws = KJUR.jws.JWS;
365     var a = sJWS.split(".");
366     var uHeader = a[0];
367     var uPayload = a[1];
368     var uSignatureInput = uHeader + "." + uPayload;
369     var hSig = b64utohex(a[2]);
370 
371     // 1. parse JWS header
372     var pHeader = jws.readSafeJSONString(b64utoutf8(a[0]));
373     var alg = null;
374     var algType = null; // HS|RS|PS|ES|no
375     if (pHeader.alg === undefined) {
376 	throw "algorithm not specified in header";
377     } else {
378 	alg = pHeader.alg;
379 	algType = alg.substr(0, 2);
380     }
381 
382     // 2. check whether alg is acceptable algorithms
383     if (acceptAlgs != null &&
384         Object.prototype.toString.call(acceptAlgs) === '[object Array]' &&
385         acceptAlgs.length > 0) {
386 	var acceptAlgStr = ":" + acceptAlgs.join(":") + ":";
387 	if (acceptAlgStr.indexOf(":" + alg + ":") == -1) {
388 	    throw "algorithm '" + alg + "' not accepted in the list";
389 	}
390     }
391 
392     // 3. check whether key is a proper key for alg.
393     if (alg != "none" && key === null) {
394 	throw "key shall be specified to verify.";
395     }
396 
397     // 3.1. There is no key check for HS* because Mac will check it.
398     //      since jsrsasign 5.0.0.
399 
400     // 3.2. convert key object if key is a public key or cert PEM string
401     if (typeof key == "string" &&
402 	key.indexOf("-----BEGIN ") != -1) {
403 	key = KEYUTIL.getKey(key);
404     }
405 
406     // 3.3. check whether key is RSAKey obj if alg is RS* or PS*.
407     if (algType == "RS" || algType == "PS") {
408 	if (!(key instanceof RSAKey)) {
409 	    throw "key shall be a RSAKey obj for RS* and PS* algs";
410 	}
411     }
412 
413     // 3.4. check whether key is ECDSA obj if alg is ES*.
414     if (algType == "ES") {
415 	if (!(key instanceof KJUR.crypto.ECDSA)) {
416 	    throw "key shall be a ECDSA obj for ES* algs";
417 	}
418     }
419 
420     // 3.5. check when alg is 'none'
421     if (alg == "none") {
422     }
423 
424     // 4. check whether alg is supported alg in jsjws.
425     var sigAlg = null;
426     if (jws.jwsalg2sigalg[pHeader.alg] === undefined) {
427 	throw "unsupported alg name: " + alg;
428     } else {
429 	sigAlg = jws.jwsalg2sigalg[alg];
430     }
431 
432     // 5. verify
433     if (sigAlg == "none") {
434         throw "not supported";
435     } else if (sigAlg.substr(0, 4) == "Hmac") {
436 	var hSig2 = null;
437 	if (key === undefined)
438 	    throw "hexadecimal key shall be specified for HMAC";
439 	//try {
440 	    var mac = new KJUR.crypto.Mac({'alg': sigAlg, 'pass': key});
441 	    mac.updateString(uSignatureInput);
442 	    hSig2 = mac.doFinal();
443 	//} catch(ex) {};
444 	return hSig == hSig2;
445     } else if (sigAlg.indexOf("withECDSA") != -1) {
446 	var hASN1Sig = null;
447         try {
448 	    hASN1Sig = KJUR.crypto.ECDSA.concatSigToASN1Sig(hSig);
449 	} catch (ex) {
450 	    return false;
451 	}
452 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
453 	sig.init(key)
454 	sig.updateString(uSignatureInput);
455 	return sig.verify(hASN1Sig);
456     } else {
457 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
458 	sig.init(key)
459 	sig.updateString(uSignatureInput);
460 	return sig.verify(hSig);
461     }
462 };
463 
464 /**
465  * parse header and payload of JWS signature<br/>
466  * @name parse
467  * @memberOf KJUR.jws.JWS
468  * @function
469  * @static
470  * @param {String} sJWS string of JWS signature to parse
471  * @return {Array} associative array of parsed header and payload. See below.
472  * @throws if sJWS is malformed JWS signature
473  * @since jws 3.3.3
474  * @description
475  * This method parses JWS signature string. 
476  * Resulted associative array has following properties:
477  * <ul>
478  * <li>headerObj - JSON object of header</li>
479  * <li>payloadObj - JSON object of payload if payload is JSON string otherwise undefined</li>
480  * <li>headerPP - pretty printed JSON header by stringify</li>
481  * <li>payloadPP - pretty printed JSON payload by stringify if payload is JSON otherwise Base64URL decoded raw string of payload</li>
482  * <li>sigHex - hexadecimal string of signature</li>
483  * </ul>
484  * @example
485  * KJUR.jws.JWS.parse(sJWS) ->
486  * { 
487  *   headerObj: {"alg": "RS256", "typ": "JWS"},
488  *   payloadObj: {"product": "orange", "quantity": 100},
489  *   headerPP: 
490  *   '{
491  *     "alg": "RS256",
492  *     "typ": "JWS"
493  *   }',
494  *   payloadPP: 
495  *   '{
496  *     "product": "orange",
497  *     "quantity": 100
498  *   }',
499  *   sigHex: "91f3cd..." 
500  * }
501  */
502 KJUR.jws.JWS.parse = function(sJWS) {
503     var a = sJWS.split(".");
504     var result = {};
505     var uHeader, uPayload, uSig;
506     if (a.length != 2 && a.length != 3)
507 	throw "malformed sJWS: wrong number of '.' splitted elements";
508 
509     uHeader = a[0];
510     uPayload = a[1];
511     if (a.length == 3) uSig = a[2]; 
512 
513     result.headerObj = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(uHeader));
514     result.payloadObj = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(uPayload));
515 
516     result.headerPP = JSON.stringify(result.headerObj, null, "  ");
517     if (result.payloadObj == null) {
518 	result.payloadPP = b64utoutf8(uPayload);
519     } else {
520 	result.payloadPP = JSON.stringify(result.payloadObj, null, "  ");
521     }
522 
523     if (uSig !== undefined) {
524 	result.sigHex = b64utohex(uSig);
525     }
526 
527     return result;
528 };
529 
530 /**
531  * @name verifyJWT
532  * @memberOf KJUR.jws.JWS
533  * @function
534  * @static
535  * @param {String} sJWT string of JSON Web Token(JWT) to verify
536  * @param {Object} key string of public key, certificate or key object to verify
537  * @param {Array} acceptField associative array of acceptable fields (OPTION)
538  * @return {Boolean} true if the JWT token is valid otherwise false
539  * @since jws 3.2.3 jsrsasign 4.8.0
540  *
541  * @description
542  * This method verifies a
543  * <a href="https://tools.ietf.org/html/rfc7519">RFC 7519</a> 
544  * JSON Web Token(JWT).
545  * It will verify following:
546  * <ul>
547  * <li>Header.alg
548  * <ul>
549  * <li>alg is specified in JWT header.</li>
550  * <li>alg is included in acceptField.alg array. (MANDATORY)</li>
551  * <li>alg is proper for key.</li>
552  * </ul>
553  * </li>
554  * <li>Payload.iss (issuer) - Payload.iss is included in acceptField.iss array if specified. (OPTION)</li>
555  * <li>Payload.sub (subject) - Payload.sub is included in acceptField.sub array if specified. (OPTION)</li>
556  * <li>Payload.aud (audience) - Payload.aud is included in acceptField.aud array or 
557  *     the same as value if specified. (OPTION)</li>
558  * <li>Time validity
559  * <ul>
560  * <li>
561  * If acceptField.verifyAt as number of UNIX origin time is specifed for validation time, 
562  * this method will verify at the time for it, otherwise current time will be used to verify.
563  * </li>
564  * <li>
565  * Clock of JWT generator or verifier can be fast or slow. If these clocks are
566  * very different, JWT validation may fail. To avoid such case, 'jsrsasign' supports
567  * 'acceptField.gracePeriod' parameter which specifies acceptable time difference
568  * of those clocks in seconds. So if you want to accept slow or fast in 2 hours,
569  * you can specify <code>acceptField.gracePeriod = 2 * 60 * 60;</code>.
570  * "gracePeriod" is zero by default.
571  * "gracePeriod" is supported since jsrsasign 5.0.12.
572  * </li>
573  * <li>Payload.exp (expire) - Validation time is smaller than Payload.exp + gracePeriod.</li>
574  * <li>Payload.nbf (not before) - Validation time is greater than Payload.nbf - gracePeriod.</li>
575  * <li>Payload.iat (issued at) - Validation time is greater than Payload.iat - gracePeriod.</li>
576  * </ul>
577  * </li>
578  * <li>Payload.jti (JWT id) - Payload.jti is included in acceptField.jti if specified. (OPTION)</li>
579  * <li>JWS signature of JWS is valid for specified key.</li>
580  * </ul>
581  *
582  * @example
583  * // simple validation for HS256
584  * isValid = KJUR.jws.JWS.verifyJWT("eyJhbG...", "616161", {alg: ["HS256"]}),
585  *
586  * // full validation for RS or PS
587  * pubkey = KEYUTIL.getKey('-----BEGIN CERT...');
588  * isValid = KJUR.jws.JWS.verifyJWT('eyJh...', pubkey, {
589  *   alg: ['RS256', 'RS512', 'PS256', 'PS512'],
590  *   iss: ['http://foo.com'],
591  *   sub: ['mailto:john@foo.com', 'mailto:alice@foo.com'],
592  *   verifyAt: KJUR.jws.IntDate.get('20150520235959Z'),
593  *   aud: ['http://foo.com'], // aud: 'http://foo.com' is fine too.
594  *   jti: 'id123456',
595  *   gracePeriod: 1 * 60 * 60 // accept 1 hour slow or fast
596  * });
597  */
598 KJUR.jws.JWS.verifyJWT = function(sJWT, key, acceptField) {
599     var ns1 = KJUR.jws.JWS;
600 
601     // 1. parse JWT
602     var a = sJWT.split(".");
603     var uHeader = a[0];
604     var uPayload = a[1];
605     var uSignatureInput = uHeader + "." + uPayload;
606     var hSig = b64utohex(a[2]);
607 
608     // 2. parse JWS header
609     var pHeader = ns1.readSafeJSONString(b64utoutf8(uHeader));
610 
611     // 3. parse JWS payload
612     var pPayload = ns1.readSafeJSONString(b64utoutf8(uPayload));
613 
614     // 4. algorithm ('alg' in header) check
615     if (pHeader.alg === undefined) return false;
616     if (acceptField.alg === undefined)
617 	throw "acceptField.alg shall be specified";
618     if (! ns1.inArray(pHeader.alg, acceptField.alg)) return false;
619 
620     // 5. issuer ('iss' in payload) check
621     if (pPayload.iss !== undefined && typeof acceptField.iss === "object") {
622 	if (! ns1.inArray(pPayload.iss, acceptField.iss)) return false;
623     }
624 
625     // 6. subject ('sub' in payload) check
626     if (pPayload.sub !== undefined && typeof acceptField.sub === "object") {
627 	if (! ns1.inArray(pPayload.sub, acceptField.sub)) return false;
628     }
629 
630     // 7. audience ('aud' in payload) check
631     if (pPayload.aud !== undefined && typeof acceptField.aud === "object") {
632 	if (typeof pPayload.aud == "string") {
633 	    if (! ns1.inArray(pPayload.aud, acceptField.aud))
634 		return false;
635 	} else if (typeof pPayload.aud == "object") {
636 	    if (! ns1.includedArray(pPayload.aud, acceptField.aud))
637 		return false;
638 	}
639     }
640 
641     // 8. time validity 
642     //   (nbf - gracePeriod < now < exp + gracePeriod) && (iat - gracePeriod < now)
643     var now = KJUR.jws.IntDate.getNow();
644     if (acceptField.verifyAt !== undefined && typeof acceptField.verifyAt === "number") {
645 	now = acceptField.verifyAt;
646     }
647     if (acceptField.gracePeriod === undefined || 
648         typeof acceptField.gracePeriod !== "number") {
649 	acceptField.gracePeriod = 0;
650     }
651 
652     // 8.1 expired time 'exp' check
653     if (pPayload.exp !== undefined && typeof pPayload.exp == "number") {
654 	if (pPayload.exp + acceptField.gracePeriod < now) return false;
655     }
656 
657     // 8.2 not before time 'nbf' check
658     if (pPayload.nbf !== undefined && typeof pPayload.nbf == "number") {
659 	if (now < pPayload.nbf - acceptField.gracePeriod) return false;
660     }
661     
662     // 8.3 issued at time 'iat' check
663     if (pPayload.iat !== undefined && typeof pPayload.iat == "number") {
664 	if (now < pPayload.iat - acceptField.gracePeriod) return false;
665     }
666 
667     // 9 JWT id 'jti' check
668     if (pPayload.jti !== undefined && acceptField.jti !== undefined) {
669       if (pPayload.jti !== acceptField.jti) return false;
670     }
671 
672     // 10 JWS signature check
673     if (! KJUR.jws.JWS.verify(sJWT, key, acceptField.alg)) return false;
674 
675     // 11 passed all check
676     return true;
677 };
678 
679 /**
680  * check whether array is included by another array
681  * @name includedArray
682  * @memberOf KJUR.jws.JWS
683  * @function
684  * @static
685  * @param {Array} a1 check whether set a1 is included by a2
686  * @param {Array} a2 check whether set a1 is included by a2
687  * @return {Boolean} check whether set a1 is included by a2
688  * @since jws 3.2.3
689  * This method verifies whether an array is included by another array.
690  * It doesn't care about item ordering in a array.
691  * @example
692  * KJUR.jws.JWS.includedArray(['b'], ['b', 'c', 'a']) => true
693  * KJUR.jws.JWS.includedArray(['a', 'b'], ['b', 'c', 'a']) => true
694  * KJUR.jws.JWS.includedArray(['a', 'b'], ['b', 'c']) => false
695  */
696 KJUR.jws.JWS.includedArray = function(a1, a2) {
697     var inArray = KJUR.jws.JWS.inArray;
698     if (a1 === null) return false;
699     if (typeof a1 !== "object") return false;
700     if (typeof a1.length !== "number") return false;
701 
702     for (var i = 0; i < a1.length; i++) {
703 	if (! inArray(a1[i], a2)) return false;
704     }
705     return true;
706 };
707 
708 /**
709  * check whether item is included by array
710  * @name inArray
711  * @memberOf KJUR.jws.JWS
712  * @function
713  * @static
714  * @param {String} item check whether item is included by array
715  * @param {Array} a check whether item is included by array
716  * @return {Boolean} check whether item is included by array
717  * @since jws 3.2.3
718  * This method verifies whether an item is included by an array.
719  * It doesn't care about item ordering in an array.
720  * @example
721  * KJUR.jws.JWS.inArray('b', ['b', 'c', 'a']) => true
722  * KJUR.jws.JWS.inArray('a', ['b', 'c', 'a']) => true
723  * KJUR.jws.JWS.inArray('a', ['b', 'c']) => false
724  */
725 KJUR.jws.JWS.inArray = function(item, a) {
726     if (a === null) return false;
727     if (typeof a !== "object") return false;
728     if (typeof a.length !== "number") return false;
729     for (var i = 0; i < a.length; i++) {
730 	if (a[i] == item) return true;
731     }
732     return false;
733 };
734 
735 /**
736  * static associative array of general signature algorithm name from JWS algorithm name
737  * @since jws 3.0.0
738  */
739 KJUR.jws.JWS.jwsalg2sigalg = {
740     "HS256":	"HmacSHA256",
741     "HS384":	"HmacSHA384",
742     "HS512":	"HmacSHA512",
743     "RS256":	"SHA256withRSA",
744     "RS384":	"SHA384withRSA",
745     "RS512":	"SHA512withRSA",
746     "ES256":	"SHA256withECDSA",
747     "ES384":	"SHA384withECDSA",
748     //"ES512":	"SHA512withECDSA", // unsupported because of jsrsasign's bug
749     "PS256":	"SHA256withRSAandMGF1",
750     "PS384":	"SHA384withRSAandMGF1",
751     "PS512":	"SHA512withRSAandMGF1",
752     "none":	"none",
753 };
754 
755 // === utility static method ==================================================
756 
757 /**
758  * check whether a String "s" is a safe JSON string or not.<br/>
759  * If a String "s" is a malformed JSON string or an other object type
760  * this returns 0, otherwise this returns 1.
761  * @name isSafeJSONString
762  * @memberOf KJUR.jws.JWS
763  * @function
764  * @static
765  * @param {String} s JSON string
766  * @return {Number} 1 or 0
767  */
768 KJUR.jws.JWS.isSafeJSONString = function(s, h, p) {
769     var o = null;
770     try {
771 	o = jsonParse(s);
772 	if (typeof o != "object") return 0;
773 	if (o.constructor === Array) return 0;
774 	if (h) h[p] = o;
775 	return 1;
776     } catch (ex) {
777 	return 0;
778     }
779 };
780 
781 /**
782  * read a String "s" as JSON object if it is safe.<br/>
783  * If a String "s" is a malformed JSON string or not JSON string,
784  * this returns null, otherwise returns JSON object.
785  * @name readSafeJSONString
786  * @memberOf KJUR.jws.JWS
787  * @function
788  * @static
789  * @param {String} s JSON string
790  * @return {Object} JSON object or null
791  * @since 1.1.1
792  */
793 KJUR.jws.JWS.readSafeJSONString = function(s) {
794     var o = null;
795     try {
796 	o = jsonParse(s);
797 	if (typeof o != "object") return null;
798 	if (o.constructor === Array) return null;
799 	return o;
800     } catch (ex) {
801 	return null;
802     }
803 };
804 
805 /**
806  * get Encoed Signature Value from JWS string.<br/>
807  * @name getEncodedSignatureValueFromJWS
808  * @memberOf KJUR.jws.JWS
809  * @function
810  * @static
811  * @param {String} sJWS JWS signature string to be verified
812  * @return {String} string of Encoded Signature Value 
813  * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
814  */
815 KJUR.jws.JWS.getEncodedSignatureValueFromJWS = function(sJWS) {
816     if (sJWS.match(/^[^.]+\.[^.]+\.([^.]+)$/) == null) {
817 	throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
818     }
819     return RegExp.$1;
820 };
821 
822 /**
823  * get RFC 7638 JWK thumbprint from JWK object
824  * @name getJWKthumbprint
825  * @memberOf KJUR.jws.JWS
826  * @function
827  * @static
828  * @param {String} o JWK object to be calculated thumbprint
829  * @return {String} Base64 URL encoded JWK thumbprint value
830  * @since jsrsasign 5.0.2 jws 3.3.2
831  * @description
832  * This method calculates JWK thmubprint for specified JWK object
833  * as described in 
834  * <a href="https://tools.ietf.org/html/rfc7638">RFC 7638</a>.
835  * It supports all type of "kty". (i.e. "RSA", "EC" and "oct"
836  * (for symmetric key))
837  * Working sample is 
838  * <a href="https://kjur.github.io/jsrsasign/sample/tool_jwktp.html">here</a>.
839  * @example
840  * jwk = {"kty":"RSA", "n":"0vx...", "e":"AQAB", ...};
841  * thumbprint = KJUR.jws.JWS.getJWKthumbprint(jwk);
842  */
843 KJUR.jws.JWS.getJWKthumbprint = function(o) {
844     if (o.kty !== "RSA" &&
845 	o.kty !== "EC" &&
846 	o.kty !== "oct")
847 	throw "unsupported algorithm for JWK Thumprint";
848 
849     // 1. get canonically ordered json string
850     var s = '{';
851     if (o.kty === "RSA") {
852 	if (typeof o.n != "string" || typeof o.e != "string")
853 	    throw "wrong n and e value for RSA key";
854 	s += '"' + 'e' + '":"' + o.e + '",';
855 	s += '"' + 'kty' + '":"' + o.kty + '",';
856 	s += '"' + 'n' + '":"' + o.n + '"}';
857     } else if (o.kty === "EC") {
858 	if (typeof o.crv != "string" || 
859 	    typeof o.x != "string" ||
860 	    typeof o.y != "string")
861 	    throw "wrong crv, x and y value for EC key";
862 	s += '"' + 'crv' + '":"' + o.crv + '",';
863 	s += '"' + 'kty' + '":"' + o.kty + '",';
864 	s += '"' + 'x' + '":"' + o.x + '",';
865 	s += '"' + 'y' + '":"' + o.y + '"}';
866     } else if (o.kty === "oct") {
867 	if (typeof o.k != "string")
868 	    throw "wrong k value for oct(symmetric) key";
869 	s += '"' + 'kty' + '":"' + o.kty + '",';
870 	s += '"' + 'k' + '":"' + o.k + '"}';
871     }
872     //alert(s);
873 
874     // 2. get thumb print
875     var hJWK = rstrtohex(s);
876     var hash = KJUR.crypto.Util.hashHex(hJWK, "sha256");
877     var hashB64U = hextob64u(hash);
878 
879     return hashB64U;
880 };
881 
882 /**
883  * IntDate class for time representation for JSON Web Token(JWT)
884  * @class KJUR.jws.IntDate class
885  * @name KJUR.jws.IntDate
886  * @since jws 3.0.1
887  * @description
888  * Utility class for IntDate which is integer representation of UNIX origin time
889  * used in JSON Web Token(JWT).
890  */
891 KJUR.jws.IntDate = {};
892 
893 /**
894  * get UNIX origin time from by string
895  * @name get
896  * @memberOf KJUR.jws.IntDate
897  * @function
898  * @static
899  * @param {String} s string of time representation
900  * @return {Integer} UNIX origin time in seconds for argument 's'
901  * @since jws 3.0.1
902  * @throws "unsupported format: s" when malformed format
903  * @description
904  * This method will accept following representation of time.
905  * <ul>
906  * <li>now - current time</li>
907  * <li>now + 1hour - after 1 hour from now</li>
908  * <li>now + 1day - after 1 day from now</li>
909  * <li>now + 1month - after 30 days from now</li>
910  * <li>now + 1year - after 365 days from now</li>
911  * <li>YYYYmmDDHHMMSSZ - UTC time (ex. 20130828235959Z)</li>
912  * <li>number - UNIX origin time (seconds from 1970-01-01 00:00:00) (ex. 1377714748)</li>
913  * </ul>
914  */
915 KJUR.jws.IntDate.get = function(s) {
916     if (s == "now") {
917 	return KJUR.jws.IntDate.getNow();
918     } else if (s == "now + 1hour") {
919 	return KJUR.jws.IntDate.getNow() + 60 * 60;
920     } else if (s == "now + 1day") {
921 	return KJUR.jws.IntDate.getNow() + 60 * 60 * 24;
922     } else if (s == "now + 1month") {
923 	return KJUR.jws.IntDate.getNow() + 60 * 60 * 24 * 30;
924     } else if (s == "now + 1year") {
925 	return KJUR.jws.IntDate.getNow() + 60 * 60 * 24 * 365;
926     } else if (s.match(/Z$/)) {
927 	return KJUR.jws.IntDate.getZulu(s);
928     } else if (s.match(/^[0-9]+$/)) {
929 	return parseInt(s);
930     }
931     throw "unsupported format: " + s;
932 };
933 
934 /**
935  * get UNIX origin time from Zulu time representation string
936  * @name getZulu
937  * @memberOf KJUR.jws.IntDate
938  * @function
939  * @static
940  * @param {String} s string of Zulu time representation (ex. 20151012125959Z)
941  * @return {Integer} UNIX origin time in seconds for argument 's'
942  * @since jws 3.0.1
943  * @throws "unsupported format: s" when malformed format
944  * @description
945  * This method provides UNIX origin time from Zulu time.
946  * Following representations are supported:
947  * <ul>
948  * <li>YYYYMMDDHHmmSSZ - GeneralizedTime format</li>
949  * <li>YYMMDDHHmmSSZ - UTCTime format. If YY is greater or equal to 
950  * 50 then it represents 19YY otherwise 20YY.</li>
951  * </ul>
952  * @example
953  * KJUR.jws.IntDate.getZulu("20151012125959Z") => 1478...
954  * KJUR.jws.IntDate.getZulu("151012125959Z") => 1478...
955  */
956 KJUR.jws.IntDate.getZulu = function(s) {
957     var a;
958     if (a = s.match(/(\d+)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z/)) {
959         var sYear = RegExp.$1;
960 	var year = parseInt(sYear);
961 	if (sYear.length == 4) {
962         } else if (sYear.length == 2) {
963 	    if (50 <= year && year < 100) {
964 		year = 1900 + year;
965 	    } else if (0 <= year && year < 50) {
966 		year = 2000 + year;
967 	    } else {
968 		throw "malformed year string for UTCTime";
969 	    }
970 	} else {
971 	    throw "malformed year string";
972 	}
973 	var month = parseInt(RegExp.$2) - 1;
974 	var day = parseInt(RegExp.$3);
975 	var hour = parseInt(RegExp.$4);
976 	var min = parseInt(RegExp.$5);
977 	var sec = parseInt(RegExp.$6);
978 	var d = new Date(Date.UTC(year, month, day, hour, min, sec));
979 	return ~~(d / 1000);
980     }
981     throw "unsupported format: " + s;
982 };
983 
984 /**
985  * get UNIX origin time of current time
986  * @name getNow
987  * @memberOf KJUR.jws.IntDate
988  * @function
989  * @static
990  * @return {Integer} UNIX origin time for current time
991  * @since jws 3.0.1
992  * @description
993  * This method provides UNIX origin time for current time
994  * @example
995  * KJUR.jws.IntDate.getNow() => 1478...
996  */
997 KJUR.jws.IntDate.getNow = function() {
998     var d = ~~(new Date() / 1000);
999     return d;
1000 };
1001 
1002 /**
1003  * get UTC time string from UNIX origin time value
1004  * @name intDate2UTCString
1005  * @memberOf KJUR.jws.IntDate
1006  * @function
1007  * @static
1008  * @param {Integer} intDate UNIX origin time value (ex. 1478...)
1009  * @return {String} UTC time string
1010  * @since jws 3.0.1
1011  * @description
1012  * This method provides UTC time string for UNIX origin time value.
1013  * @example
1014  * KJUR.jws.IntDate.intDate2UTCString(1478...) => "2015 Oct ..."
1015  */
1016 KJUR.jws.IntDate.intDate2UTCString = function(intDate) {
1017     var d = new Date(intDate * 1000);
1018     return d.toUTCString();
1019 };
1020 
1021 /**
1022  * get UTC time string from UNIX origin time value
1023  * @name intDate2Zulu
1024  * @memberOf KJUR.jws.IntDate
1025  * @function
1026  * @static
1027  * @param {Integer} intDate UNIX origin time value (ex. 1478...)
1028  * @return {String} Zulu time string
1029  * @since jws 3.0.1
1030  * @description
1031  * This method provides Zulu time string for UNIX origin time value.
1032  * @example
1033  * KJUR.jws.IntDate.intDate2UTCString(1478...) => "20151012...Z"
1034  */
1035 KJUR.jws.IntDate.intDate2Zulu = function(intDate) {
1036     var d = new Date(intDate * 1000);
1037     var year = ("0000" + d.getUTCFullYear()).slice(-4);    
1038     var mon =  ("00" + (d.getUTCMonth() + 1)).slice(-2);    
1039     var day =  ("00" + d.getUTCDate()).slice(-2);    
1040     var hour = ("00" + d.getUTCHours()).slice(-2);    
1041     var min =  ("00" + d.getUTCMinutes()).slice(-2);    
1042     var sec =  ("00" + d.getUTCSeconds()).slice(-2);    
1043     return year + mon + day + hour + min + sec + "Z";
1044 };
1045 
1046