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