tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }