Certificate.ts (30182B)
1 import * as asn1js from "asn1js"; 2 import * as pvtsutils from "pvtsutils"; 3 import * as pvutils from "pvutils"; 4 import * as common from "./common"; 5 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; 6 import { RelativeDistinguishedNames, RelativeDistinguishedNamesJson, RelativeDistinguishedNamesSchema } from "./RelativeDistinguishedNames"; 7 import { Time, TimeJson, TimeSchema } from "./Time"; 8 import { PublicKeyInfo, PublicKeyInfoJson, PublicKeyInfoSchema } from "./PublicKeyInfo"; 9 import { Extension, ExtensionJson } from "./Extension"; 10 import { Extensions, ExtensionsSchema } from "./Extensions"; 11 import * as Schema from "./Schema"; 12 import { id_BasicConstraints } from "./ObjectIdentifiers"; 13 import { BasicConstraints } from "./BasicConstraints"; 14 import { CryptoEnginePublicKeyParams } from "./CryptoEngine/CryptoEngineInterface"; 15 import { AsnError } from "./errors"; 16 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 17 import { EMPTY_BUFFER, EMPTY_STRING } from "./constants"; 18 19 const TBS = "tbs"; 20 const VERSION = "version"; 21 const SERIAL_NUMBER = "serialNumber"; 22 const SIGNATURE = "signature"; 23 const ISSUER = "issuer"; 24 const NOT_BEFORE = "notBefore"; 25 const NOT_AFTER = "notAfter"; 26 const SUBJECT = "subject"; 27 const SUBJECT_PUBLIC_KEY_INFO = "subjectPublicKeyInfo"; 28 const ISSUER_UNIQUE_ID = "issuerUniqueID"; 29 const SUBJECT_UNIQUE_ID = "subjectUniqueID"; 30 const EXTENSIONS = "extensions"; 31 const SIGNATURE_ALGORITHM = "signatureAlgorithm"; 32 const SIGNATURE_VALUE = "signatureValue"; 33 const TBS_CERTIFICATE = "tbsCertificate"; 34 const TBS_CERTIFICATE_VERSION = `${TBS_CERTIFICATE}.${VERSION}`; 35 const TBS_CERTIFICATE_SERIAL_NUMBER = `${TBS_CERTIFICATE}.${SERIAL_NUMBER}`; 36 const TBS_CERTIFICATE_SIGNATURE = `${TBS_CERTIFICATE}.${SIGNATURE}`; 37 const TBS_CERTIFICATE_ISSUER = `${TBS_CERTIFICATE}.${ISSUER}`; 38 const TBS_CERTIFICATE_NOT_BEFORE = `${TBS_CERTIFICATE}.${NOT_BEFORE}`; 39 const TBS_CERTIFICATE_NOT_AFTER = `${TBS_CERTIFICATE}.${NOT_AFTER}`; 40 const TBS_CERTIFICATE_SUBJECT = `${TBS_CERTIFICATE}.${SUBJECT}`; 41 const TBS_CERTIFICATE_SUBJECT_PUBLIC_KEY = `${TBS_CERTIFICATE}.${SUBJECT_PUBLIC_KEY_INFO}`; 42 const TBS_CERTIFICATE_ISSUER_UNIQUE_ID = `${TBS_CERTIFICATE}.${ISSUER_UNIQUE_ID}`; 43 const TBS_CERTIFICATE_SUBJECT_UNIQUE_ID = `${TBS_CERTIFICATE}.${SUBJECT_UNIQUE_ID}`; 44 const TBS_CERTIFICATE_EXTENSIONS = `${TBS_CERTIFICATE}.${EXTENSIONS}`; 45 const CLEAR_PROPS = [ 46 TBS_CERTIFICATE, 47 TBS_CERTIFICATE_VERSION, 48 TBS_CERTIFICATE_SERIAL_NUMBER, 49 TBS_CERTIFICATE_SIGNATURE, 50 TBS_CERTIFICATE_ISSUER, 51 TBS_CERTIFICATE_NOT_BEFORE, 52 TBS_CERTIFICATE_NOT_AFTER, 53 TBS_CERTIFICATE_SUBJECT, 54 TBS_CERTIFICATE_SUBJECT_PUBLIC_KEY, 55 TBS_CERTIFICATE_ISSUER_UNIQUE_ID, 56 TBS_CERTIFICATE_SUBJECT_UNIQUE_ID, 57 TBS_CERTIFICATE_EXTENSIONS, 58 SIGNATURE_ALGORITHM, 59 SIGNATURE_VALUE 60 ]; 61 62 export type TBSCertificateSchema = Schema.SchemaParameters<{ 63 tbsCertificateVersion?: string; 64 tbsCertificateSerialNumber?: string; 65 signature?: AlgorithmIdentifierSchema; 66 issuer?: RelativeDistinguishedNamesSchema; 67 tbsCertificateValidity?: string; 68 notBefore?: TimeSchema; 69 notAfter?: TimeSchema; 70 subject?: RelativeDistinguishedNamesSchema; 71 subjectPublicKeyInfo?: PublicKeyInfoSchema; 72 tbsCertificateIssuerUniqueID?: string; 73 tbsCertificateSubjectUniqueID?: string; 74 extensions?: ExtensionsSchema; 75 }>; 76 77 function tbsCertificate(parameters: TBSCertificateSchema = {}): Schema.SchemaType { 78 //TBSCertificate ::= SEQUENCE { 79 // version [0] EXPLICIT Version DEFAULT v1, 80 // serialNumber CertificateSerialNumber, 81 // signature AlgorithmIdentifier, 82 // issuer Name, 83 // validity Validity, 84 // subject Name, 85 // subjectPublicKeyInfo SubjectPublicKeyInfo, 86 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 87 // -- If present, version MUST be v2 or v3 88 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 89 // -- If present, version MUST be v2 or v3 90 // extensions [3] EXPLICIT Extensions OPTIONAL 91 // -- If present, version MUST be v3 92 //} 93 94 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 95 96 return (new asn1js.Sequence({ 97 name: (names.blockName || TBS_CERTIFICATE), 98 value: [ 99 new asn1js.Constructed({ 100 optional: true, 101 idBlock: { 102 tagClass: 3, // CONTEXT-SPECIFIC 103 tagNumber: 0 // [0] 104 }, 105 value: [ 106 new asn1js.Integer({ name: (names.tbsCertificateVersion || TBS_CERTIFICATE_VERSION) }) // EXPLICIT integer value 107 ] 108 }), 109 new asn1js.Integer({ name: (names.tbsCertificateSerialNumber || TBS_CERTIFICATE_SERIAL_NUMBER) }), 110 AlgorithmIdentifier.schema(names.signature || { 111 names: { 112 blockName: TBS_CERTIFICATE_SIGNATURE 113 } 114 }), 115 RelativeDistinguishedNames.schema(names.issuer || { 116 names: { 117 blockName: TBS_CERTIFICATE_ISSUER 118 } 119 }), 120 new asn1js.Sequence({ 121 name: (names.tbsCertificateValidity || "tbsCertificate.validity"), 122 value: [ 123 Time.schema(names.notBefore || { 124 names: { 125 utcTimeName: TBS_CERTIFICATE_NOT_BEFORE, 126 generalTimeName: TBS_CERTIFICATE_NOT_BEFORE 127 } 128 }), 129 Time.schema(names.notAfter || { 130 names: { 131 utcTimeName: TBS_CERTIFICATE_NOT_AFTER, 132 generalTimeName: TBS_CERTIFICATE_NOT_AFTER 133 } 134 }) 135 ] 136 }), 137 RelativeDistinguishedNames.schema(names.subject || { 138 names: { 139 blockName: TBS_CERTIFICATE_SUBJECT 140 } 141 }), 142 PublicKeyInfo.schema(names.subjectPublicKeyInfo || { 143 names: { 144 blockName: TBS_CERTIFICATE_SUBJECT_PUBLIC_KEY 145 } 146 }), 147 new asn1js.Primitive({ 148 name: (names.tbsCertificateIssuerUniqueID || TBS_CERTIFICATE_ISSUER_UNIQUE_ID), 149 optional: true, 150 idBlock: { 151 tagClass: 3, // CONTEXT-SPECIFIC 152 tagNumber: 1 // [1] 153 } 154 }), // IMPLICIT BIT_STRING value 155 new asn1js.Primitive({ 156 name: (names.tbsCertificateSubjectUniqueID || TBS_CERTIFICATE_SUBJECT_UNIQUE_ID), 157 optional: true, 158 idBlock: { 159 tagClass: 3, // CONTEXT-SPECIFIC 160 tagNumber: 2 // [2] 161 } 162 }), // IMPLICIT BIT_STRING value 163 new asn1js.Constructed({ 164 optional: true, 165 idBlock: { 166 tagClass: 3, // CONTEXT-SPECIFIC 167 tagNumber: 3 // [3] 168 }, 169 value: [Extensions.schema(names.extensions || { 170 names: { 171 blockName: TBS_CERTIFICATE_EXTENSIONS 172 } 173 })] 174 }) // EXPLICIT SEQUENCE value 175 ] 176 })); 177 } 178 179 export interface ICertificate { 180 /** 181 * ToBeSigned (TBS) part of the certificate 182 */ 183 tbs: ArrayBuffer; 184 /** 185 * Version number 186 */ 187 version: number; 188 /** 189 * Serial number of the certificate 190 */ 191 serialNumber: asn1js.Integer; 192 /** 193 * This field contains the algorithm identifier for the algorithm used by the CA to sign the certificate 194 */ 195 signature: AlgorithmIdentifier; 196 /** 197 * The issuer field identifies the entity that has signed and issued the certificate 198 */ 199 issuer: RelativeDistinguishedNames; 200 /** 201 * The date on which the certificate validity period begins 202 */ 203 notBefore: Time; 204 /** 205 * The date on which the certificate validity period ends 206 */ 207 notAfter: Time; 208 /** 209 * The subject field identifies the entity associated with the public key stored in the subject public key field 210 */ 211 subject: RelativeDistinguishedNames; 212 /** 213 * This field is used to carry the public key and identify the algorithm with which the key is used 214 */ 215 subjectPublicKeyInfo: PublicKeyInfo; 216 /** 217 * The subject and issuer unique identifiers are present in the certificate to handle the possibility of reuse of subject and/or issuer names over time 218 */ 219 issuerUniqueID?: ArrayBuffer; 220 /** 221 * The subject and issuer unique identifiers are present in the certificate to handle the possibility of reuse of subject and/or issuer names over time 222 */ 223 subjectUniqueID?: ArrayBuffer; 224 /** 225 * If present, this field is a SEQUENCE of one or more certificate extensions 226 */ 227 extensions?: Extension[]; 228 /** 229 * The signatureAlgorithm field contains the identifier for the cryptographic algorithm used by the CA to sign this certificate 230 */ 231 signatureAlgorithm: AlgorithmIdentifier; 232 /** 233 * The signatureValue field contains a digital signature computed upon the ASN.1 DER encoded tbsCertificate 234 */ 235 signatureValue: asn1js.BitString; 236 } 237 238 /** 239 * Constructor parameters for the {@link Certificate} class 240 */ 241 export type CertificateParameters = PkiObjectParameters & Partial<ICertificate>; 242 243 /** 244 * Parameters for {@link Certificate} schema generation 245 */ 246 export type CertificateSchema = Schema.SchemaParameters<{ 247 tbsCertificate?: TBSCertificateSchema; 248 signatureAlgorithm?: AlgorithmIdentifierSchema; 249 signatureValue?: string; 250 }>; 251 252 export interface CertificateJson { 253 tbs: string; 254 version: number; 255 serialNumber: asn1js.IntegerJson; 256 signature: AlgorithmIdentifierJson; 257 issuer: RelativeDistinguishedNamesJson; 258 notBefore: TimeJson; 259 notAfter: TimeJson; 260 subject: RelativeDistinguishedNamesJson; 261 subjectPublicKeyInfo: PublicKeyInfoJson | JsonWebKey; 262 issuerUniqueID?: string; 263 subjectUniqueID?: string; 264 extensions?: ExtensionJson[]; 265 signatureAlgorithm: AlgorithmIdentifierJson; 266 signatureValue: asn1js.BitStringJson; 267 } 268 269 /** 270 * Represents an X.509 certificate described in [RFC5280 Section 4](https://datatracker.ietf.org/doc/html/rfc5280#section-4). 271 * 272 * @example The following example demonstrates how to parse X.509 Certificate 273 * ```js 274 * const asn1 = asn1js.fromBER(raw); 275 * if (asn1.offset === -1) { 276 * throw new Error("Incorrect encoded ASN.1 data"); 277 * } 278 * 279 * const cert = new pkijs.Certificate({ schema: asn1.result }); 280 * ``` 281 * 282 * @example The following example demonstrates how to create self-signed certificate 283 * ```js 284 * const crypto = pkijs.getCrypto(true); 285 * 286 * // Create certificate 287 * const certificate = new pkijs.Certificate(); 288 * certificate.version = 2; 289 * certificate.serialNumber = new asn1js.Integer({ value: 1 }); 290 * certificate.issuer.typesAndValues.push(new pkijs.AttributeTypeAndValue({ 291 * type: "2.5.4.3", // Common name 292 * value: new asn1js.BmpString({ value: "Test" }) 293 * })); 294 * certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ 295 * type: "2.5.4.3", // Common name 296 * value: new asn1js.BmpString({ value: "Test" }) 297 * })); 298 * 299 * certificate.notBefore.value = new Date(); 300 * const notAfter = new Date(); 301 * notAfter.setUTCFullYear(notAfter.getUTCFullYear() + 1); 302 * certificate.notAfter.value = notAfter; 303 * 304 * certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array 305 * 306 * // "BasicConstraints" extension 307 * const basicConstr = new pkijs.BasicConstraints({ 308 * cA: true, 309 * pathLenConstraint: 3 310 * }); 311 * certificate.extensions.push(new pkijs.Extension({ 312 * extnID: "2.5.29.19", 313 * critical: false, 314 * extnValue: basicConstr.toSchema().toBER(false), 315 * parsedValue: basicConstr // Parsed value for well-known extensions 316 * })); 317 * 318 * // "KeyUsage" extension 319 * const bitArray = new ArrayBuffer(1); 320 * const bitView = new Uint8Array(bitArray); 321 * bitView[0] |= 0x02; // Key usage "cRLSign" flag 322 * bitView[0] |= 0x04; // Key usage "keyCertSign" flag 323 * const keyUsage = new asn1js.BitString({ valueHex: bitArray }); 324 * certificate.extensions.push(new pkijs.Extension({ 325 * extnID: "2.5.29.15", 326 * critical: false, 327 * extnValue: keyUsage.toBER(false), 328 * parsedValue: keyUsage // Parsed value for well-known extensions 329 * })); 330 * 331 * const algorithm = pkijs.getAlgorithmParameters("RSASSA-PKCS1-v1_5", "generateKey"); 332 * if ("hash" in algorithm.algorithm) { 333 * algorithm.algorithm.hash.name = "SHA-256"; 334 * } 335 * 336 * const keys = await crypto.generateKey(algorithm.algorithm, true, algorithm.usages); 337 * 338 * // Exporting public key into "subjectPublicKeyInfo" value of certificate 339 * await certificate.subjectPublicKeyInfo.importKey(keys.publicKey); 340 * 341 * // Signing final certificate 342 * await certificate.sign(keys.privateKey, "SHA-256"); 343 * 344 * const raw = certificate.toSchema().toBER(); 345 * ``` 346 */ 347 export class Certificate extends PkiObject implements ICertificate { 348 349 public static override CLASS_NAME = "Certificate"; 350 351 public tbsView!: Uint8Array; 352 /** 353 * @deprecated Since version 3.0.0 354 */ 355 public get tbs(): ArrayBuffer { 356 return pvtsutils.BufferSourceConverter.toArrayBuffer(this.tbsView); 357 } 358 359 /** 360 * @deprecated Since version 3.0.0 361 */ 362 public set tbs(value: ArrayBuffer) { 363 this.tbsView = new Uint8Array(value); 364 } 365 366 public version!: number; 367 public serialNumber!: asn1js.Integer; 368 public signature!: AlgorithmIdentifier; 369 public issuer!: RelativeDistinguishedNames; 370 public notBefore!: Time; 371 public notAfter!: Time; 372 public subject!: RelativeDistinguishedNames; 373 public subjectPublicKeyInfo!: PublicKeyInfo; 374 public issuerUniqueID?: ArrayBuffer; 375 public subjectUniqueID?: ArrayBuffer; 376 public extensions?: Extension[]; 377 public signatureAlgorithm!: AlgorithmIdentifier; 378 public signatureValue!: asn1js.BitString; 379 380 /** 381 * Initializes a new instance of the {@link Certificate} class 382 * @param parameters Initialization parameters 383 */ 384 constructor(parameters: CertificateParameters = {}) { 385 super(); 386 387 this.tbsView = new Uint8Array(pvutils.getParametersValue(parameters, TBS, Certificate.defaultValues(TBS))); 388 this.version = pvutils.getParametersValue(parameters, VERSION, Certificate.defaultValues(VERSION)); 389 this.serialNumber = pvutils.getParametersValue(parameters, SERIAL_NUMBER, Certificate.defaultValues(SERIAL_NUMBER)); 390 this.signature = pvutils.getParametersValue(parameters, SIGNATURE, Certificate.defaultValues(SIGNATURE)); 391 this.issuer = pvutils.getParametersValue(parameters, ISSUER, Certificate.defaultValues(ISSUER)); 392 this.notBefore = pvutils.getParametersValue(parameters, NOT_BEFORE, Certificate.defaultValues(NOT_BEFORE)); 393 this.notAfter = pvutils.getParametersValue(parameters, NOT_AFTER, Certificate.defaultValues(NOT_AFTER)); 394 this.subject = pvutils.getParametersValue(parameters, SUBJECT, Certificate.defaultValues(SUBJECT)); 395 this.subjectPublicKeyInfo = pvutils.getParametersValue(parameters, SUBJECT_PUBLIC_KEY_INFO, Certificate.defaultValues(SUBJECT_PUBLIC_KEY_INFO)); 396 if (ISSUER_UNIQUE_ID in parameters) { 397 this.issuerUniqueID = pvutils.getParametersValue(parameters, ISSUER_UNIQUE_ID, Certificate.defaultValues(ISSUER_UNIQUE_ID)); 398 } 399 if (SUBJECT_UNIQUE_ID in parameters) { 400 this.subjectUniqueID = pvutils.getParametersValue(parameters, SUBJECT_UNIQUE_ID, Certificate.defaultValues(SUBJECT_UNIQUE_ID)); 401 } 402 if (EXTENSIONS in parameters) { 403 this.extensions = pvutils.getParametersValue(parameters, EXTENSIONS, Certificate.defaultValues(EXTENSIONS)); 404 } 405 this.signatureAlgorithm = pvutils.getParametersValue(parameters, SIGNATURE_ALGORITHM, Certificate.defaultValues(SIGNATURE_ALGORITHM)); 406 this.signatureValue = pvutils.getParametersValue(parameters, SIGNATURE_VALUE, Certificate.defaultValues(SIGNATURE_VALUE)); 407 408 if (parameters.schema) { 409 this.fromSchema(parameters.schema); 410 } 411 } 412 413 /** 414 * Return default values for all class members 415 * @param memberName String name for a class member 416 * @returns Predefined default value 417 */ 418 public static override defaultValues(memberName: typeof TBS): ArrayBuffer; 419 public static override defaultValues(memberName: typeof VERSION): number; 420 public static override defaultValues(memberName: typeof SERIAL_NUMBER): asn1js.Integer; 421 public static override defaultValues(memberName: typeof SIGNATURE): AlgorithmIdentifier; 422 public static override defaultValues(memberName: typeof ISSUER): RelativeDistinguishedNames; 423 public static override defaultValues(memberName: typeof NOT_BEFORE): Time; 424 public static override defaultValues(memberName: typeof NOT_AFTER): Time; 425 public static override defaultValues(memberName: typeof SUBJECT): RelativeDistinguishedNames; 426 public static override defaultValues(memberName: typeof SUBJECT_PUBLIC_KEY_INFO): PublicKeyInfo; 427 public static override defaultValues(memberName: typeof ISSUER_UNIQUE_ID): ArrayBuffer; 428 public static override defaultValues(memberName: typeof SUBJECT_UNIQUE_ID): ArrayBuffer; 429 public static override defaultValues(memberName: typeof EXTENSIONS): Extension[]; 430 public static override defaultValues(memberName: typeof SIGNATURE_ALGORITHM): AlgorithmIdentifier; 431 public static override defaultValues(memberName: typeof SIGNATURE_VALUE): asn1js.BitString; 432 public static override defaultValues(memberName: string): any { 433 switch (memberName) { 434 case TBS: 435 return EMPTY_BUFFER; 436 case VERSION: 437 return 0; 438 case SERIAL_NUMBER: 439 return new asn1js.Integer(); 440 case SIGNATURE: 441 return new AlgorithmIdentifier(); 442 case ISSUER: 443 return new RelativeDistinguishedNames(); 444 case NOT_BEFORE: 445 return new Time(); 446 case NOT_AFTER: 447 return new Time(); 448 case SUBJECT: 449 return new RelativeDistinguishedNames(); 450 case SUBJECT_PUBLIC_KEY_INFO: 451 return new PublicKeyInfo(); 452 case ISSUER_UNIQUE_ID: 453 return EMPTY_BUFFER; 454 case SUBJECT_UNIQUE_ID: 455 return EMPTY_BUFFER; 456 case EXTENSIONS: 457 return []; 458 case SIGNATURE_ALGORITHM: 459 return new AlgorithmIdentifier(); 460 case SIGNATURE_VALUE: 461 return new asn1js.BitString(); 462 default: 463 return super.defaultValues(memberName); 464 } 465 } 466 467 /** 468 * @inheritdoc 469 * @asn ASN.1 schema 470 * ```asn 471 * Certificate ::= SEQUENCE { 472 * tbsCertificate TBSCertificate, 473 * signatureAlgorithm AlgorithmIdentifier, 474 * signatureValue BIT STRING } 475 * 476 * TBSCertificate ::= SEQUENCE { 477 * version [0] EXPLICIT Version DEFAULT v1, 478 * serialNumber CertificateSerialNumber, 479 * signature AlgorithmIdentifier, 480 * issuer Name, 481 * validity Validity, 482 * subject Name, 483 * subjectPublicKeyInfo SubjectPublicKeyInfo, 484 * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 485 * -- If present, version MUST be v2 or v3 486 * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 487 * -- If present, version MUST be v2 or v3 488 * extensions [3] EXPLICIT Extensions OPTIONAL 489 * -- If present, version MUST be v3 490 * } 491 * 492 * Version ::= INTEGER { v1(0), v2(1), v3(2) } 493 * 494 * CertificateSerialNumber ::= INTEGER 495 * 496 * Validity ::= SEQUENCE { 497 * notBefore Time, 498 * notAfter Time } 499 * 500 * Time ::= CHOICE { 501 * utcTime UTCTime, 502 * generalTime GeneralizedTime } 503 * 504 * UniqueIdentifier ::= BIT STRING 505 * 506 * SubjectPublicKeyInfo ::= SEQUENCE { 507 * algorithm AlgorithmIdentifier, 508 * subjectPublicKey BIT STRING } 509 * 510 * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension 511 * 512 * Extension ::= SEQUENCE { 513 * extnID OBJECT IDENTIFIER, 514 * critical BOOLEAN DEFAULT FALSE, 515 * extnValue OCTET STRING 516 * -- contains the DER encoding of an ASN.1 value 517 * -- corresponding to the extension type identified 518 * -- by extnID 519 * } 520 *``` 521 */ 522 public static override schema(parameters: CertificateSchema = {}): Schema.SchemaType { 523 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 524 525 return (new asn1js.Sequence({ 526 name: (names.blockName || EMPTY_STRING), 527 value: [ 528 tbsCertificate(names.tbsCertificate), 529 AlgorithmIdentifier.schema(names.signatureAlgorithm || { 530 names: { 531 blockName: SIGNATURE_ALGORITHM 532 } 533 }), 534 new asn1js.BitString({ name: (names.signatureValue || SIGNATURE_VALUE) }) 535 ] 536 })); 537 } 538 539 public fromSchema(schema: Schema.SchemaType): void { 540 // Clear input data first 541 pvutils.clearProps(schema, CLEAR_PROPS); 542 543 //#region Check the schema is valid 544 const asn1 = asn1js.compareSchema(schema, 545 schema, 546 Certificate.schema({ 547 names: { 548 tbsCertificate: { 549 names: { 550 extensions: { 551 names: { 552 extensions: TBS_CERTIFICATE_EXTENSIONS 553 } 554 } 555 } 556 } 557 } 558 }) 559 ); 560 AsnError.assertSchema(asn1, this.className); 561 //#endregion 562 563 //#region Get internal properties from parsed schema 564 this.tbsView = (asn1.result.tbsCertificate as asn1js.Sequence).valueBeforeDecodeView; 565 566 if (TBS_CERTIFICATE_VERSION in asn1.result) 567 this.version = asn1.result[TBS_CERTIFICATE_VERSION].valueBlock.valueDec; 568 this.serialNumber = asn1.result[TBS_CERTIFICATE_SERIAL_NUMBER]; 569 this.signature = new AlgorithmIdentifier({ schema: asn1.result[TBS_CERTIFICATE_SIGNATURE] }); 570 this.issuer = new RelativeDistinguishedNames({ schema: asn1.result[TBS_CERTIFICATE_ISSUER] }); 571 this.notBefore = new Time({ schema: asn1.result[TBS_CERTIFICATE_NOT_BEFORE] }); 572 this.notAfter = new Time({ schema: asn1.result[TBS_CERTIFICATE_NOT_AFTER] }); 573 this.subject = new RelativeDistinguishedNames({ schema: asn1.result[TBS_CERTIFICATE_SUBJECT] }); 574 this.subjectPublicKeyInfo = new PublicKeyInfo({ schema: asn1.result[TBS_CERTIFICATE_SUBJECT_PUBLIC_KEY] }); 575 if (TBS_CERTIFICATE_ISSUER_UNIQUE_ID in asn1.result) 576 this.issuerUniqueID = asn1.result[TBS_CERTIFICATE_ISSUER_UNIQUE_ID].valueBlock.valueHex; 577 if (TBS_CERTIFICATE_SUBJECT_UNIQUE_ID in asn1.result) 578 this.subjectUniqueID = asn1.result[TBS_CERTIFICATE_SUBJECT_UNIQUE_ID].valueBlock.valueHex; 579 if (TBS_CERTIFICATE_EXTENSIONS in asn1.result) 580 this.extensions = Array.from(asn1.result[TBS_CERTIFICATE_EXTENSIONS], element => new Extension({ schema: element })); 581 582 this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.signatureAlgorithm }); 583 this.signatureValue = asn1.result.signatureValue; 584 //#endregion 585 } 586 587 /** 588 * Creates ASN.1 schema for existing values of TBS part for the certificate 589 * @returns ASN.1 SEQUENCE 590 */ 591 public encodeTBS(): asn1js.Sequence { 592 //#region Create array for output sequence 593 const outputArray = []; 594 595 if ((VERSION in this) && (this.version !== Certificate.defaultValues(VERSION))) { 596 outputArray.push(new asn1js.Constructed({ 597 optional: true, 598 idBlock: { 599 tagClass: 3, // CONTEXT-SPECIFIC 600 tagNumber: 0 // [0] 601 }, 602 value: [ 603 new asn1js.Integer({ value: this.version }) // EXPLICIT integer value 604 ] 605 })); 606 } 607 608 outputArray.push(this.serialNumber); 609 outputArray.push(this.signature.toSchema()); 610 outputArray.push(this.issuer.toSchema()); 611 612 outputArray.push(new asn1js.Sequence({ 613 value: [ 614 this.notBefore.toSchema(), 615 this.notAfter.toSchema() 616 ] 617 })); 618 619 outputArray.push(this.subject.toSchema()); 620 outputArray.push(this.subjectPublicKeyInfo.toSchema()); 621 622 if (this.issuerUniqueID) { 623 outputArray.push(new asn1js.Primitive({ 624 optional: true, 625 idBlock: { 626 tagClass: 3, // CONTEXT-SPECIFIC 627 tagNumber: 1 // [1] 628 }, 629 valueHex: this.issuerUniqueID 630 })); 631 } 632 if (this.subjectUniqueID) { 633 outputArray.push(new asn1js.Primitive({ 634 optional: true, 635 idBlock: { 636 tagClass: 3, // CONTEXT-SPECIFIC 637 tagNumber: 2 // [2] 638 }, 639 valueHex: this.subjectUniqueID 640 })); 641 } 642 643 if (this.extensions) { 644 outputArray.push(new asn1js.Constructed({ 645 optional: true, 646 idBlock: { 647 tagClass: 3, // CONTEXT-SPECIFIC 648 tagNumber: 3 // [3] 649 }, 650 value: [new asn1js.Sequence({ 651 value: Array.from(this.extensions, o => o.toSchema()) 652 })] 653 })); 654 } 655 //#endregion 656 657 //#region Create and return output sequence 658 return (new asn1js.Sequence({ 659 value: outputArray 660 })); 661 //#endregion 662 } 663 664 public toSchema(encodeFlag = false): asn1js.Sequence { 665 let tbsSchema: asn1js.AsnType; 666 667 // Decode stored TBS value 668 if (encodeFlag === false) { 669 if (!this.tbsView.byteLength) { // No stored certificate TBS part 670 return Certificate.schema().value[0]; 671 } 672 673 const asn1 = asn1js.fromBER(this.tbsView); 674 AsnError.assert(asn1, "TBS Certificate"); 675 676 tbsSchema = asn1.result; 677 } else { 678 // Create TBS schema via assembling from TBS parts 679 tbsSchema = this.encodeTBS(); 680 } 681 682 // Construct and return new ASN.1 schema for this object 683 return (new asn1js.Sequence({ 684 value: [ 685 tbsSchema, 686 this.signatureAlgorithm.toSchema(), 687 this.signatureValue 688 ] 689 })); 690 } 691 692 public toJSON(): CertificateJson { 693 const res: CertificateJson = { 694 tbs: pvtsutils.Convert.ToHex(this.tbsView), 695 version: this.version, 696 serialNumber: this.serialNumber.toJSON(), 697 signature: this.signature.toJSON(), 698 issuer: this.issuer.toJSON(), 699 notBefore: this.notBefore.toJSON(), 700 notAfter: this.notAfter.toJSON(), 701 subject: this.subject.toJSON(), 702 subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(), 703 signatureAlgorithm: this.signatureAlgorithm.toJSON(), 704 signatureValue: this.signatureValue.toJSON(), 705 }; 706 707 if ((VERSION in this) && (this.version !== Certificate.defaultValues(VERSION))) { 708 res.version = this.version; 709 } 710 711 if (this.issuerUniqueID) { 712 res.issuerUniqueID = pvtsutils.Convert.ToHex(this.issuerUniqueID); 713 } 714 715 if (this.subjectUniqueID) { 716 res.subjectUniqueID = pvtsutils.Convert.ToHex(this.subjectUniqueID); 717 } 718 719 if (this.extensions) { 720 res.extensions = Array.from(this.extensions, o => o.toJSON()); 721 } 722 723 return res; 724 } 725 726 /** 727 * Importing public key for current certificate 728 * @param parameters Public key export parameters 729 * @param crypto Crypto engine 730 * @returns WebCrypto public key 731 */ 732 public async getPublicKey(parameters?: CryptoEnginePublicKeyParams, crypto = common.getCrypto(true)): Promise<CryptoKey> { 733 return crypto.getPublicKey(this.subjectPublicKeyInfo, this.signatureAlgorithm, parameters); 734 } 735 736 /** 737 * Get hash value for subject public key (default SHA-1) 738 * @param hashAlgorithm Hashing algorithm name 739 * @param crypto Crypto engine 740 * @returns Computed hash value from `Certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey` 741 */ 742 public async getKeyHash(hashAlgorithm = "SHA-1", crypto = common.getCrypto(true)): Promise<ArrayBuffer> { 743 return crypto.digest({ name: hashAlgorithm }, this.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView as BufferSource); 744 } 745 746 /** 747 * Make a signature for current value from TBS section 748 * @param privateKey Private key for SUBJECT_PUBLIC_KEY_INFO structure 749 * @param hashAlgorithm Hashing algorithm 750 * @param crypto Crypto engine 751 */ 752 public async sign(privateKey: CryptoKey, hashAlgorithm = "SHA-1", crypto = common.getCrypto(true)): Promise<void> { 753 // Initial checking 754 if (!privateKey) { 755 throw new Error("Need to provide a private key for signing"); 756 } 757 758 // Get a "default parameters" for current algorithm and set correct signature algorithm 759 const signatureParameters = await crypto.getSignatureParameters(privateKey, hashAlgorithm); 760 const parameters = signatureParameters.parameters; 761 this.signature = signatureParameters.signatureAlgorithm; 762 this.signatureAlgorithm = signatureParameters.signatureAlgorithm; 763 764 // Create TBS data for signing 765 this.tbsView = new Uint8Array(this.encodeTBS().toBER()); 766 767 // Signing TBS data on provided private key 768 // TODO remove any 769 const signature = await crypto.signWithPrivateKey(this.tbsView as BufferSource, privateKey, parameters as any); 770 this.signatureValue = new asn1js.BitString({ valueHex: signature }); 771 } 772 773 /** 774 * Verifies the certificate signature 775 * @param issuerCertificate 776 * @param crypto Crypto engine 777 */ 778 public async verify(issuerCertificate?: Certificate, crypto = common.getCrypto(true)): Promise<boolean> { 779 let subjectPublicKeyInfo: PublicKeyInfo | undefined; 780 781 // Set correct SUBJECT_PUBLIC_KEY_INFO value 782 if (issuerCertificate) { 783 subjectPublicKeyInfo = issuerCertificate.subjectPublicKeyInfo; 784 } else if (this.issuer.isEqual(this.subject)) { 785 // Self-signed certificate 786 subjectPublicKeyInfo = this.subjectPublicKeyInfo; 787 } 788 789 if (!(subjectPublicKeyInfo instanceof PublicKeyInfo)) { 790 throw new Error("Please provide issuer certificate as a parameter"); 791 } 792 793 return crypto.verifyWithPublicKey(this.tbsView as BufferSource, this.signatureValue, subjectPublicKeyInfo, this.signatureAlgorithm); 794 } 795 796 } 797 798 /** 799 * Check CA flag for the certificate 800 * @param cert Certificate to find CA flag for 801 * @returns Returns {@link Certificate} if `cert` is CA certificate otherwise return `null` 802 */ 803 export function checkCA(cert: Certificate, signerCert: Certificate | null = null): Certificate | null { 804 //#region Do not include signer's certificate 805 if (signerCert && cert.issuer.isEqual(signerCert.issuer) && cert.serialNumber.isEqual(signerCert.serialNumber)) { 806 return null; 807 } 808 //#endregion 809 810 let isCA = false; 811 812 if (cert.extensions) { 813 for (const extension of cert.extensions) { 814 if (extension.extnID === id_BasicConstraints && extension.parsedValue instanceof BasicConstraints) { 815 if (extension.parsedValue.cA) { 816 isCA = true; 817 break; 818 } 819 } 820 } 821 } 822 823 if (isCA) { 824 return cert; 825 } 826 827 return null; 828 }