1 /* x509crl.js (c) 2012-2022 Kenji Urushima | kjur.github.io/jsrsasign/license 2 */ 3 /* 4 * x509crl.js - X509CRL class to parse X.509 CRL 5 * 6 * Copyright (c) 2010-2022 Kenji Urushima (kenji.urushima@gmail.com) 7 * 8 * This software is licensed under the terms of the MIT License. 9 * https://kjur.github.io/jsrsasign/license 10 * 11 * The above copyright and license notice shall be 12 * included in all copies or substantial portions of the Software. 13 */ 14 15 /** 16 * @fileOverview 17 * @name x509crl.js 18 * @author Kenji Urushima kenji.urushima@gmail.com 19 * @version jsrsasign 10.5.16 x509crl 1.0.5 (2022-Apr-08) 20 * @since jsrsasign 10.1.0 21 * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a> 22 */ 23 24 /** 25 * hexadecimal X.509 CRL ASN.1 parser class.<br/> 26 * @class hexadecimal X.509 CRL ASN.1 parser class 27 * @param {String} params X.509 CRL PEM string or hexadecimal string 28 * @property {String} hex hexadecimal string of X.509 CRL ASN.1 data 29 * @property {Integer} posSigAlg index of SignatureAlgorithm field in TBSCertList position depends on CRL version field 30 * @property {Integer} posRevCert index of revokedCertificates field in TBSCertList depends on CRL version and nextUpdate field 31 * @author Kenji Urushima 32 * @version 1.0.0 (2020-Aug-26) 33 * @see X509 34 * @see <a href="https://kjur.github.io/jsrsasigns/">jsrsasign home page https://kjur.github.io/jsrsasign/</a> 35 * 36 * @description 37 * This class parses X.509 CRL. Following methods are provided to 38 * get field value:<br/> 39 * <b>BASIC FIELD</b><br/> 40 * <ul> 41 * <li>version - {@link X509CRL#getVersion}</li> 42 * <li>signatureAlgorithm - {@link X509CRL#getSignatureAlgorithmField}</li> 43 * <li>issuer - {@link X509CRL#getIssuer}</li> 44 * <li>issuer - {@link X509CRL#getIssuerHex}</li> 45 * <li>thisUpdate - {@link X509CRL#getThisUpdate}</li> 46 * <li>nextUpdate - {@link X509CRL#getNextUpdate}</li> 47 * <li>revokedCertificates - {@link X509CRL#getRevCertArray}</li> 48 * <li>revokedCertificate - {@link X509CRL#getRevCert}</li> 49 * <li>signature - {@link X509CRL#getSignatureValueHex}</li> 50 * </ul> 51 * <b>UTILITIES</b><br/> 52 * <ul> 53 * <li>{@link X509CRL#getParam} - get all parameters</li> 54 * </ul> 55 * 56 * @example 57 * // constructor 58 * crl = new X509CRL("-----BEGIN X509 CRL..."); 59 * crl = new X509CRL("3082..."); 60 */ 61 var X509CRL = function(params) { 62 var _KJUR = KJUR, 63 _isHex = _KJUR.lang.String.isHex, 64 _ASN1HEX = ASN1HEX, 65 _getV = _ASN1HEX.getV, 66 _getTLV = _ASN1HEX.getTLV, 67 _getVbyList = _ASN1HEX.getVbyList, 68 _getTLVbyList = _ASN1HEX.getTLVbyList, 69 _getTLVbyListEx = _ASN1HEX.getTLVbyListEx, 70 _getIdxbyList = _ASN1HEX.getIdxbyList, 71 _getIdxbyListEx = _ASN1HEX.getIdxbyListEx, 72 _getChildIdx = _ASN1HEX.getChildIdx, 73 _x509obj = new X509(); 74 75 this.hex = null; 76 this.posSigAlg = null; 77 this.posRevCert = null; 78 this.parsed = null; 79 80 /* 81 * set field position of SignatureAlgorithm and revokedCertificates<br/> 82 * @description 83 * This method will set "posSigAlg" and "posRevCert" properties. 84 */ 85 this._setPos = function() { 86 // for sigAlg 87 var idx = _getIdxbyList(this.hex, 0, [0, 0]); 88 var tag = this.hex.substr(idx, 2); 89 if (tag == "02") { 90 this.posSigAlg = 1; 91 } else if (tag == "30") { 92 this.posSigAlg = 0; 93 } else { 94 throw new Error("malformed 1st item of TBSCertList: " + tag); 95 } 96 97 // for revCerts 98 var idx2 = _getIdxbyList(this.hex, 0, [0, this.posSigAlg + 3]); 99 var tag2 = this.hex.substr(idx2, 2); 100 if (tag2 == "17" || tag2 == "18") { 101 var idx3, tag3; 102 idx3 = _getIdxbyList(this.hex, 0, [0, this.posSigAlg + 4]); 103 this.posRevCert = null; 104 if (idx3 != -1) { 105 tag3 = this.hex.substr(idx3, 2); 106 if (tag3 == "30") { 107 this.posRevCert = this.posSigAlg + 4; 108 } 109 } 110 } else if (tag2 == "30") { // found revCert 111 this.posRevCert = this.posSigAlg + 3; 112 } else if (tag2 == "a0") { // no nextUpdate and revCert 113 this.posRevCert = null; 114 } else { 115 throw new Error("malformed nextUpdate or revCert tag: " + tag2); 116 } 117 }; 118 119 /** 120 * get X.509 CRL format version<br/> 121 * @name getVersion 122 * @memberOf X509CRL# 123 * @function 124 * @return {Number} version field value (generally 2) or null 125 * @description 126 * This method returns a version field value TBSCertList. 127 * This returns null if there is no such field. 128 * @example 129 * crl = new X509CRL("-----BEGIN X509 CRL..."); 130 * crl.getVersion() → 2 131 */ 132 this.getVersion = function() { 133 if (this.posSigAlg == 0) return null; 134 return parseInt(_getVbyList(this.hex, 0, [0, 0], "02"), 16) + 1; 135 } 136 137 /** 138 * get signature algorithm name in basic field 139 * @name getSignatureAlgorithmField 140 * @memberOf X509CRL# 141 * @function 142 * @return {String} signature algorithm name (ex. SHA1withRSA, SHA256withECDSA, SHA512withRSAandMGF1) 143 * @see X509#getSignatureAlgorithmField 144 * @see KJUR.asn1.x509.AlgirithmIdentifier 145 * 146 * @description 147 * This method will get a name of signature algorithm in CRL. 148 * 149 * @example 150 * crl = new X509CRL("-----BEGIN X509 CRL..."); 151 * crl.getSignatureAlgorithmField() → "SHA256withRSAandMGF1" 152 */ 153 this.getSignatureAlgorithmField = function() { 154 var hTLV = _getTLVbyList(this.hex, 0, [0, this.posSigAlg], "30"); 155 return _x509obj.getAlgorithmIdentifierName(hTLV); 156 }; 157 158 /** 159 * get JSON object of issuer field<br/> 160 * @name getIssuer 161 * @memberOf X509CRL# 162 * @function 163 * @return {Array} JSON object of issuer field 164 * @see X509#getIssuer 165 * @see X509#getX500Name 166 * @see KJUR.asn1.x509.X500Name 167 * 168 * @description 169 * This method returns parsed issuer field value as 170 * JSON object. 171 * 172 * @example 173 * crl = new X509CRL("-----BEGIN X509 CRL..."); 174 * x.getIssuer() → 175 * { array: [[{type:'C',value:'JP',ds:'prn'}],...], 176 * str: "/C=JP/..." } 177 */ 178 this.getIssuer = function() { 179 return _x509obj.getX500Name(this.getIssuerHex()); 180 }; 181 182 /** 183 * get hexadecimal string of issuer field TLV of certificate.<br/> 184 * @name getIssuerHex 185 * @memberOf X509CRL# 186 * @function 187 * @return {string} hexadecial string of issuer DN ASN.1 188 * @see X509CRL#getIssuer 189 * @since jsrsasign 10.5.5 x509crl 1.0.3 190 * 191 * @description 192 * This method returns ASN.1 DER hexadecimal string of 193 * issuer field. 194 * 195 * @example 196 * crl = new X509CRL("-----BEGIN X509 CRL..."); 197 * x.getIssuerHex() → "30..." 198 */ 199 this.getIssuerHex = function() { 200 return _getTLVbyList(this.hex, 0, [0, this.posSigAlg + 1], "30"); 201 }; 202 203 /** 204 * get JSON object of thisUpdate field<br/> 205 * @name getThisUpdate 206 * @memberOf X509CRL# 207 * @function 208 * @return {String} string of thisUpdate field (ex. "YYMMDDHHmmSSZ") 209 * @see X509#getNotBefore 210 * @see X509CRL#getNextUpdate 211 * @see KJUR.asn1.x509.Time 212 * 213 * @description 214 * This method returns parsed thisUpdate field value as 215 * string. 216 * 217 * @example 218 * crl = new X509CRL("-----BEGIN X509 CRL..."); 219 * x.getThisUpdate() → "200825235959Z" 220 */ 221 this.getThisUpdate = function() { 222 var hThisUpdate = _getVbyList(this.hex, 0, [0, this.posSigAlg + 2]); 223 return result = hextorstr(hThisUpdate); 224 }; 225 226 /** 227 * get JSON object of nextUpdate field<br/> 228 * @name getNextUpdate 229 * @memberOf X509CRL# 230 * @function 231 * @return {String} string of nextUpdate field or null 232 * @see X509#getNotBefore 233 * @see X509CRL#getThisUpdate 234 * @see KJUR.asn1.x509.Time 235 * 236 * @description 237 * This method returns parsed nextUpdate field value as 238 * string. "nextUpdate" is OPTIONAL field so 239 * when nextUpdate field doesn't exists, this returns null. 240 * 241 * @example 242 * crl = new X509CRL("-----BEGIN X509 CRL..."); 243 * crl.getNextUpdate() → "200825235959Z" 244 */ 245 this.getNextUpdate = function() { 246 var idx = _getIdxbyList(this.hex, 0, [0, this.posSigAlg + 3]); 247 var tag = this.hex.substr(idx, 2); 248 if (tag != "17" && tag != "18") return null; 249 return hextorstr(_getV(this.hex, idx)); 250 }; 251 252 /** 253 * get array for revokedCertificates field<br/> 254 * @name getRevCertArray 255 * @memberOf X509CRL# 256 * @function 257 * @return {Array} array of revokedCertificate parameter or null 258 * @see X509CRL#getRevCert 259 * 260 * @description 261 * This method returns parsed revokedCertificates field value as 262 * array of revokedCertificate parameter. 263 * If the field doesn't exists, it returns null. 264 * 265 * @example 266 * crl = new X509CRL("-----BEGIN X509 CRL..."); 267 * crl.getRevCertArray() → 268 * [{sn:"123a", date:"208025235959Z", ext: [{extname:"cRLReason",code:3}]}, 269 * {sn:"123b", date:"208026235959Z", ext: [{extname:"cRLReason",code:0}]}] 270 */ 271 this.getRevCertArray = function() { 272 if (this.posRevCert == null) return null; 273 var a = []; 274 var idx = _getIdxbyList(this.hex, 0, [0, this.posRevCert]); 275 var aIdx = _getChildIdx(this.hex, idx); 276 for (var i = 0; i < aIdx.length; i++) { 277 var hRevCert = _getTLV(this.hex, aIdx[i]); 278 a.push(this.getRevCert(hRevCert)); 279 } 280 return a; 281 }; 282 283 /** 284 * get revokedCertificate JSON parameter<br/> 285 * @name getRevCert 286 * @memberOf X509CRL# 287 * @function 288 * @return {Array} JSON object for revokedCertificate parameter 289 * @see X509CRL#getRevCertArray 290 * 291 * @description 292 * This method returns parsed revokedCertificate parameter 293 * as JSON object. 294 * 295 * @example 296 * crl = new X509CRL(); 297 * crl.getRevCertArray("30...") → 298 * {sn:"123a", date:"208025235959Z", ext: [{extname:"cRLReason",code:3}]} 299 */ 300 this.getRevCert = function(hRevCert) { 301 var param = {}; 302 var aIdx = _getChildIdx(hRevCert, 0); 303 304 param.sn = {hex: _getVbyList(hRevCert, 0, [0], "02")}; 305 param.date = hextorstr(_getVbyList(hRevCert, 0, [1])); 306 if (aIdx.length == 3) { 307 param.ext = 308 _x509obj.getExtParamArray(_getTLVbyList(hRevCert, 0, [2])); 309 } 310 311 return param; 312 }; 313 314 /** 315 * get revokedCertificate associative array for checking certificate<br/> 316 * @name findRevCert 317 * @memberOf X509CRL# 318 * @function 319 * @param {string} PEM or hexadecimal string of certificate to be revocation-checked 320 * @return {object} JSON object for revokedCertificate or null 321 * @see X509CRL#getParam 322 * @see X509CRL#findRevCertBySN 323 * @since jsrsasign 10.5.5 x509crl 1.0.3 324 * 325 * @description 326 * This method will find revokedCertificate entry as JSON object 327 * for a specified certificate. <br/> 328 * When the serial number is not found in the entry, this returns null.<br/> 329 * Before finding, {@link X509CRL#getParam} is called internally 330 * to parse CRL.<br/> 331 * NOTE: This method will just find an entry for a serial number. 332 * You need to check whether CRL is proper one or not 333 * for checking certificate such as signature validation or 334 * name checking. 335 * 336 * @example 337 * crl = new X509CRL(PEMCRL); 338 * 339 * crl.findRevCert(PEMCERT-REVOKED) → 340 * {sn:"123a", date:"208025235959Z", ext: [{extname:"cRLReason",code:3}]} 341 * 342 * crl.findRevCert(PEMCERT-NOTREVOKED) → null 343 * 344 * crl.findRevCert(CERT-HEX) → null or {sn:...} 345 */ 346 this.findRevCert = function(sCert) { 347 var x = new X509(sCert); 348 var hSN = x.getSerialNumberHex(); 349 return this.findRevCertBySN(hSN); 350 }; 351 352 /** 353 * get revokedCertificate associative array for serial number<br/> 354 * @name findRevCertBySN 355 * @memberOf X509CRL# 356 * @function 357 * @param {string} hexadecimal string of checking certificate serial number 358 * @return {object} JSON object for revokedCertificate or null 359 * @see X509CRL#getParam 360 * @see X509CRL#findRevCert 361 * @since jsrsasign 10.5.5 x509crl 1.0.3 362 * 363 * @description 364 * This method will find revokedCertificate entry as JSON object 365 * for a specified serial number. <br/> 366 * When the serial number is not found in the entry, this returns null.<br/> 367 * Before finding, {@link X509CRL#getParam} is called internally 368 * to parse CRL.<br/> 369 * NOTE: This method will just find an entry for a serial number. 370 * You need to check whether CRL is proper one or not 371 * for checking certificate such as signature validation or 372 * name checking. 373 * 374 * @example 375 * crl = new X509CRL(PEMCRL); 376 * crl.findRevCertBySN("123a") → // revoked 377 * {sn:"123a", date:"208025235959Z", ext: [{extname:"cRLReason",code:3}]} 378 * 379 * crl.findRevCertBySN("0000") → null // not revoked 380 */ 381 this.findRevCertBySN = function(hSN) { 382 if (this.parsed == null) this.getParam(); 383 if (this.parsed.revcert == null) return null; 384 var revcert = this.parsed.revcert; 385 for (var i = 0; i < revcert.length; i++) { 386 if (hSN == revcert[i].sn.hex) return revcert[i]; 387 } 388 return null; 389 }; 390 391 /** 392 * get signature value as hexadecimal string<br/> 393 * @name getSignatureValueHex 394 * @memberOf X509CRL# 395 * @function 396 * @return {String} signature value hexadecimal string without BitString unused bits 397 * 398 * @description 399 * This method will get signature value of CRL. 400 * 401 * @example 402 * crl = new X509CRL("-----BEGIN X509 CRL..."); 403 * crl.getSignatureValueHex() &rarr "8a4c47913..." 404 */ 405 this.getSignatureValueHex = function() { 406 return _getVbyList(this.hex, 0, [2], "03", true); 407 }; 408 409 /** 410 * verifies signature value by public key<br/> 411 * @name verifySignature 412 * @memberOf X509CRL# 413 * @function 414 * @param {Object} pubKey public key object, pubkey PEM or PEM issuer cert 415 * @return {Boolean} true if signature value is valid otherwise false 416 * @see X509#verifySignature 417 * @see KJUR.crypto.Signature 418 * 419 * @description 420 * This method verifies signature value of hexadecimal string of 421 * X.509 CRL by specified public key. 422 * The signature algorithm used to verify will refer 423 * signatureAlgorithm field. 424 * (See {@link X509CRL#getSignatureAlgorithmField}) 425 * 426 * @example 427 * crl = new X509CRL("-----BEGIN X509 CRL..."); 428 * x.verifySignature(pubKey) → true, false or raising exception 429 */ 430 this.verifySignature = function(pubKey) { 431 var algName = this.getSignatureAlgorithmField(); 432 var hSigVal = this.getSignatureValueHex(); 433 var hTbsCertList = _getTLVbyList(this.hex, 0, [0], "30"); 434 435 var sig = new KJUR.crypto.Signature({alg: algName}); 436 sig.init(pubKey); 437 sig.updateHex(hTbsCertList); 438 return sig.verify(hSigVal); 439 }; 440 441 /** 442 * get JSON object for CRL parameters<br/> 443 * @name getParam 444 * @memberOf X509CRL# 445 * @function 446 * @return {Array} JSON object for CRL parameters 447 * @see KJUR.asn1.x509.CRL 448 * 449 * @description 450 * This method returns a JSON object of the CRL 451 * parameters. 452 * Return value can be passed to 453 * {@link KJUR.asn1.x509.CRL} constructor. 454 * <br/> 455 * NOTE1: From jsrsasign 10.5.16, optional argument can be applied. 456 * It can have following members: 457 * <ul> 458 * <li>tbshex - if this is true, tbshex member with hex value of 459 * tbsCertList will be added</li> 460 * <li>nodnarray - if this is true, array member for subject and 461 * issuer will be deleted to simplify it<li> 462 * </ul> 463 * 464 * @example 465 * crl = new X509CRL("-----BEGIN X509 CRL..."); 466 * crl.getParam() → 467 * {version: 2, 468 * sigalg: "SHA256withRSA", 469 * issuer: {array: 470 * [[{type:"C",value:"JP",ds:"prn"}],[{type:"O",value:"T1",ds:"prn"}]]}, 471 * thisupdate: "200820212434Z", 472 * nextupdate: "200910212434Z", 473 * revcert: [ 474 * {sn:{hex:"123d..."}, 475 * date:"061110000000Z", 476 * ext:[{extname:"cRLReason",code:4}]}], 477 * ext: [ 478 * {extname:"authorityKeyIdentifier",kid:{hex: "03de..."}}, 479 * {extname:"cRLNumber",num:{hex:"0211"}}], 480 * sighex: "3c5e..."} 481 * 482 * crl.getParam({tbshex: true}) → { ... , tbshex: "30..." } 483 * crl.getParam({nodnarray: true}) → {issuer: {str: "/C=JP"}, ...} 484 */ 485 this.getParam = function(option) { 486 var result = {}; 487 488 var version = this.getVersion(); 489 if (version != null) result.version = version; 490 491 result.sigalg = this.getSignatureAlgorithmField(); 492 result.issuer = this.getIssuer(); 493 result.thisupdate = this.getThisUpdate(); 494 495 var nextUpdate = this.getNextUpdate(); 496 if (nextUpdate != null) result.nextupdate = nextUpdate; 497 498 var revCerts = this.getRevCertArray(); 499 if (revCerts != null) result.revcert = revCerts; 500 501 var idxExt = _getIdxbyListEx(this.hex, 0, [0, "[0]"]); 502 if (idxExt != -1) { 503 var hExtSeq = _getTLVbyListEx(this.hex, 0, [0, "[0]", 0]); 504 result.ext = _x509obj.getExtParamArray(hExtSeq); 505 } 506 507 result.sighex = this.getSignatureValueHex(); 508 509 this.parsed = result; 510 511 // for options 512 if (typeof option == "object") { 513 if (option.tbshex == true) { 514 result.tbshex = _getTLVbyList(this.hex, 0, [0]); 515 } 516 if (option.nodnarray == true) { 517 delete result.issuer.array; 518 } 519 } 520 521 return result; 522 }; 523 524 if (typeof params == "string") { 525 if (_isHex(params)) { 526 this.hex = params; 527 } else if (params.match(/-----BEGIN X509 CRL/)) { 528 this.hex = pemtohex(params); 529 } 530 this._setPos(); 531 } 532 }; 533