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