1 /* x509crl.js (c) 2012-2020 Kenji Urushima | kjur.github.io/jsrsasign/license
  2  */
  3 /*
  4  * x509crl.js - X509CRL class to parse X.509 CRL
  5  *
  6  * Copyright (c) 2010-2020 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.1.0 x509crl 1.0.2 (2020-Nov-18)
 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>thisUpdate - {@link X509CRL#getThisUpdate}</li>
 45  * <li>nextUpdate - {@link X509CRL#getNextUpdate}</li>
 46  * <li>revokedCertificates - {@link X509CRL#getRevCertArray}</li>
 47  * <li>revokedCertificate - {@link X509CRL#getRevCert}</li>
 48  * <li>signature - {@link X509CRL#getSignatureValueHex}</li>
 49  * </ul>
 50  * <b>UTILITIES</b><br/>
 51  * <ul>
 52  * <li>{@link X509CRL#getParam} - get all parameters</li>
 53  * </ul>
 54  */
 55 var X509CRL = function(params) {
 56     var _KJUR = KJUR,
 57 	_isHex = _KJUR.lang.String.isHex,
 58 	_ASN1HEX = ASN1HEX,
 59 	_getV = _ASN1HEX.getV,
 60 	_getTLV = _ASN1HEX.getTLV,
 61 	_getVbyList = _ASN1HEX.getVbyList,
 62 	_getTLVbyList = _ASN1HEX.getTLVbyList,
 63 	_getTLVbyListEx = _ASN1HEX.getTLVbyListEx,
 64 	_getIdxbyList = _ASN1HEX.getIdxbyList,
 65 	_getIdxbyListEx = _ASN1HEX.getIdxbyListEx,
 66 	_getChildIdx = _ASN1HEX.getChildIdx,
 67 	_x509obj = new X509();
 68     
 69     this.hex = null;
 70     this.posSigAlg = null;
 71     this.posRevCert = null;
 72 
 73     /*
 74      * set field position of SignatureAlgorithm and revokedCertificates<br/>
 75      * @description
 76      * This method will set "posSigAlg" and "posRevCert" properties.
 77      */
 78     this._setPos = function() {
 79 	// for sigAlg
 80 	var idx = _getIdxbyList(this.hex, 0, [0, 0]);
 81 	var tag = this.hex.substr(idx, 2);
 82 	if (tag == "02") {
 83 	    this.posSigAlg = 1;
 84 	} else if (tag == "30") {
 85 	    this.posSigAlg = 0;
 86 	} else {
 87 	    throw new Error("malformed 1st item of TBSCertList: " + tag);
 88 	}
 89 
 90 	// for revCerts
 91 	var idx2 = _getIdxbyList(this.hex, 0, [0, this.posSigAlg + 3]);
 92 	var tag2 = this.hex.substr(idx2, 2);
 93 	if (tag2 == "17" || tag2 == "18") {
 94 	    var idx3, tag3;
 95 	    idx3 = _getIdxbyList(this.hex, 0, [0, this.posSigAlg + 4]);
 96 	    this.posRevCert = null;
 97 	    if (idx3 != -1) {
 98 		tag3 = this.hex.substr(idx3, 2);
 99 		if (tag3 == "30") {
100 		    this.posRevCert = this.posSigAlg + 4;
101 		}
102 	    }
103 	} else if (tag2 == "30") { // found revCert
104 	    this.posRevCert = this.posSigAlg + 3;
105 	} else if (tag2 == "a0") { // no nextUpdate and revCert
106 	    this.posRevCert = null;
107 	} else {
108 	    throw new Error("malformed nextUpdate or revCert tag: " + tag2);
109 	}
110     };
111 
112     /**
113      * get X.509 CRL format version<br/>
114      * @name getVersion
115      * @memberOf X509CRL#
116      * @function
117      * @return {Number} version field value (generally 2) or null
118      * @description
119      * This method returns a version field value TBSCertList.
120      * This returns null if there is no such field.
121      * @example
122      * crl = new X509CRL("-----BEGIN X509 CRL...");
123      * crl.getVersion() → 2
124      */
125     this.getVersion = function() {
126 	if (this.posSigAlg == 0) return null;
127 	return parseInt(_getVbyList(this.hex, 0, [0, 0], "02"), 16) + 1;
128     }
129 
130     /**
131      * get signature algorithm name in basic field
132      * @name getSignatureAlgorithmField
133      * @memberOf X509CRL#
134      * @function
135      * @return {String} signature algorithm name (ex. SHA1withRSA, SHA256withECDSA, SHA512withRSAandMGF1)
136      * @see X509#getSignatureAlgorithmField
137      * @see KJUR.asn1.x509.AlgirithmIdentifier
138      * 
139      * @description
140      * This method will get a name of signature algorithm in CRL.
141      *
142      * @example
143      * crl = new X509CRL("-----BEGIN X509 CRL...");
144      * crl.getSignatureAlgorithmField() → "SHA256withRSAandMGF1"
145      */
146     this.getSignatureAlgorithmField = function() {
147 	var hTLV = _getTLVbyList(this.hex, 0, [0, this.posSigAlg], "30");
148 	return _x509obj.getAlgorithmIdentifierName(hTLV);
149     };
150 
151     /**
152      * get JSON object of issuer field<br/>
153      * @name getIssuer
154      * @memberOf X509CRL#
155      * @function
156      * @return {Array} JSON object of issuer field
157      * @see X509#getIssuer
158      * @see X509#getX500Name
159      * @see KJUR.asn1.x509.X500Name
160      *
161      * @description
162      * This method returns parsed issuer field value as
163      * JSON object.
164      *
165      * @example
166      * crl = new X509CRL("-----BEGIN X509 CRL...");
167      * x.getIssuer() →
168      * { array: [[{type:'C',value:'JP',ds:'prn'}],...],
169      *   str: "/C=JP/..." }
170      */
171     this.getIssuer = function() {
172 	var hIssuer = _getTLVbyList(this.hex, 0, [0, this.posSigAlg + 1], "30");
173 	return _x509obj.getX500Name(hIssuer);
174     };
175 
176     /**
177      * get JSON object of thisUpdate field<br/>
178      * @name getThisUpdate
179      * @memberOf X509CRL#
180      * @function
181      * @return {String} string of thisUpdate field (ex. "YYMMDDHHmmSSZ")
182      * @see X509#getNotBefore
183      * @see X509CRL#getNextUpdate
184      * @see KJUR.asn1.x509.Time
185      *
186      * @description
187      * This method returns parsed thisUpdate field value as
188      * string.
189      *
190      * @example
191      * crl = new X509CRL("-----BEGIN X509 CRL...");
192      * x.getThisUpdate() → "200825235959Z"
193      */
194     this.getThisUpdate = function() {
195 	var hThisUpdate = _getVbyList(this.hex, 0, [0, this.posSigAlg + 2]);
196 	return result = hextorstr(hThisUpdate);
197     };
198 
199     /**
200      * get JSON object of nextUpdate field<br/>
201      * @name getNextUpdate
202      * @memberOf X509CRL#
203      * @function
204      * @return {String} string of nextUpdate field or null
205      * @see X509#getNotBefore
206      * @see X509CRL#getThisUpdate
207      * @see KJUR.asn1.x509.Time
208      *
209      * @description
210      * This method returns parsed nextUpdate field value as
211      * string. "nextUpdate" is OPTIONAL field so 
212      * when nextUpdate field doesn't exists, this returns null.
213      *
214      * @example
215      * crl = new X509CRL("-----BEGIN X509 CRL...");
216      * crl.getNextUpdate() → "200825235959Z"
217      */
218     this.getNextUpdate = function() {
219 	var idx = _getIdxbyList(this.hex, 0, [0, this.posSigAlg + 3]);
220 	var tag = this.hex.substr(idx, 2);
221 	if (tag != "17" && tag != "18") return null;
222 	return hextorstr(_getV(this.hex, idx));
223     };
224 
225     /**
226      * get array for revokedCertificates field<br/>
227      * @name getRevCertArray
228      * @memberOf X509CRL#
229      * @function
230      * @return {Array} array of revokedCertificate parameter or null
231      * @see X509CRL#getRevCert
232      *
233      * @description
234      * This method returns parsed revokedCertificates field value as
235      * array of revokedCertificate parameter.
236      * If the field doesn't exists, it returns null.
237      *
238      * @example
239      * crl = new X509CRL("-----BEGIN X509 CRL...");
240      * crl.getRevCertArray() →
241      * [{sn:"123a", date:"208025235959Z", ext: [{extname:"cRLReason",code:3}]},
242      *  {sn:"123b", date:"208026235959Z", ext: [{extname:"cRLReason",code:0}]}]
243      */
244     this.getRevCertArray = function() {
245 	if (this.posRevCert == null) return null;
246 	var a = [];
247 	var idx = _getIdxbyList(this.hex, 0, [0, this.posRevCert]);
248 	var aIdx = _getChildIdx(this.hex, idx);
249 	for (var i = 0; i < aIdx.length; i++) {
250 	    var hRevCert = _getTLV(this.hex, aIdx[i]);
251 	    a.push(this.getRevCert(hRevCert));
252 	}
253 	return a;
254     };
255 
256     /**
257      * get revokedCertificate JSON parameter<br/>
258      * @name getRevCert
259      * @memberOf X509CRL#
260      * @function
261      * @return {Array} JSON object for revokedCertificate parameter
262      * @see X509CRL#getRevCertArray
263      *
264      * @description
265      * This method returns parsed revokedCertificate parameter
266      * as JSON object.
267      *
268      * @example
269      * crl = new X509CRL();
270      * crl.getRevCertArray("30...") →
271      * {sn:"123a", date:"208025235959Z", ext: [{extname:"cRLReason",code:3}]}
272      */
273     this.getRevCert = function(hRevCert) {
274 	var param = {};
275 	var aIdx = _getChildIdx(hRevCert, 0);
276 
277 	param.sn = {hex: _getVbyList(hRevCert, 0, [0], "02")};
278 	param.date = hextorstr(_getVbyList(hRevCert, 0, [1]));
279 	if (aIdx.length == 3) {
280 	    param.ext = 
281 		_x509obj.getExtParamArray(_getTLVbyList(hRevCert, 0, [2]));
282 	}
283 
284 	return param;
285     };
286     
287     /**
288      * get signature value as hexadecimal string<br/>
289      * @name getSignatureValueHex
290      * @memberOf X509CRL#
291      * @function
292      * @return {String} signature value hexadecimal string without BitString unused bits
293      *
294      * @description
295      * This method will get signature value of CRL.
296      *
297      * @example
298      * crl = new X509CRL("-----BEGIN X509 CRL...");
299      * crl.getSignatureValueHex() &rarr "8a4c47913..."
300      */
301     this.getSignatureValueHex = function() {
302 	return _getVbyList(this.hex, 0, [2], "03", true);
303     };
304 
305     /**
306      * verifies signature value by public key<br/>
307      * @name verifySignature
308      * @memberOf X509CRL#
309      * @function
310      * @param {Object} pubKey public key object, pubkey PEM or PEM issuer cert
311      * @return {Boolean} true if signature value is valid otherwise false
312      * @see X509#verifySignature
313      * @see KJUR.crypto.Signature
314      *
315      * @description
316      * This method verifies signature value of hexadecimal string of 
317      * X.509 CRL by specified public key.
318      * The signature algorithm used to verify will refer
319      * signatureAlgorithm field. 
320      * (See {@link X509CRL#getSignatureAlgorithmField})
321      *
322      * @example
323      * crl = new X509CRL("-----BEGIN X509 CRL...");
324      * x.verifySignature(pubKey) → true, false or raising exception
325      */
326     this.verifySignature = function(pubKey) {
327 	var algName = this.getSignatureAlgorithmField();
328 	var hSigVal = this.getSignatureValueHex();
329 	var hTbsCertList = _getTLVbyList(this.hex, 0, [0], "30");
330 	
331 	var sig = new KJUR.crypto.Signature({alg: algName});
332 	sig.init(pubKey);
333 	sig.updateHex(hTbsCertList);
334 	return sig.verify(hSigVal);
335     };
336 
337     /**
338      * get JSON object for CRL parameters<br/>
339      * @name getParam
340      * @memberOf X509CRL#
341      * @function
342      * @return {Array} JSON object for CRL parameters
343      * @see KJUR.asn1.x509.CRL
344      *
345      * @description
346      * This method returns a JSON object of the CRL
347      * parameters. 
348      * Return value can be passed to
349      * {@link KJUR.asn1.x509.CRL} constructor.
350      *
351      * @example
352      * crl = new X509CRL("-----BEGIN X509 CRL...");
353      * crl.getParam() →
354      * {version: 2,
355      *  sigalg: "SHA256withRSA",
356      *  issuer: {array:
357      *    [[{type:"C",value:"JP",ds:"prn"}],[{type:"O",value:"T1",ds:"prn"}]]},
358      *  thisupdate: "200820212434Z",
359      *  nextupdate: "200910212434Z",
360      *  revcert: [
361      *   {sn:{hex:"123d..."},
362      *    date:"061110000000Z",
363      *    ext:[{extname:"cRLReason",code:4}]}],
364      *  ext: [
365      *   {extname:"authorityKeyIdentifier",kid:{hex: "03de..."}},
366      *   {extname:"cRLNumber",num:{hex:"0211"}}],
367      *  sighex: "3c5e..."}
368      */
369     this.getParam = function() {
370 	var result = {};
371 
372 	var version = this.getVersion();
373 	if (version != null) result.version = version;
374 	
375 	result.sigalg = this.getSignatureAlgorithmField();
376 	result.issuer = this.getIssuer();
377 	result.thisupdate = this.getThisUpdate();
378 
379 	var nextUpdate = this.getNextUpdate();
380 	if (nextUpdate != null) result.nextupdate = nextUpdate;
381 
382 	var revCerts = this.getRevCertArray();
383 	if (revCerts != null) result.revcert = revCerts;
384 
385 	var idxExt = _getIdxbyListEx(this.hex, 0, [0, "[0]"]);
386 	if (idxExt != -1) {
387 	    var hExtSeq = _getTLVbyListEx(this.hex, 0, [0, "[0]", 0]);
388 	    result.ext = _x509obj.getExtParamArray(hExtSeq);
389 	}
390 
391 	result.sighex = this.getSignatureValueHex();
392 	return result;
393     };
394 
395     if (typeof params == "string") {
396 	if (_isHex(params)) {
397 	    this.hex = params;
398 	} else if (params.match(/-----BEGIN X509 CRL/)) {
399 	    this.hex = pemtohex(params);
400 	}
401 	this._setPos();
402     }
403 };
404