1 /* asn1csr-2.0.6.js (c) 2015-2022 Kenji Urushima | kjur.github.io/jsrsasign/license
  2  */
  3 /*
  4  * asn1csr.js - ASN.1 DER encoder classes for PKCS#10 CSR
  5  *
  6  * Copyright (c) 2015-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 asn1csr-1.0.js
 18  * @author Kenji Urushima kenji.urushima@gmail.com
 19  * @version jsrsasign 10.5.26 asn1csr 2.0.6 (2022-Jul-14)
 20  * @since jsrsasign 4.9.0
 21  * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a>
 22  */
 23 
 24 /**
 25  * kjur's ASN.1 class for CSR/PKCS#10 name space
 26  * <p>
 27  * This name space is a sub name space for {@link KJUR.asn1}.
 28  * This name space contains classes for
 29  * <a href="https://tools.ietf.org/html/rfc2986">RFC 2986</a>
 30  * certificate signing request(CSR/PKCS#10) and its utilities
 31  * to be issued your certificate from certification authorities.
 32  * <h4>PROVIDING ASN.1 STRUCTURES</h4>
 33  * <ul>
 34  * <li>{@link KJUR.asn1.csr.CertificationRequest}</li>
 35  * <li>{@link KJUR.asn1.csr.CertificationRequestInfo}</li>
 36  * </ul>
 37  * <h4>PROVIDING UTILITY CLASSES</h4>
 38  * <ul>
 39  * <li>{@link KJUR.asn1.csr.CSRUtil}</li>
 40  * </ul>
 41  * </p>
 42  * @name KJUR.asn1.csr
 43  * @namespace
 44  */
 45 if (typeof KJUR.asn1.csr == "undefined" || !KJUR.asn1.csr) KJUR.asn1.csr = {};
 46 
 47 /**
 48  * ASN.1 CertificationRequest structure class
 49  * @name KJUR.asn1.csr.CertificationRequest
 50  * @class ASN.1 CertificationRequest structure class
 51  * @param {Array} params associative array of parameters
 52  * @extends KJUR.asn1.ASN1Object
 53  * @since jsrsasign 4.9.0 asn1csr 1.0.0
 54  * @see KJUR.asn1.csr.CertificationRequestInfo
 55  * @description
 56  * This class provides CertificateRequestInfo ASN.1 structure
 57  * defined in 
 58  * <a href="https://tools.ietf.org/html/rfc2986#page-5">
 59  * RFC 2986 4.2</a>.
 60  * <pre>
 61  * CertificationRequest ::= SEQUENCE {
 62  *   certificationRequestInfo CertificationRequestInfo,
 63  *   signatureAlgorithm       AlgorithmIdentifier{{ SignatureAlgorithms }},
 64  *   signature                BIT STRING }
 65  * CertificationRequestInfo ::= SEQUENCE {
 66  *   version       INTEGER { v1(0) } (v1,...),
 67  *   subject       Name,
 68  *   subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
 69  *   attributes    [0] Attributes{{ CRIAttributes }} }
 70  * </pre>
 71  *
 72  * Argument "params" JSON object can have following keys:
 73  * <ul>
 74  * <li>{Array}subject - parameter to be passed to {@link KJUR.asn1.x509.X500Name}</li>
 75  * <li>{Object}sbjpubkey - PEM string or key object to be passed to {@link KEYUTIL.getKey}</li>
 76  * <li>{Array}extreq - array of certificate extension parameters</li>
 77  * <li>{String}sigalg - signature algorithm name (ex. SHA256withRSA)</li>
 78  * <li>{Object}sbjprvkey - PEM string or key object to be passed to {@link KEYUTIL.getKey} 
 79  * (OPTION)</li>
 80  * <li>{String}sighex - hexadecimal string of signature value. 
 81  * When this is not defined and
 82  * sbjprvkey is specified, sighex will be set automatically
 83  * during getEncodedHex() is called. (OPTION)</li>
 84  * </ul>
 85  *
 86  * <br/>
 87  * CAUTION: 
 88  * Argument "params" JSON value format have been changed without 
 89  * backward compatibility since jsrsasign 9.0.0 asn1csr 2.0.0.
 90  *
 91  * @example
 92  * // sign by private key
 93  * csr = new KJUR.asn1.csr.CertificationRequest({
 94  *   subject: {str:"/C=US/O=Test"},
 95  *   sbjpubkey: "-----BEGIN PUBLIC KEY...",
 96  *   extreq: [{extname:"subjectAltName",array:[{dns:"example.com"}]}]
 97  *   sigalg: "SHA256withRSA",
 98  *   sbjprvkey: "-----BEGIN PRIVATE KEY..."
 99  * });
100  * pem = csr.getPEM(); // signed with sbjprvkey automatically
101  *
102  * // or specifying signature value
103  * csr = new KJUR.asn1.csr.CertificationRequest({
104  *   subject: {str:"/C=US/O=Test"},
105  *   sbjpubkey: "-----BEGIN PUBLIC KEY...",
106  *   extreq: [{extname:"subjectAltName",array:[{dns:"example.com"}]}]
107  *   sigalg: "SHA256withRSA",
108  *   sighex: "1234abcd..."
109  * });
110  * pem = csr.getPEM();
111  */
112 KJUR.asn1.csr.CertificationRequest = function(params) {
113     var _KJUR = KJUR,
114 	_KJUR_asn1 = _KJUR.asn1,
115 	_DERBitString = _KJUR_asn1.DERBitString,
116 	_DERSequence = _KJUR_asn1.DERSequence,
117 	_KJUR_asn1_csr = _KJUR_asn1.csr,
118 	_KJUR_asn1_x509 = _KJUR_asn1.x509,
119 	_CertificationRequestInfo = _KJUR_asn1_csr.CertificationRequestInfo;
120 
121     _KJUR_asn1_csr.CertificationRequest.superclass.constructor.call(this);
122 
123     /**
124      * set parameter<br/>
125      * @name setByParam
126      * @memberOf KJUR.asn1.csr.CertificationRequest#
127      * @function
128      * @param params {Array} JSON object of CSR parameters
129      * @since jsrsasign 9.0.0 asn1csr 2.0.0
130      * @description
131      * This method will set parameter to this object.
132      * @example
133      * csr = new KJUR.asn1.x509.CertificationRequest();
134      * csr.setByParam({
135      *   subject: {str: "/C=JP/O=Test"},
136      *   ...
137      * });
138      */
139     this.setByParam = function(params) {
140 	this.params = params;
141     };
142 
143     /**
144      * sign CertificationRequest and set signature value internally<br/>
145      * @name sign
146      * @memberOf KJUR.asn1.csr.CertificationRequest#
147      * @function
148      * @description
149      * This method self-signs CertificateRequestInfo with a subject's
150      * private key and set signature value internally.
151      * <br/>
152      * @example
153      * csr = new KJUR.asn1.csr.CertificationRequest({
154      *   subject: "/C=JP/O=Test",
155      *   sbjpubkey: ...
156      * });
157      * csr.sign();
158      */
159     this.sign = function() {
160 	var hCSRI = 
161 	    (new _CertificationRequestInfo(this.params)).tohex();
162 	var sig = new KJUR.crypto.Signature({alg: this.params.sigalg});
163 	sig.init(this.params.sbjprvkey);
164 	sig.updateHex(hCSRI);
165 	var sighex = sig.sign();
166 	this.params.sighex = sighex;
167     };
168 
169     /**
170      * get PEM formatted certificate signing request (CSR/PKCS#10)<br/>
171      * @name getPEM
172      * @memberOf KJUR.asn1.csr.CertificationRequest#
173      * @function
174      * @return PEM formatted string of CSR/PKCS#10
175      * @description
176      * This method is to a get CSR PEM string
177      * <br/>
178      * @example
179      * csr = new KJUR.asn1.csr.CertificationRequest({
180      *   subject: "/C=JP/O=Test",
181      *   sbjpubkey: ...
182      * });
183      * csr.getPEM() → "-----BEGIN CERTIFICATE REQUEST..."
184      */
185     this.getPEM = function() {
186 	return hextopem(this.tohex(), "CERTIFICATE REQUEST");
187     };
188 
189     this.tohex = function() {
190 	var params = this.params;
191 	var csri = new KJUR.asn1.csr.CertificationRequestInfo(this.params);
192 	var algid = 
193 	    new KJUR.asn1.x509.AlgorithmIdentifier({name: params.sigalg});
194 
195 	if (params.sighex == undefined && params.sbjprvkey != undefined) {
196 	    this.sign();
197 	}
198 
199 	if (params.sighex == undefined) {
200 	    throw new Error("sighex or sbjprvkey parameter not defined");
201 	}
202 
203 	var asn1Sig = new _DERBitString({hex: "00" + params.sighex});
204 	
205 	var seq = new _DERSequence({array: [csri, algid, asn1Sig]});
206 	return seq.tohex();
207     };
208     this.getEncodedHex = function() { return this.tohex(); };
209 
210     if (params !== undefined) this.setByParam(params);
211 };
212 extendClass(KJUR.asn1.csr.CertificationRequest, KJUR.asn1.ASN1Object);
213 
214 /**
215  * ASN.1 CertificationRequestInfo structure class
216  * @name KJUR.asn1.csr.CertificationRequestInfo
217  * @class ASN.1 CertificationRequestInfo structure class
218  * @param {Array} params associative array of parameters (ex. {})
219  * @extends KJUR.asn1.ASN1Object
220  * @since jsrsasign 4.9.0 asn1csr 1.0.0
221  * @see KJUR.asn1.csr.CertificationRequest
222  * @description
223  * This class provides CertificateRequestInfo ASN.1 structure
224  * defined in 
225  * <a href="https://tools.ietf.org/html/rfc2986#page-5">
226  * RFC 2986 4.1</a>.
227  * <pre>
228  * CertificationRequestInfo ::= SEQUENCE {
229  *   version       INTEGER { v1(0) } (v1,...),
230  *   subject       Name,
231  *   subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
232  *   attributes    [0] Attributes{{ CRIAttributes }} }
233  * </pre>
234  * <br/>
235  * <br/>
236  * CAUTION: 
237  * Argument "params" JSON value format have been changed without 
238  * backward compatibility since jsrsasign 9.0.0 asn1csr 2.0.0.
239  *
240  * @example
241  * csri = new KJUR.asn1.csr.CertificationRequestInfo({
242  *   subject: {str: '/C=US/CN=b'},
243  *   sbjpubkey: <<PUBLIC KEY PEM>>,
244  *   extreq: [
245  *     {extname:"subjectAltName", array:[{dns:"example.com"}]}
246  *   ]});
247  * csri.tohex() → "30..."
248  */
249 KJUR.asn1.csr.CertificationRequestInfo = function(params) {
250     var _KJUR = KJUR,
251 	_KJUR_asn1 = _KJUR.asn1,
252 	_DERBitString = _KJUR_asn1.DERBitString,
253 	_DERSequence = _KJUR_asn1.DERSequence,
254 	_DERInteger = _KJUR_asn1.DERInteger,
255 	_DERUTF8String = _KJUR_asn1.DERUTF8String,
256 	_DERTaggedObject = _KJUR_asn1.DERTaggedObject,
257 	_newObject = _KJUR_asn1.ASN1Util.newObject,
258 	_KJUR_asn1_csr = _KJUR_asn1.csr,
259 	_KJUR_asn1_x509 = _KJUR_asn1.x509,
260 	_X500Name = _KJUR_asn1_x509.X500Name,
261 	_Extensions = _KJUR_asn1_x509.Extensions,
262 	_SubjectPublicKeyInfo = _KJUR_asn1_x509.SubjectPublicKeyInfo;
263     
264     _KJUR_asn1_csr.CertificationRequestInfo.superclass.constructor.call(this);
265 
266     this.params = null;
267 
268     this.setByParam = function(params) {
269 	if (params != undefined) this.params = params;
270     };
271 
272     this.tohex = function() {
273 	var params = this.params;
274 	var a = [];
275 	a.push(new _DERInteger({'int': 0})); // version
276 	a.push(new _X500Name(params.subject));
277 	a.push(new _SubjectPublicKeyInfo(KEYUTIL.getKey(params.sbjpubkey)));
278 	if (params.extreq != undefined) {
279 	    var extseq = new _Extensions(params.extreq);
280 	    var tagobj = _newObject({
281 		tag: {
282 		    tag:'a0',
283 		    explict:true,
284 		    obj:{seq: [{oid: "1.2.840.113549.1.9.14"},
285 			       {set: [extseq]}]}
286 		}
287 	    });
288 	    a.push(tagobj);
289 	} else {
290 	    a.push(new _DERTaggedObject({tag:"a0",
291 					 explicit:false,
292 					 obj:new _DERUTF8String({str:''})}));
293 	}
294 	var seq = new _DERSequence({array: a});
295 	return seq.tohex();
296     };
297     this.getEncodedHex = function() { return this.tohex(); };
298 
299     if (params != undefined) this.setByParam(params);
300 };
301 
302 extendClass(KJUR.asn1.csr.CertificationRequestInfo, KJUR.asn1.ASN1Object);
303 
304 /**
305  * Certification Request (CSR/PKCS#10) utilities class<br/>
306  * @name KJUR.asn1.csr.CSRUtil
307  * @class Certification Request (CSR/PKCS#10) utilities class
308  * @description
309  * This class provides utility static methods for CSR/PKCS#10.
310  * Here is a list of methods:
311  * <ul>
312  * <li>{@link KJUR.asn1.csr.CSRUtil.newCSRPEM} (DEPRECATED)</li>
313  * <li>{@link KJUR.asn1.csr.CSRUtil.getParam}</li>
314  * </ul>
315  * <br/>
316  */
317 KJUR.asn1.csr.CSRUtil = new function() {
318 };
319 
320 /**
321  * generate a PEM format of CSR/PKCS#10 certificate signing request (DEPRECATED)<br/>
322  * @name newCSRPEM
323  * @memberOf KJUR.asn1.csr.CSRUtil
324  * @function
325  * @param {Array} param parameter to generate CSR
326  * @since jsrsasign 4.9.0 asn1csr 1.0.0
327  * @deprecated since jsrsasign 9.0.0 asn1csr 2.0.0. please use {@link KJUR.asn1.csr.CertificationRequest} constructor.
328  * @description
329  * This method can generate a CSR certificate signing.
330  * 
331  * @example
332  * // 1) by key object
333  * pem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
334  *   subject: {str: '/C=US/O=Test/CN=example.com'},
335  *   sbjpubkey: pubKeyObj,
336  *   sigalg: "SHA256withRSA",
337  *   sbjprvkey: prvKeyObj,
338  *   extreq: [{
339  *     extname: "subjectAltName",
340  *     array: [{dns:"example.com"}]
341  *   }]
342  * });
343  *
344  * // 2) by private/public key PEM 
345  * pem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
346  *   subject: {str: '/C=US/O=Test/CN=example.com'},
347  *   sbjpubkey: pubKeyPEM,
348  *   sigalg: "SHA256withRSA",
349  *   sbjprvkey: prvKeyPEM
350  * });
351  *
352  * // 3) with generateKeypair
353  * kp = KEYUTIL.generateKeypair("RSA", 2048);
354  * pem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
355  *   subject: {str: '/C=US/O=Test/CN=example.com'},
356  *   sbjpubkey: kp.pubKeyObj,
357  *   sigalg: "SHA256withRSA",
358  *   sbjprvkey: kp.prvKeyObj
359  * });
360  *
361  * // 4) by private/public key PEM with extension
362  * pem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
363  *   subject: {str: '/C=US/O=Test/CN=example.com'},
364  *   ext: [
365  *     {subjectAltName: {array: [{dns: 'example.net'}]}}
366  *   ],
367  *   sbjpubkey: pubKeyPEM,
368  *   sigalg: "SHA256withRSA",
369  *   sbjprvkey: prvKeyPEM
370  * });
371  */
372 KJUR.asn1.csr.CSRUtil.newCSRPEM = function(param) {
373     var _KEYUTIL = KEYUTIL,
374 	_KJUR_asn1_csr = KJUR.asn1.csr;
375 
376     var csr = new _KJUR_asn1_csr.CertificationRequest(param);
377     var pem = csr.getPEM();
378     return pem;
379 };
380 
381 /**
382  * get field values from CSR/PKCS#10 PEM string<br/>
383  * @name getParam
384  * @memberOf KJUR.asn1.csr.CSRUtil
385  * @function
386  * @param {string} sPEM PEM string of CSR/PKCS#10
387  * @param {boolean} flagTBS result object also concludes CertificationRequestInfo (OPTION, DEFAULT=false)
388  * @returns {Array} JSON object with parsed parameters such as name or public key
389  * @since jsrsasign 9.0.0 asn1csr 2.0.0
390  * @see KJUR.asn1.csr.CertificationRequest
391  * @see KJUR.asn1.csr.CertificationRequestInfo
392  * @see KJUR.asn1.x509.X500Name
393  * @see X509#getExtParamArray
394  * @description
395  * This method parses PEM CSR/PKCS#1 string and retrieves
396  * fields such as subject name and public key. 
397  * Following parameters are available in the
398  * resulted JSON object.
399  * <ul>
400  * <li>{X500Name}subject - subject name parameters </li>
401  * <li>{String}sbjpubkey - PEM string of subject public key</li>
402  * <li>{Array}extreq - array of extensionRequest parameters</li>
403  * <li>{String}sigalg - name of signature algorithm field</li>
404  * <li>{String}sighex - hexadecimal string of signature value</li>
405  * <li>{String}tbs - a hexadecimal string of CertificationRequestInfo as to be signed(OPTION)</li>
406  * </ul>
407  * Returned JSON object can be passed to 
408  * {@link KJUR.asn1.csr.CertificationRequest} class constructor.
409  * <br/>
410  * CAUTION: 
411  * Returned JSON value format have been changed without 
412  * backward compatibility since jsrsasign 9.0.0 asn1csr 2.0.0.
413  * <br/>
414  * NOTE:
415  * The "flagTBS" supported since jsrsasign 10.5.26.
416  *
417  * @example
418  * KJUR.asn1.csr.CSRUtil.getParam("-----BEGIN CERTIFICATE REQUEST...") →
419  * {
420  *   subject: { array:[[{type:"C",value:"JP",ds:"prn"}],...],
421  *              str: "/C=JP/O=Test"},
422  *   sbjpubkey: "-----BEGIN PUBLIC KEY...",
423  *   extreq: [{extname:"subjectAltName",array:[{dns:"example.com"}]}]
424  *   sigalg: "SHA256withRSA",
425  *   sighex: "1ab3df.."
426  * }
427  *
428  * KJUR.asn1.csr.CSRUtil.getParam("-----BEGIN CERTIFICATE REQUEST...", true) →
429  * result will also have a member "tbs" in the object.
430  */
431 KJUR.asn1.csr.CSRUtil.getParam = function(sPEM, flagTBS) {
432     var _ASN1HEX = ASN1HEX,
433 	_getV = _ASN1HEX.getV,
434 	_getIdxbyList = _ASN1HEX.getIdxbyList,
435 	_getTLVbyList = _ASN1HEX.getTLVbyList,
436 	_getTLVbyListEx = _ASN1HEX.getTLVbyListEx,
437 	_getVbyListEx = _ASN1HEX.getVbyListEx;
438 
439     /*
440      * get a hexadecimal string of sequence of extension request attribute value
441      * @param {String} h hexadecimal string of whole CSR
442      * @return {String} hexadecimal string of SEQUENCE of extension request attribute value
443      */
444     var _getExtReqSeqHex = function(h) {
445 	var idx1 = _getIdxbyList(h, 0, [0, 3, 0, 0], "06"); // extreq attr OID idx
446 	if (_getV(h, idx1) != "2a864886f70d01090e") {
447 	    return null;
448 	}
449 
450 	return _getTLVbyList(h, 0, [0, 3, 0, 1, 0], "30"); // ext seq idx
451     };
452 
453     var result = {};
454 
455     if (sPEM.indexOf("-----BEGIN CERTIFICATE REQUEST") == -1)
456 	throw new Error("argument is not PEM file");
457 
458     var hex = pemtohex(sPEM, "CERTIFICATE REQUEST");
459 
460     if (flagTBS) {
461 	result.tbs = _getTLVbyList(hex, 0, [0]);
462     }
463 
464     try {
465 	var hSubject = _getTLVbyListEx(hex, 0, [0, 1]);
466 	if (hSubject == "3000") {
467 	    result.subject = {};
468 	} else {
469 	    var x = new X509();
470 	    result.subject = x.getX500Name(hSubject);
471 	}
472     } catch (ex) {};
473 
474     var hPubKey = _getTLVbyListEx(hex, 0, [0, 2]);
475     var pubkeyobj = KEYUTIL.getKey(hPubKey, null, "pkcs8pub");
476     result.sbjpubkey = KEYUTIL.getPEM(pubkeyobj, "PKCS8PUB");
477 
478     var hExtReqSeq = _getExtReqSeqHex(hex);
479     var x = new X509();
480     if (hExtReqSeq != null) {
481 	result.extreq = x.getExtParamArray(hExtReqSeq);
482     }
483 
484     try {
485 	var hSigAlg = _getTLVbyListEx(hex, 0, [1], "30");
486 	var x = new X509();
487 	result.sigalg = x.getAlgorithmIdentifierName(hSigAlg);
488     } catch (ex) {};
489 
490     try {
491 	var hSig = _getVbyListEx(hex, 0, [2]);
492 	result.sighex = hSig;
493     } catch (ex) {};
494 
495     return result;
496 };
497 
498 /**
499  * verify self-signed CSR/PKCS#10 signature<br/>
500  * @name verifySignature
501  * @memberOf KJUR.asn1.csr.CSRUtil
502  * @function
503  * @param {object} csr PEM CSR string or parsed JSON object of CSR
504  * @returns {boolean} true if self-signed signature is valid otherwise false
505  * @since jsrsasign 10.5.26 asn1csr 2.0.6
506  * @see KJUR.asn1.csr.CertificationRequest
507  * @see KJUR.asn1.csr.CertificationRequestInfo
508  * @see KJUR.asn1.csr.CSRUtil#getParam
509  * @description
510  * This method verifies self-signed signature of CSR/PKCS#10
511  * with its public key which is concluded in the CSR.
512  *
513  * @example
514  * KJUR.asn1.csr.CSRUtil.verifySignatrue("-----BEGIN CERTIFICATE REQUEST...") → true or false
515  * 
516  * p = KJUR.asn1.csr.CSRUtil.getParam("-----BEGIN CERTIFICATE REQUEST-----", true); // with tbs
517  * KJUR.asn1.csr.CSRUtil.verifySignatrue(p) → true or false
518  */
519 KJUR.asn1.csr.CSRUtil.verifySignature = function(csr) {
520     try {
521 	var pCSR = null;
522 	if (typeof csr == "string" &&
523 	    csr.indexOf("-----BEGIN CERTIFICATE REQUEST") != -1) {
524 	    pCSR = KJUR.asn1.csr.CSRUtil.getParam(csr, true);
525 	} else if (typeof csr == "object" &&
526 		   csr.sbjpubkey != undefined &&
527 		   csr.sigalg != undefined &&
528 		   csr.sighex != undefined &&
529 		   csr.tbs != undefined) {
530 	    pCSR = csr;
531 	}
532 	if (pCSR == null) return false;
533 
534 	// verify self-signed signature
535 	var sig = new KJUR.crypto.Signature({alg: pCSR.sigalg});
536 	sig.init(pCSR.sbjpubkey);
537 	sig.updateHex(pCSR.tbs);
538 	return sig.verify(pCSR.sighex);
539     } catch(ex) {
540 	alert(ex);
541 	return false;
542     }
543 };
544 
545 
546