1 /*! jws-3.0.0 (c) 2013 Kenji Urushima | kjur.github.com/jsjws/license
  2  */
  3 /*
  4  * jws.js - JSON Web Signature Class
  5  *
  6  * version: 3.0.0 (2013 Aug 27)
  7  *
  8  * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
  9  *
 10  * This software is licensed under the terms of the MIT License.
 11  * http://kjur.github.com/jsjws/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.0.js
 20  * @author Kenji Urushima kenji.urushima@gmail.com
 21  * @version 3.0.0 (2013-Aug-27)
 22  * @since jsjws 1.0
 23  * @license <a href="http://kjur.github.io/jsjws/license/">MIT License</a>
 24  */
 25 
 26 if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
 27 if (typeof KJUR.jws == "undefined" || !KJUR.jws) KJUR.jws = {};
 28 
 29 /**
 30  * JSON Web Signature(JWS) class.<br/>
 31  * @name KJUR.jws.JWS
 32  * @class JSON Web Signature(JWS) class
 33  * @property {Dictionary} parsedJWS This property is set after JWS signature verification. <br/>
 34  *           Following "parsedJWS_*" properties can be accessed as "parsedJWS.*" because of
 35  *           JsDoc restriction.
 36  * @property {String} parsedJWS_headB64U string of Encrypted JWS Header
 37  * @property {String} parsedJWS_payloadB64U string of Encrypted JWS Payload
 38  * @property {String} parsedJWS_sigvalB64U string of Encrypted JWS signature value
 39  * @property {String} parsedJWS_si string of Signature Input
 40  * @property {String} parsedJWS_sigvalH hexadecimal string of JWS signature value
 41  * @property {String} parsedJWS_sigvalBI BigInteger(defined in jsbn.js) object of JWS signature value
 42  * @property {String} parsedJWS_headS string of decoded JWS Header
 43  * @property {String} parsedJWS_headS string of decoded JWS Payload
 44  * @requires base64x.js, json-sans-eval.js and jsrsasign library
 45  * @see <a href="http://kjur.github.com/jsjws/">'jwjws'(JWS JavaScript Library) home page http://kjur.github.com/jsjws/</a>
 46  * @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a>
 47  * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-14">IETF I-D JSON Web Algorithms (JWA)</a>
 48  * @since jsjws 1.0
 49  * @description
 50  * <h4>Supported Algorithms</h4>
 51  * Here is supported algorithm names for {@link KJUR.jws.JWS.sign} and {@link KJUR.jws.JWS.verify}
 52  * methods.
 53  * <table>
 54  * <tr><th>alg value</th><th>spec requirement</th><th>jsjws support</th></tr>
 55  * <tr><td>HS256</td><td>REQUIRED</td><td>SUPPORTED</td></tr>
 56  * <tr><td>HS384</td><td>OPTIONAL</td><td>-</td></tr>
 57  * <tr><td>HS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 58  * <tr><td>RS256</td><td>RECOMMENDED</td><td>SUPPORTED</td></tr>
 59  * <tr><td>RS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 60  * <tr><td>RS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 61  * <tr><td>ES256</td><td>RECOMMENDED+</td><td>SUPPORTED</td></tr>
 62  * <tr><td>ES384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 63  * <tr><td>ES512</td><td>OPTIONAL</td><td>-</td></tr>
 64  * <tr><td>PS256</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 65  * <tr><td>PS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 66  * <tr><td>PS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
 67  * <tr><td>none</td><td>REQUIRED</td><td>SUPPORTED</td></tr>
 68  * </table>
 69  */
 70 KJUR.jws.JWS = function() {
 71 
 72     // === utility =============================================================
 73 
 74     /**
 75      * parse JWS string and set public property 'parsedJWS' dictionary.<br/>
 76      * @name parseJWS
 77      * @memberOf KJUR.jws.JWS
 78      * @function
 79      * @param {String} sJWS JWS signature string to be parsed.
 80      * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
 81      * @throws if JWS Header is a malformed JSON string.
 82      * @since jws 1.1
 83      */
 84     this.parseJWS = function(sJWS, sigValNotNeeded) {
 85 	if ((this.parsedJWS !== undefined) &&
 86 	    (sigValNotNeeded || (this.parsedJWS.sigvalH !== undefined))) {
 87 	    return;
 88 	}
 89 	if (sJWS.match(/^([^.]+)\.([^.]+)\.([^.]+)$/) == null) {
 90 	    throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
 91 	}
 92 	var b6Head = RegExp.$1;
 93 	var b6Payload = RegExp.$2;
 94 	var b6SigVal = RegExp.$3;
 95 	var sSI = b6Head + "." + b6Payload;
 96 	this.parsedJWS = {};
 97 	this.parsedJWS.headB64U = b6Head;
 98 	this.parsedJWS.payloadB64U = b6Payload;
 99 	this.parsedJWS.sigvalB64U = b6SigVal;
100 	this.parsedJWS.si = sSI;
101 
102 	if (!sigValNotNeeded) {
103 	    var hSigVal = b64utohex(b6SigVal);
104 	    var biSigVal = parseBigInt(hSigVal, 16);
105 	    this.parsedJWS.sigvalH = hSigVal;
106 	    this.parsedJWS.sigvalBI = biSigVal;
107 	}
108 
109 	var sHead = b64utoutf8(b6Head);
110 	var sPayload = b64utoutf8(b6Payload);
111 	this.parsedJWS.headS = sHead;
112 	this.parsedJWS.payloadS = sPayload;
113 
114 	if (! this.isSafeJSONString(sHead, this.parsedJWS, 'headP'))
115 	    throw "malformed JSON string for JWS Head: " + sHead;
116     };
117 
118     // ==== JWS Validation =========================================================
119     function _getSignatureInputByString(sHead, sPayload) {
120 	return utf8tob64u(sHead) + "." + utf8tob64u(sPayload);
121     };
122 
123     function _getHashBySignatureInput(sSignatureInput, sHashAlg) {
124 	var hashfunc = function(s) { return KJUR.crypto.Util.hashString(s, sHashAlg); };
125 	if (hashfunc == null) throw "hash function not defined in jsrsasign: " + sHashAlg;
126 	return hashfunc(sSignatureInput);
127     };
128 
129     function _jws_verifySignature(sHead, sPayload, hSig, hN, hE) {
130 	var sSignatureInput = _getSignatureInputByString(sHead, sPayload);
131 	var biSig = parseBigInt(hSig, 16);
132 	return _rsasign_verifySignatureWithArgs(sSignatureInput, biSig, hN, hE);
133     };
134 
135     /**
136      * verify JWS signature with naked RSA public key.<br/>
137      * This only supports "RS256" and "RS512" algorithm.
138      * @name verifyJWSByNE
139      * @memberOf KJUR.jws.JWS
140      * @function
141      * @param {String} sJWS JWS signature string to be verified
142      * @param {String} hN hexadecimal string for modulus of RSA public key
143      * @param {String} hE hexadecimal string for public exponent of RSA public key
144      * @return {String} returns 1 when JWS signature is valid, otherwise returns 0
145      * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
146      * @throws if JWS Header is a malformed JSON string.
147      * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.verify}
148      */
149     this.verifyJWSByNE = function(sJWS, hN, hE) {
150 	this.parseJWS(sJWS);
151 	return _rsasign_verifySignatureWithArgs(this.parsedJWS.si, this.parsedJWS.sigvalBI, hN, hE);    
152     };
153 
154     /**
155      * verify JWS signature with RSA public key.<br/>
156      * This only supports "RS256", "RS512", "PS256" and "PS512" algorithms.
157      * @name verifyJWSByKey
158      * @memberOf KJUR.jws.JWS
159      * @function
160      * @param {String} sJWS JWS signature string to be verified
161      * @param {RSAKey} key RSA public key
162      * @return {Boolean} returns true when JWS signature is valid, otherwise returns false
163      * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
164      * @throws if JWS Header is a malformed JSON string.
165      * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.verify}
166      */
167     this.verifyJWSByKey = function(sJWS, key) {
168 	this.parseJWS(sJWS);
169 	var hashAlg = _jws_getHashAlgFromParsedHead(this.parsedJWS.headP);
170         var isPSS = this.parsedJWS.headP['alg'].substr(0, 2) == "PS";
171 
172 	if (key.hashAndVerify) {
173 	    return key.hashAndVerify(hashAlg,
174 				     new Buffer(this.parsedJWS.si, 'utf8').toString('base64'),
175 				     b64utob64(this.parsedJWS.sigvalB64U),
176 				     'base64',
177 				     isPSS);
178 	} else if (isPSS) {
179 	    return key.verifyStringPSS(this.parsedJWS.si,
180 				       this.parsedJWS.sigvalH, hashAlg);
181 	} else {
182 	    return key.verifyString(this.parsedJWS.si,
183 				    this.parsedJWS.sigvalH);
184 	}
185     };
186 
187     /**
188      * verify JWS signature by PEM formatted X.509 certificate.<br/>
189      * This only supports "RS256" and "RS512" algorithm.
190      * @name verifyJWSByPemX509Cert
191      * @memberOf KJUR.jws.JWS
192      * @function
193      * @param {String} sJWS JWS signature string to be verified
194      * @param {String} sPemX509Cert string of PEM formatted X.509 certificate
195      * @return {String} returns 1 when JWS signature is valid, otherwise returns 0
196      * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
197      * @throws if JWS Header is a malformed JSON string.
198      * @since 1.1
199      * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.verify}
200      */
201     this.verifyJWSByPemX509Cert = function(sJWS, sPemX509Cert) {
202 	this.parseJWS(sJWS);
203 	var x509 = new X509();
204 	x509.readCertPEM(sPemX509Cert);
205 	return x509.subjectPublicKeyRSA.verifyString(this.parsedJWS.si, this.parsedJWS.sigvalH);
206     };
207 
208     // ==== JWS Generation =========================================================
209     function _jws_getHashAlgFromParsedHead(head) {
210 	var sigAlg = head["alg"];
211 	var hashAlg = "";
212 
213 	if (sigAlg != "RS256" && sigAlg != "RS512" &&
214 	    sigAlg != "PS256" && sigAlg != "PS512")
215 	    throw "JWS signature algorithm not supported: " + sigAlg;
216 	if (sigAlg.substr(2) == "256") hashAlg = "sha256";
217 	if (sigAlg.substr(2) == "512") hashAlg = "sha512";
218 	return hashAlg;
219     };
220 
221     function _jws_getHashAlgFromHead(sHead) {
222 	return _jws_getHashAlgFromParsedHead(jsonParse(sHead));
223     };
224 
225     function _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD) {
226 	var rsa = new RSAKey();
227 	rsa.setPrivate(hN, hE, hD);
228 
229 	var hashAlg = _jws_getHashAlgFromHead(sHead);
230 	var sigValue = rsa.signString(sSI, hashAlg);
231 	return sigValue;
232     };
233 
234     function _jws_generateSignatureValueBySI_Key(sHead, sPayload, sSI, key, head) {
235 	var hashAlg = null;
236 	if (typeof head == "undefined") {
237 	    hashAlg = _jws_getHashAlgFromHead(sHead);
238 	} else {
239 	    hashAlg = _jws_getHashAlgFromParsedHead(head);
240 	}
241 
242 	var isPSS = head['alg'].substr(0, 2) == "PS";
243 
244 	if (key.hashAndSign) {
245 	    return b64tob64u(key.hashAndSign(hashAlg, sSI, 'binary', 'base64', isPSS));
246 	} else if (isPSS) {
247 	    return hextob64u(key.signStringPSS(sSI, hashAlg));
248 	} else {
249 	    return hextob64u(key.signString(sSI, hashAlg));
250 	}
251     };
252 
253     function _jws_generateSignatureValueByNED(sHead, sPayload, hN, hE, hD) {
254 	var sSI = _getSignatureInputByString(sHead, sPayload);
255 	return _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD);
256     };
257 
258     /**
259      * generate JWS signature by Header, Payload and a naked RSA private key.<br/>
260      * This only supports "RS256" and "RS512" algorithm.
261      * @name generateJWSByNED
262      * @memberOf KJUR.jws.JWS
263      * @function
264      * @param {String} sHead string of JWS Header
265      * @param {String} sPayload string of JWS Payload
266      * @param {String} hN hexadecimal string for modulus of RSA public key
267      * @param {String} hE hexadecimal string for public exponent of RSA public key
268      * @param {String} hD hexadecimal string for private exponent of RSA private key
269      * @return {String} JWS signature string
270      * @throws if sHead is a malformed JSON string.
271      * @throws if supported signature algorithm was not specified in JSON Header.
272      * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.sign}
273      */
274     this.generateJWSByNED = function(sHead, sPayload, hN, hE, hD) {
275 	if (! this.isSafeJSONString(sHead)) throw "JWS Head is not safe JSON string: " + sHead;
276 	var sSI = _getSignatureInputByString(sHead, sPayload);
277 	var hSigValue = _jws_generateSignatureValueBySI_NED(sHead, sPayload, sSI, hN, hE, hD);
278 	var b64SigValue = hextob64u(hSigValue);
279 	
280 	this.parsedJWS = {};
281 	this.parsedJWS.headB64U = sSI.split(".")[0];
282 	this.parsedJWS.payloadB64U = sSI.split(".")[1];
283 	this.parsedJWS.sigvalB64U = b64SigValue;
284 
285 	return sSI + "." + b64SigValue;
286     };
287 
288     /**
289      * generate JWS signature by Header, Payload and a RSA private key.<br/>
290      * This only supports "RS256", "RS512", "PS256" and "PS512" algorithms.
291      * @name generateJWSByKey
292      * @memberOf KJUR.jws.JWS
293      * @function
294      * @param {String} sHead string of JWS Header
295      * @param {String} sPayload string of JWS Payload
296      * @param {RSAKey} RSA private key
297      * @return {String} JWS signature string
298      * @throws if sHead is a malformed JSON string.
299      * @throws if supported signature algorithm was not specified in JSON Header.
300      * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.sign}
301      */
302     this.generateJWSByKey = function(sHead, sPayload, key) {
303 	var obj = {};
304 	if (!this.isSafeJSONString(sHead, obj, 'headP'))
305 	    throw "JWS Head is not safe JSON string: " + sHead;
306 	var sSI = _getSignatureInputByString(sHead, sPayload);
307 	var b64SigValue = _jws_generateSignatureValueBySI_Key(sHead, sPayload, sSI, key, obj.headP);
308 
309 	this.parsedJWS = {};
310 	this.parsedJWS.headB64U = sSI.split(".")[0];
311 	this.parsedJWS.payloadB64U = sSI.split(".")[1];
312 	this.parsedJWS.sigvalB64U = b64SigValue;
313 
314 	return sSI + "." + b64SigValue;
315     };
316 
317     // === sign with PKCS#1 RSA private key =====================================================
318     function _jws_generateSignatureValueBySI_PemPrvKey(sHead, sPayload, sSI, sPemPrvKey) {
319 	var rsa = new RSAKey();
320 	rsa.readPrivateKeyFromPEMString(sPemPrvKey);
321 	var hashAlg = _jws_getHashAlgFromHead(sHead);
322 	var sigValue = rsa.signString(sSI, hashAlg);
323 	return sigValue;
324     };
325 
326     /**
327      * generate JWS signature by Header, Payload and a PEM formatted PKCS#1 RSA private key.<br/>
328      * This only supports "RS256" and "RS512" algorithm.
329      * @name generateJWSByP1PrvKey
330      * @memberOf KJUR.jws.JWS
331      * @function
332      * @param {String} sHead string of JWS Header
333      * @param {String} sPayload string of JWS Payload
334      * @param {String} string for sPemPrvKey PEM formatted PKCS#1 RSA private key<br/>
335      *                 Heading and trailing space characters in PEM key will be ignored.
336      * @return {String} JWS signature string
337      * @throws if sHead is a malformed JSON string.
338      * @throws if supported signature algorithm was not specified in JSON Header.
339      * @since 1.1
340      * @deprecated from 3.0.0 please move to {@link KJUR.jws.JWS.sign}
341      */
342     this.generateJWSByP1PrvKey = function(sHead, sPayload, sPemPrvKey) {
343 	if (! this.isSafeJSONString(sHead)) throw "JWS Head is not safe JSON string: " + sHead;
344 	var sSI = _getSignatureInputByString(sHead, sPayload);
345 	var hSigValue = _jws_generateSignatureValueBySI_PemPrvKey(sHead, sPayload, sSI, sPemPrvKey);
346 	var b64SigValue = hextob64u(hSigValue);
347 
348 	this.parsedJWS = {};
349 	this.parsedJWS.headB64U = sSI.split(".")[0];
350 	this.parsedJWS.payloadB64U = sSI.split(".")[1];
351 	this.parsedJWS.sigvalB64U = b64SigValue;
352 
353 	return sSI + "." + b64SigValue;
354     };
355 };
356 
357 // === major static method ========================================================
358 
359 /**
360  * generate JWS signature by specified key<br/>
361  * @name sign
362  * @memberOf KJUR.jws.JWS
363  * @function
364  * @static
365  * @param {String} alg JWS algorithm name to sign and force set to sHead or null 
366  * @param {String} sHead string of JWS Header
367  * @param {String} sPayload string of JWS Payload
368  * @param {String} key string of private key or key object to sign
369  * @param {String} pass (OPTION)passcode to use encrypted private key 
370  * @return {String} JWS signature string
371  * @since jws 3.0.0
372  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Signature.html">jsrsasign KJUR.crypto.Signature method</a>
373  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Mac.html">jsrsasign KJUR.crypto.Mac method</a>
374  * @description
375  * This method supports following algorithms.
376  * <table>
377  * <tr><th>alg value</th><th>spec requirement</th><th>jsjws support</th></tr>
378  * <tr><td>HS256</td><td>REQUIRED</td><td>SUPPORTED</td></tr>
379  * <tr><td>HS384</td><td>OPTIONAL</td><td>-</td></tr>
380  * <tr><td>HS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
381  * <tr><td>RS256</td><td>RECOMMENDED</td><td>SUPPORTED</td></tr>
382  * <tr><td>RS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
383  * <tr><td>RS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
384  * <tr><td>ES256</td><td>RECOMMENDED+</td><td>SUPPORTED</td></tr>
385  * <tr><td>ES384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
386  * <tr><td>ES512</td><td>OPTIONAL</td><td>-</td></tr>
387  * <tr><td>PS256</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
388  * <tr><td>PS384</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
389  * <tr><td>PS512</td><td>OPTIONAL</td><td>SUPPORTED</td></tr>
390  * <tr><td>none</td><td>REQUIRED</td><td>SUPPORTED</td></tr>
391  * </table>
392  * <dl>
393  * <dt>NOTE1:
394  * <dd>salt length of RSAPSS signature is the same as the hash algorithm length
395  * because of <a href="http://www.ietf.org/mail-archive/web/jose/current/msg02901.html">IETF JOSE ML discussion</a>.
396  * <dt>NOTE2:
397  * <dd>The reason of HS384 unsupport is  
398  * <a href="https://code.google.com/p/crypto-js/issues/detail?id=84">CryptoJS HmacSHA384 bug</a>.
399  * </dl>
400  */
401 KJUR.jws.JWS.sign = function(alg, sHeader, sPayload, key, pass) {
402     var ns1 = KJUR.jws.JWS;
403 
404     if (! ns1.isSafeJSONString(sHeader))
405 	throw "JWS Head is not safe JSON string: " + sHead;
406 
407     var pHeader = ns1.readSafeJSONString(sHeader);
408 
409     // 1. use alg if defined in sHeader
410     if ((alg == '' || alg == null) &&
411 	pHeader['alg'] !== undefined) {
412 	alg = pHeader['alg'];
413     }
414 
415     // 2. set alg in sHeader if undefined
416     if ((alg != '' && alg != null) &&
417 	pHeader['alg'] === undefined) {
418 	pHeader['alg'] = alg;
419 	sHeader = JSON.stringify(pHeader);
420     }
421 
422     // 3. set signature algorithm like SHA1withRSA
423     var sigAlg = null;
424     if (ns1.jwsalg2sigalg[alg] === undefined) {
425 	throw "unsupported alg name: " + alg;
426     } else {
427 	sigAlg = ns1.jwsalg2sigalg[alg];
428     }
429     
430     var uHeader = utf8tob64u(sHeader);
431     var uPayload = utf8tob64u(sPayload);
432     var uSignatureInput = uHeader + "." + uPayload
433     
434     // 4. sign
435     var hSig = "";
436     if (sigAlg.substr(0, 4) == "Hmac") {
437 	if (key === undefined)
438 	    throw "hexadecimal key shall be specified for HMAC";
439 	var mac = new KJUR.crypto.Mac({'alg': sigAlg, 'pass': hextorstr(key)});
440 	mac.updateString(uSignatureInput);
441 	hSig = mac.doFinal();
442     } else if (sigAlg.indexOf("withECDSA") != -1) {
443 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
444 	sig.init(key, pass);
445 	sig.updateString(uSignatureInput);
446 	hASN1Sig = sig.sign();
447 	hSig = KJUR.crypto.ECDSA.asn1SigToConcatSig(hASN1Sig);
448     } else if (sigAlg != "none") {
449 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
450 	sig.init(key, pass);
451 	sig.updateString(uSignatureInput);
452 	hSig = sig.sign();
453     }
454 
455     var uSig = hextob64u(hSig);
456     return uSignatureInput + "." + uSig;
457 };
458 
459 /**
460  * verify JWS signature by specified key or certificate<br/>
461  * @name verify
462  * @memberOf KJUR.jws.JWS
463  * @function
464  * @static
465  * @param {String} sJWS string of JWS signature to verify
466  * @param {String} key string of public key, certificate or key object to verify
467  * @return {Boolean} true if the signature is valid otherwise false
468  * @since jws 3.0.0
469  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Signature.html">jsrsasign KJUR.crypto.Signature method</a>
470  * @see <a href="http://kjur.github.io/jsrsasign/api/symbols/KJUR.crypto.Mac.html">jsrsasign KJUR.crypto.Mac method</a>
471  */
472 KJUR.jws.JWS.verify = function(sJWS, key) {
473     var jws = KJUR.jws.JWS;
474     var a = sJWS.split(".");
475     var uHeader = a[0];
476     var uPayload = a[1];
477     var uSignatureInput = uHeader + "." + uPayload;
478     var hSig = b64utohex(a[2]);
479 
480     var pHeader = jws.readSafeJSONString(b64utoutf8(a[0]));
481     var alg = null;
482     if (pHeader.alg === undefined) {
483 	throw "algorithm not specified in header";
484     } else {
485 	alg = pHeader.alg;
486     }
487 
488     var sigAlg = null;
489     if (jws.jwsalg2sigalg[pHeader.alg] === undefined) {
490 	throw "unsupported alg name: " + alg;
491     } else {
492 	sigAlg = jws.jwsalg2sigalg[alg];
493     }
494 
495     // x. verify
496     if (sigAlg == "none") {
497 	return true;
498     } else if (sigAlg.substr(0, 4) == "Hmac") {
499 	if (key === undefined)
500 	    throw "hexadecimal key shall be specified for HMAC";
501 	var mac = new KJUR.crypto.Mac({'alg': sigAlg, 'pass': hextorstr(key)});
502 	mac.updateString(uSignatureInput);
503 	hSig2 = mac.doFinal();
504 	return hSig == hSig2;
505     } else if (sigAlg.indexOf("withECDSA") != -1) {
506 	var hASN1Sig = null;
507         try {
508 	    hASN1Sig = KJUR.crypto.ECDSA.concatSigToASN1Sig(hSig);
509 	} catch (ex) {
510 	    return false;
511 	}
512 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
513 	sig.init(key)
514 	sig.updateString(uSignatureInput);
515 	return sig.verify(hASN1Sig);
516     } else {
517 	var sig = new KJUR.crypto.Signature({'alg': sigAlg});
518 	sig.init(key)
519 	sig.updateString(uSignatureInput);
520 	return sig.verify(hSig);
521     }
522 };
523 
524 /*
525  * @since jws 3.0.0
526  */
527 KJUR.jws.JWS.jwsalg2sigalg = {
528     "HS256":	"HmacSHA256",
529     //"HS384":	"HmacSHA384", // unsupported because of CryptoJS bug
530     "HS512":	"HmacSHA512",
531     "RS256":	"SHA256withRSA",
532     "RS384":	"SHA384withRSA",
533     "RS512":	"SHA512withRSA",
534     "ES256":	"SHA256withECDSA",
535     "ES384":	"SHA384withECDSA",
536     //"ES512":	"SHA512withECDSA", // unsupported because of jsrsasign's bug
537     "PS256":	"SHA256withRSAandMGF1",
538     "PS384":	"SHA384withRSAandMGF1",
539     "PS512":	"SHA512withRSAandMGF1",
540     "none":	"none",
541 };
542 
543 // === utility static method ======================================================
544 
545 /**
546  * check whether a String "s" is a safe JSON string or not.<br/>
547  * If a String "s" is a malformed JSON string or an other object type
548  * this returns 0, otherwise this returns 1.
549  * @name isSafeJSONString
550  * @memberOf KJUR.jws.JWS
551  * @function
552  * @static
553  * @param {String} s JSON string
554  * @return {Number} 1 or 0
555  */
556 KJUR.jws.JWS.isSafeJSONString = function(s, h, p) {
557     var o = null;
558     try {
559 	o = jsonParse(s);
560 	if (typeof o != "object") return 0;
561 	if (o.constructor === Array) return 0;
562 	if (h) h[p] = o;
563 	return 1;
564     } catch (ex) {
565 	return 0;
566     }
567 };
568 
569 /**
570  * read a String "s" as JSON object if it is safe.<br/>
571  * If a String "s" is a malformed JSON string or not JSON string,
572  * this returns null, otherwise returns JSON object.
573  * @name readSafeJSONString
574  * @memberOf KJUR.jws.JWS
575  * @function
576  * @static
577  * @param {String} s JSON string
578  * @return {Object} JSON object or null
579  * @since 1.1.1
580  */
581 KJUR.jws.JWS.readSafeJSONString = function(s) {
582     var o = null;
583     try {
584 	o = jsonParse(s);
585 	if (typeof o != "object") return null;
586 	if (o.constructor === Array) return null;
587 	return o;
588     } catch (ex) {
589 	return null;
590     }
591 };
592 
593 /**
594  * get Encoed Signature Value from JWS string.<br/>
595  * @name getEncodedSignatureValueFromJWS
596  * @memberOf KJUR.jws.JWS
597  * @function
598  * @static
599  * @param {String} sJWS JWS signature string to be verified
600  * @return {String} string of Encoded Signature Value 
601  * @throws if sJWS is not comma separated string such like "Header.Payload.Signature".
602  */
603 KJUR.jws.JWS.getEncodedSignatureValueFromJWS = function(sJWS) {
604     if (sJWS.match(/^[^.]+\.[^.]+\.([^.]+)$/) == null) {
605 	throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
606     }
607     return RegExp.$1;
608 };
609 
610 /**
611  * IntDate class for time representation for JSON Web Token(JWT)
612  * @class KJUR.jws.IntDate class
613  * @name KJUR.jws.IntDate
614  * @since jws 3.0.1
615  * @description
616  * Utility class for IntDate which is integer representation of UNIX origin time
617  * used in JSON Web Token(JWT).
618  */
619 KJUR.jws.IntDate = function() {
620 };
621 
622 /**
623  * @name get
624  * @memberOf KJUR.jws.IntDate
625  * @function
626  * @static
627  * @param {String} s string of time representation
628  * @return {Integer} UNIX origin time in seconds for argument 's'
629  * @since jws 3.0.1
630  * @throws "unsupported format: s" when malformed format
631  * @description
632  * This method will accept following representation of time.
633  * <ul>
634  * <li>now - current time</li>
635  * <li>now + 1hour - after 1 hour from now</li>
636  * <li>now + 1day - after 1 day from now</li>
637  * <li>now + 1month - after 30 days from now</li>
638  * <li>now + 1year - after 365 days from now</li>
639  * <li>YYYYmmDDHHMMSSZ - UTC time (ex. 20130828235959Z)</li>
640  * <li>number - UNIX origin time (seconds from 1970-01-01 00:00:00) (ex. 1377714748)</li>
641  * </ul>
642  */
643 KJUR.jws.IntDate.get = function(s) {
644     if (s == "now") {
645 	return KJUR.jws.IntDate.getNow();
646     } else if (s == "now + 1hour") {
647 	return KJUR.jws.IntDate.getNow() + 60 * 60;
648     } else if (s == "now + 1day") {
649 	return KJUR.jws.IntDate.getNow() + 60 * 60 * 24;
650     } else if (s == "now + 1month") {
651 	return KJUR.jws.IntDate.getNow() + 60 * 60 * 24 * 30;
652     } else if (s == "now + 1year") {
653 	return KJUR.jws.IntDate.getNow() + 60 * 60 * 24 * 365;
654     } else if (s.match(/Z$/)) {
655 	return KJUR.jws.IntDate.getZulu(s);
656     } else if (s.match(/^[0-9]+$/)) {
657 	return parseInt(s);
658     }
659     throw "unsupported format: " + s;
660 };
661 
662 KJUR.jws.IntDate.getZulu = function(s) {
663     if (a = s.match(/(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z/)) {
664 	var year = parseInt(RegExp.$1);
665 	var month = parseInt(RegExp.$2) - 1;
666 	var day = parseInt(RegExp.$3);
667 	var hour = parseInt(RegExp.$4);
668 	var min = parseInt(RegExp.$5);
669 	var sec = parseInt(RegExp.$6);
670 	var d = new Date(Date.UTC(year, month, day, hour, min, sec));
671 	return ~~(d / 1000);
672     }
673     throw "unsupported format: " + s;
674 };
675 
676 /*
677  * @since jws 3.0.1
678  */
679 KJUR.jws.IntDate.getNow = function() {
680     var d = ~~(new Date() / 1000);
681     return d;
682 };
683 
684 /*
685  * @since jws 3.0.1
686  */
687 KJUR.jws.IntDate.intDate2UTCString = function(intDate) {
688     var d = new Date(intDate * 1000);
689     return d.toUTCString();
690 };
691 
692 /*
693  * @since jws 3.0.1
694  */
695 KJUR.jws.IntDate.intDate2Zulu = function(intDate) {
696     var d = new Date(intDate * 1000);
697     var year = ("0000" + d.getUTCFullYear()).slice(-4);    
698     var mon =  ("00" + (d.getUTCMonth() + 1)).slice(-2);    
699     var day =  ("00" + d.getUTCDate()).slice(-2);    
700     var hour = ("00" + d.getUTCHours()).slice(-2);    
701     var min =  ("00" + d.getUTCMinutes()).slice(-2);    
702     var sec =  ("00" + d.getUTCSeconds()).slice(-2);    
703     return year + mon + day + hour + min + sec + "Z";
704 };
705