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