Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/js/PKI.js/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 33 kB image not shown  

Quelle  SignedData.ts   Sprache: unbekannt

 
import * as asn1js from "asn1js";
import * as pvtsutils from "pvtsutils";
import * as pvutils from "pvutils";
import * as common from "./common";
import { AlgorithmIdentifier, AlgorithmIdentifierJson } from "./AlgorithmIdentifier";
import { EncapsulatedContentInfo, EncapsulatedContentInfoJson, EncapsulatedContentInfoSchema } from "./EncapsulatedContentInfo";
import { Certificate, checkCA } from "./Certificate";
import { CertificateRevocationList, CertificateRevocationListJson } from "./CertificateRevocationList";
import { OtherRevocationInfoFormat, OtherRevocationInfoFormatJson } from "./OtherRevocationInfoFormat";
import { SignerInfo, SignerInfoJson } from "./SignerInfo";
import { CertificateSet, CertificateSetItem, CertificateSetItemJson } from "./CertificateSet";
import { RevocationInfoChoices, RevocationInfoChoicesSchema } from "./RevocationInfoChoices";
import { IssuerAndSerialNumber } from "./IssuerAndSerialNumber";
import { TSTInfo } from "./TSTInfo";
import { CertificateChainValidationEngine, CertificateChainValidationEngineParameters, FindIssuerCallback, FindOriginCallback } from "./CertificateChainValidationEngine";
import { BasicOCSPResponse, BasicOCSPResponseJson } from "./BasicOCSPResponse";
import { OtherCertificateFormat } from "./OtherCertificateFormat";
import { AttributeCertificateV1 } from "./AttributeCertificateV1";
import { AttributeCertificateV2 } from "./AttributeCertificateV2";
import * as Schema from "./Schema";
import { id_ContentType_Data, id_eContentType_TSTInfo, id_PKIX_OCSP_Basic } from "./ObjectIdentifiers";
import { AsnError } from "./errors";
import { PkiObject, PkiObjectParameters } from "./PkiObject";
import { EMPTY_BUFFER, EMPTY_STRING } from "./constants";
import { ICryptoEngine } from "./CryptoEngine/CryptoEngineInterface";

export type SignedDataCRL = CertificateRevocationList | OtherRevocationInfoFormat;
export type SignedDataCRLJson = CertificateRevocationListJson | OtherRevocationInfoFormatJson;

const VERSION = "version";
const DIGEST_ALGORITHMS = "digestAlgorithms";
const ENCAP_CONTENT_INFO = "encapContentInfo";
const CERTIFICATES = "certificates";
const CRLS = "crls";
const SIGNER_INFOS = "signerInfos";
const OCSPS = "ocsps";
const SIGNED_DATA = "SignedData";
const SIGNED_DATA_VERSION = `${SIGNED_DATA}.${VERSION}`;
const SIGNED_DATA_DIGEST_ALGORITHMS = `${SIGNED_DATA}.${DIGEST_ALGORITHMS}`;
const SIGNED_DATA_ENCAP_CONTENT_INFO = `${SIGNED_DATA}.${ENCAP_CONTENT_INFO}`;
const SIGNED_DATA_CERTIFICATES = `${SIGNED_DATA}.${CERTIFICATES}`;
const SIGNED_DATA_CRLS = `${SIGNED_DATA}.${CRLS}`;
const SIGNED_DATA_SIGNER_INFOS = `${SIGNED_DATA}.${SIGNER_INFOS}`;
const CLEAR_PROPS = [
  SIGNED_DATA_VERSION,
  SIGNED_DATA_DIGEST_ALGORITHMS,
  SIGNED_DATA_ENCAP_CONTENT_INFO,
  SIGNED_DATA_CERTIFICATES,
  SIGNED_DATA_CRLS,
  SIGNED_DATA_SIGNER_INFOS
];

export interface ISignedData {
  version: number;
  digestAlgorithms: AlgorithmIdentifier[];
  encapContentInfo: EncapsulatedContentInfo;
  certificates?: CertificateSetItem[];
  crls?: SignedDataCRL[];
  ocsps?: BasicOCSPResponse[];
  signerInfos: SignerInfo[];
}

export interface SignedDataJson {
  version: number;
  digestAlgorithms: AlgorithmIdentifierJson[];
  encapContentInfo: EncapsulatedContentInfoJson;
  certificates?: CertificateSetItemJson[];
  crls?: SignedDataCRLJson[];
  ocsps?: BasicOCSPResponseJson[];
  signerInfos: SignerInfoJson[];
}

export type SignedDataParameters = PkiObjectParameters & Partial<ISignedData>;

export interface SignedDataVerifyParams {
  signer?: number;
  data?: ArrayBuffer;
  trustedCerts?: Certificate[];
  checkDate?: Date;
  checkChain?: boolean;
  passedWhenNotRevValues?: boolean;
  extendedMode?: boolean;
  findOrigin?: FindOriginCallback | null;
  findIssuer?: FindIssuerCallback | null;
}

export interface SignedDataVerifyErrorParams {
  message: string;
  date?: Date;
  code?: number;
  timestampSerial?: ArrayBuffer | null;
  signatureVerified?: boolean | null;
  signerCertificate?: Certificate | null;
  signerCertificateVerified?: boolean | null;
  certificatePath?: Certificate[];
}

export interface SignedDataVerifyResult {
  message: string;
  date?: Date;
  code?: number;
  timestampSerial?: ArrayBuffer | null;
  signatureVerified?: boolean | null;
  signerCertificate?: Certificate | null;
  signerCertificateVerified?: boolean | null;
  certificatePath: Certificate[];
}

export class SignedDataVerifyError extends Error implements SignedDataVerifyResult {

  public date: Date;
  public code: number;
  public signatureVerified: boolean | null;
  public signerCertificate: Certificate | null;
  public signerCertificateVerified: boolean | null;
  public timestampSerial: ArrayBuffer | null;
  public certificatePath: Certificate[];

  constructor({
    message,
    code = 0,
    date = new Date(),
    signatureVerified = null,
    signerCertificate = null,
    signerCertificateVerified = null,
    timestampSerial = null,
    certificatePath = [],
  }: SignedDataVerifyErrorParams) {
    super(message);
    this.name = "SignedDataVerifyError";

    this.date = date;
    this.code = code;
    this.timestampSerial = timestampSerial;
    this.signatureVerified = signatureVerified;
    this.signerCertificate = signerCertificate;
    this.signerCertificateVerified = signerCertificateVerified;
    this.certificatePath = certificatePath;

  }
}

/**
 * Represents the SignedData structure described in [RFC5652](https://datatracker.ietf.org/doc/html/rfc5652)
 *
 * @example The following example demonstrates how to create and sign CMS Signed Data
 * ```js
 * // Create a new CMS Signed Data
 * const cmsSigned = new pkijs.SignedData({
 *   encapContentInfo: new pkijs.EncapsulatedContentInfo({
 *     eContentType: pkijs.ContentInfo.DATA,, // "data" content type
 *     eContent: new asn1js.OctetString({ valueHex: buffer })
 *   }),
 *   signerInfos: [
 *     new pkijs.SignerInfo({
 *       sid: new pkijs.IssuerAndSerialNumber({
 *         issuer: cert.issuer,
 *         serialNumber: cert.serialNumber
 *       })
 *     })
 *   ],
 *   // Signer certificate for chain validation
 *   certificates: [cert]
 * });
 *
 * await cmsSigned.sign(keys.privateKey, 0, "SHA-256");
 *
 * // Add Signed Data to Content Info
 * const cms = new pkijs.ContentInfo({
 *   contentType: pkijs.ContentInfo.SIGNED_DATA,,
 *   content: cmsSigned.toSchema(true),
 * });
 *
 * // Encode CMS to ASN.1
 * const cmsRaw = cms.toSchema().toBER();
 * ```
 *
 * @example The following example demonstrates how to verify CMS Signed Data
 * ```js
 * // Parse CMS and detect it's Signed Data
 * const cms = pkijs.ContentInfo.fromBER(cmsRaw);
 * if (cms.contentType !== pkijs.ContentInfo.SIGNED_DATA) {
 *   throw new Error("CMS is not Signed Data");
 * }
 *
 * // Read Signed Data
 * const signedData = new pkijs.SignedData({ schema: cms.content });
 *
 * // Verify Signed Data signature
 * const ok = await signedData.verify({
 *   signer: 0,
 *   checkChain: true,
 *   trustedCerts: [trustedCert],
 * });
 *
 * if (!ok) {
 *   throw new Error("CMS signature is invalid")
 * }
 * ```
 */
export class SignedData extends PkiObject implements ISignedData {

  public static override CLASS_NAME = "SignedData";

  public static ID_DATA: typeof id_ContentType_Data = id_ContentType_Data;

  public version!: number;
  public digestAlgorithms!: AlgorithmIdentifier[];
  public encapContentInfo!: EncapsulatedContentInfo;
  public certificates?: CertificateSetItem[];
  public crls?: SignedDataCRL[];
  public ocsps?: BasicOCSPResponse[];
  public signerInfos!: SignerInfo[];

  /**
   * Initializes a new instance of the {@link SignedData} class
   * @param parameters Initialization parameters
   */
  constructor(parameters: SignedDataParameters = {}) {
    super();

    this.version = pvutils.getParametersValue(parameters, VERSION, SignedData.defaultValues(VERSION));
    this.digestAlgorithms = pvutils.getParametersValue(parameters, DIGEST_ALGORITHMS, SignedData.defaultValues(DIGEST_ALGORITHMS));
    this.encapContentInfo = pvutils.getParametersValue(parameters, ENCAP_CONTENT_INFO, SignedData.defaultValues(ENCAP_CONTENT_INFO));
    if (CERTIFICATES in parameters) {
      this.certificates = pvutils.getParametersValue(parameters, CERTIFICATES, SignedData.defaultValues(CERTIFICATES));
    }
    if (CRLS in parameters) {
      this.crls = pvutils.getParametersValue(parameters, CRLS, SignedData.defaultValues(CRLS));
    }
    if (OCSPS in parameters) {
      this.ocsps = pvutils.getParametersValue(parameters, OCSPS, SignedData.defaultValues(OCSPS));
    }
    this.signerInfos = pvutils.getParametersValue(parameters, SIGNER_INFOS, SignedData.defaultValues(SIGNER_INFOS));

    if (parameters.schema) {
      this.fromSchema(parameters.schema);
    }
  }

  /**
   * Returns default values for all class members
   * @param memberName String name for a class member
   * @returns Default value
   */
  public static override defaultValues(memberName: typeof VERSION): number;
  public static override defaultValues(memberName: typeof DIGEST_ALGORITHMS): AlgorithmIdentifier[];
  public static override defaultValues(memberName: typeof ENCAP_CONTENT_INFO): EncapsulatedContentInfo;
  public static override defaultValues(memberName: typeof CERTIFICATES): CertificateSetItem[];
  public static override defaultValues(memberName: typeof CRLS): SignedDataCRL[];
  public static override defaultValues(memberName: typeof OCSPS): BasicOCSPResponse[];
  public static override defaultValues(memberName: typeof SIGNER_INFOS): SignerInfo[];
  public static override defaultValues(memberName: string): any {
    switch (memberName) {
      case VERSION:
        return 0;
      case DIGEST_ALGORITHMS:
        return [];
      case ENCAP_CONTENT_INFO:
        return new EncapsulatedContentInfo();
      case CERTIFICATES:
        return [];
      case CRLS:
        return [];
      case OCSPS:
        return [];
      case SIGNER_INFOS:
        return [];
      default:
        return super.defaultValues(memberName);
    }
  }

  /**
   * Compare values with default values for all class members
   * @param memberName String name for a class member
   * @param memberValue Value to compare with default value
   */
  public static compareWithDefault(memberName: string, memberValue: any): boolean {
    switch (memberName) {
      case VERSION:
        return (memberValue === SignedData.defaultValues(VERSION));
      case ENCAP_CONTENT_INFO:
        return EncapsulatedContentInfo.compareWithDefault("eContentType", memberValue.eContentType) &&
          EncapsulatedContentInfo.compareWithDefault("eContent", memberValue.eContent);
      case DIGEST_ALGORITHMS:
      case CERTIFICATES:
      case CRLS:
      case OCSPS:
      case SIGNER_INFOS:
        return (memberValue.length === 0);
      default:
        return super.defaultValues(memberName);
    }
  }

  /**
   * @inheritdoc
   * @asn ASN.1 schema
   * ```asn
   * SignedData ::= SEQUENCE {
   *    version CMSVersion,
   *    digestAlgorithms DigestAlgorithmIdentifiers,
   *    encapContentInfo EncapsulatedContentInfo,
   *    certificates [0] IMPLICIT CertificateSet OPTIONAL,
   *    crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
   *    signerInfos SignerInfos }
   *```
   */
  public static override schema(parameters: Schema.SchemaParameters<{
    version?: string;
    digestAlgorithms?: string;
    encapContentInfo?: EncapsulatedContentInfoSchema;
    certificates?: string;
    crls?: RevocationInfoChoicesSchema;
    signerInfos?: string;
  }> = {}): Schema.SchemaType {
    const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});

    if (names.optional === undefined) {
      names.optional = false;
    }

    return (new asn1js.Sequence({
      name: (names.blockName || SIGNED_DATA),
      optional: names.optional,
      value: [
        new asn1js.Integer({ name: (names.version || SIGNED_DATA_VERSION) }),
        new asn1js.Set({
          value: [
            new asn1js.Repeated({
              name: (names.digestAlgorithms || SIGNED_DATA_DIGEST_ALGORITHMS),
              value: AlgorithmIdentifier.schema()
            })
          ]
        }),
        EncapsulatedContentInfo.schema(names.encapContentInfo || {
          names: {
            blockName: SIGNED_DATA_ENCAP_CONTENT_INFO
          }
        }),
        new asn1js.Constructed({
          name: (names.certificates || SIGNED_DATA_CERTIFICATES),
          optional: true,
          idBlock: {
            tagClass: 3, // CONTEXT-SPECIFIC
            tagNumber: 0 // [0]
          },
          value: CertificateSet.schema().valueBlock.value
        }), // IMPLICIT CertificateSet
        new asn1js.Constructed({
          optional: true,
          idBlock: {
            tagClass: 3, // CONTEXT-SPECIFIC
            tagNumber: 1 // [1]
          },
          value: RevocationInfoChoices.schema(names.crls || {
            names: {
              crls: SIGNED_DATA_CRLS
            }
          }).valueBlock.value
        }), // IMPLICIT RevocationInfoChoices
        new asn1js.Set({
          value: [
            new asn1js.Repeated({
              name: (names.signerInfos || SIGNED_DATA_SIGNER_INFOS),
              value: SignerInfo.schema()
            })
          ]
        })
      ]
    }));
  }

  public fromSchema(schema: Schema.SchemaType): void {
    // Clear input data first
    pvutils.clearProps(schema, CLEAR_PROPS);

    //#region Check the schema is valid
    const asn1 = asn1js.compareSchema(schema,
      schema,
      SignedData.schema()
    );
    AsnError.assertSchema(asn1, this.className);
    //#endregion

    //#region Get internal properties from parsed schema
    this.version = asn1.result[SIGNED_DATA_VERSION].valueBlock.valueDec;

    if (SIGNED_DATA_DIGEST_ALGORITHMS in asn1.result) // Could be empty SET of digest algorithms
      this.digestAlgorithms = Array.from(asn1.result[SIGNED_DATA_DIGEST_ALGORITHMS], algorithm => new AlgorithmIdentifier({ schema: algorithm }));

    this.encapContentInfo = new EncapsulatedContentInfo({ schema: asn1.result[SIGNED_DATA_ENCAP_CONTENT_INFO] });

    if (SIGNED_DATA_CERTIFICATES in asn1.result) {
      const certificateSet = new CertificateSet({
        schema: new asn1js.Set({
          value: asn1.result[SIGNED_DATA_CERTIFICATES].valueBlock.value
        })
      });
      this.certificates = certificateSet.certificates.slice(0); // Copy all just for making comfortable access
    }

    if (SIGNED_DATA_CRLS in asn1.result) {
      this.crls = Array.from(asn1.result[SIGNED_DATA_CRLS], (crl: Schema.SchemaType) => {
        if (crl.idBlock.tagClass === 1)
          return new CertificateRevocationList({ schema: crl });

        //#region Create SEQUENCE from [1]
        crl.idBlock.tagClass = 1; // UNIVERSAL
        crl.idBlock.tagNumber = 16; // SEQUENCE
        //#endregion

        return new OtherRevocationInfoFormat({ schema: crl });
      });
    }

    if (SIGNED_DATA_SIGNER_INFOS in asn1.result) // Could be empty SET SignerInfos
      this.signerInfos = Array.from(asn1.result[SIGNED_DATA_SIGNER_INFOS], signerInfoSchema => new SignerInfo({ schema: signerInfoSchema }));
    //#endregion
  }

  public toSchema(encodeFlag = false): Schema.SchemaType {
    //#region Create array for output sequence
    const outputArray = [];

    // IF ((certificates is present) AND
    //  (any certificates with a type of other are present)) OR
    //  ((crls is present) AND
    //  (any crls with a type of other are present))
    // THEN version MUST be 5
    // ELSE
    //  IF (certificates is present) AND
    //    (any version 2 attribute certificates are present)
    //  THEN version MUST be 4
    //  ELSE
    //    IF ((certificates is present) AND
    //      (any version 1 attribute certificates are present)) OR
    //      (any SignerInfo structures are version 3) OR
    //      (encapContentInfo eContentType is other than id-data)
    //    THEN version MUST be 3
    //    ELSE version MUST be 1
    if ((this.certificates && this.certificates.length && this.certificates.some(o => o instanceof OtherCertificateFormat))
      || (this.crls && this.crls.length && this.crls.some(o => o instanceof OtherRevocationInfoFormat))) {
      this.version = 5;
    } else if (this.certificates && this.certificates.length && this.certificates.some(o => o instanceof AttributeCertificateV2)) {
      this.version = 4;
    } else if ((this.certificates && this.certificates.length && this.certificates.some(o => o instanceof AttributeCertificateV1))
      || this.signerInfos.some(o => o.version === 3)
      || this.encapContentInfo.eContentType !== SignedData.ID_DATA) {
      this.version = 3;
    } else {
      this.version = 1;
    }

    outputArray.push(new asn1js.Integer({ value: this.version }));

    //#region Create array of digest algorithms
    outputArray.push(new asn1js.Set({
      value: Array.from(this.digestAlgorithms, algorithm => algorithm.toSchema())
    }));
    //#endregion

    outputArray.push(this.encapContentInfo.toSchema());

    if (this.certificates) {
      const certificateSet = new CertificateSet({ certificates: this.certificates });
      const certificateSetSchema = certificateSet.toSchema();

      outputArray.push(new asn1js.Constructed({
        idBlock: {
          tagClass: 3,
          tagNumber: 0
        },
        value: certificateSetSchema.valueBlock.value
      }));
    }

    if (this.crls) {
      outputArray.push(new asn1js.Constructed({
        idBlock: {
          tagClass: 3, // CONTEXT-SPECIFIC
          tagNumber: 1 // [1]
        },
        value: Array.from(this.crls, crl => {
          if (crl instanceof OtherRevocationInfoFormat) {
            const crlSchema = crl.toSchema();

            crlSchema.idBlock.tagClass = 3;
            crlSchema.idBlock.tagNumber = 1;

            return crlSchema;
          }

          return crl.toSchema(encodeFlag);
        })
      }));
    }

    //#region Create array of signer infos
    outputArray.push(new asn1js.Set({
      value: Array.from(this.signerInfos, signerInfo => signerInfo.toSchema())
    }));
    //#endregion
    //#endregion

    //#region Construct and return new ASN.1 schema for this object
    return (new asn1js.Sequence({
      value: outputArray
    }));
    //#endregion
  }

  public toJSON(): SignedDataJson {
    const res: SignedDataJson = {
      version: this.version,
      digestAlgorithms: Array.from(this.digestAlgorithms, algorithm => algorithm.toJSON()),
      encapContentInfo: this.encapContentInfo.toJSON(),
      signerInfos: Array.from(this.signerInfos, signerInfo => signerInfo.toJSON()),
    };

    if (this.certificates) {
      res.certificates = Array.from(this.certificates, certificate => certificate.toJSON());
    }

    if (this.crls) {
      res.crls = Array.from(this.crls, crl => crl.toJSON());
    }


    return res;
  }

  public verify(params?: SignedDataVerifyParams & { extendedMode?: false; }, crypto?: ICryptoEngine): Promise<boolean>;
  public verify(params: SignedDataVerifyParams & { extendedMode: true; }, crypto?: ICryptoEngine): Promise<SignedDataVerifyResult>;
  public async verify({
    signer = (-1),
    data = (EMPTY_BUFFER),
    trustedCerts = [],
    checkDate = (new Date()),
    checkChain = false,
    passedWhenNotRevValues = false,
    extendedMode = false,
    findOrigin = null,
    findIssuer = null
  }: SignedDataVerifyParams = {}, crypto = common.getCrypto(true)): Promise<boolean | SignedDataVerifyResult> {
    let signerCert: Certificate | null = null;
    let timestampSerial: ArrayBuffer | null = null;
    try {
      //#region Global variables
      let messageDigestValue = EMPTY_BUFFER;
      let shaAlgorithm = EMPTY_STRING;
      let certificatePath: Certificate[] = [];
      //#endregion

      //#region Get a signer number
      const signerInfo = this.signerInfos[signer];
      if (!signerInfo) {
        throw new SignedDataVerifyError({
          date: checkDate,
          code: 1,
          message: "Unable to get signer by supplied index",
        });
      }
      //#endregion

      //#region Check that certificates field was included in signed data
      if (!this.certificates) {
        throw new SignedDataVerifyError({
          date: checkDate,
          code: 2,
          message: "No certificates attached to this signed data",
        });
      }
      //#endregion

      //#region Find a certificate for specified signer

      if (signerInfo.sid instanceof IssuerAndSerialNumber) {
        for (const certificate of this.certificates) {
          if (!(certificate instanceof Certificate))
            continue;

          if ((certificate.issuer.isEqual(signerInfo.sid.issuer)) &&
            (certificate.serialNumber.isEqual(signerInfo.sid.serialNumber))) {
            signerCert = certificate;
            break;
          }
        }
      } else { // Find by SubjectKeyIdentifier
        const sid = signerInfo.sid;
        const keyId = sid.idBlock.isConstructed
          ? sid.valueBlock.value[0].valueBlock.valueHex // EXPLICIT OCTET STRING
          : sid.valueBlock.valueHex; // IMPLICIT OCTET STRING

        for (const certificate of this.certificates) {
          if (!(certificate instanceof Certificate)) {
            continue;
          }

          const digest = await crypto.digest({ name: "sha-1" }, certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView);
          if (pvutils.isEqualBuffer(digest, keyId)) {
            signerCert = certificate;
            break;
          }
        }
      }

      if (!signerCert) {
        throw new SignedDataVerifyError({
          date: checkDate,
          code: 3,
          message: "Unable to find signer certificate",
        });
      }
      //#endregion

      //#region Verify internal digest in case of "tSTInfo" content type
      if (this.encapContentInfo.eContentType === id_eContentType_TSTInfo) {
        //#region Check "eContent" presence
        if (!this.encapContentInfo.eContent) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 15,
            message: "Error during verification: TSTInfo eContent is empty",
            signatureVerified: null,
            signerCertificate: signerCert,
            timestampSerial,
            signerCertificateVerified: true
          });
        }
        //#endregion

        //#region Initialize TST_INFO value
        let tstInfo: TSTInfo;

        try {
          tstInfo = TSTInfo.fromBER(this.encapContentInfo.eContent.valueBlock.valueHexView);
        }
        catch (ex) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 15,
            message: "Error during verification: TSTInfo wrong ASN.1 schema ",
            signatureVerified: null,
            signerCertificate: signerCert,
            timestampSerial,
            signerCertificateVerified: true
          });
        }
        //#endregion

        //#region Change "checkDate" and append "timestampSerial"
        checkDate = tstInfo.genTime;
        timestampSerial = tstInfo.serialNumber.valueBlock.valueHexView.slice();
        //#endregion

        //#region Check that we do have detached data content
        if (data.byteLength === 0) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 4,
            message: "Missed detached data input array",
          });
        }
        //#endregion

        if (!(await tstInfo.verify({ data }, crypto))) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 15,
            message: "Error during verification: TSTInfo verification is failed",
            signatureVerified: false,
            signerCertificate: signerCert,
            timestampSerial,
            signerCertificateVerified: true
          });
        }
      }

      //#endregion

      if (checkChain) {
        const certs = this.certificates.filter(certificate => (certificate instanceof Certificate && !!checkCA(certificate, signerCert))) as Certificate[];
        const chainParams: CertificateChainValidationEngineParameters = {
          checkDate,
          certs,
          trustedCerts,
        };

        if (findIssuer) {
          chainParams.findIssuer = findIssuer;
        }
        if (findOrigin) {
          chainParams.findOrigin = findOrigin;
        }

        const chainEngine = new CertificateChainValidationEngine(chainParams);
        chainEngine.certs.push(signerCert);

        if (this.crls) {
          for (const crl of this.crls) {
            if ("thisUpdate" in crl)
              chainEngine.crls.push(crl);
            else // Assumed "revocation value" has "OtherRevocationInfoFormat"
            {
              if (crl.otherRevInfoFormat === id_PKIX_OCSP_Basic) // Basic OCSP response
                chainEngine.ocsps.push(new BasicOCSPResponse({ schema: crl.otherRevInfo }));
            }
          }
        }

        if (this.ocsps) {
          chainEngine.ocsps.push(...(this.ocsps));
        }

        const verificationResult = await chainEngine.verify({ passedWhenNotRevValues }, crypto)
          .catch(e => {
            throw new SignedDataVerifyError({
              date: checkDate,
              code: 5,
              message: `Validation of signer's certificate failed with error: ${((e instanceof Object) ? e.resultMessage : e)}`,
              signerCertificate: signerCert,
              signerCertificateVerified: false
            });
          });
        if (verificationResult.certificatePath) {
          certificatePath = verificationResult.certificatePath;
        }

        if (!verificationResult.result)
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 5,
            message: `Validation of signer's certificate failed: ${verificationResult.resultMessage}`,
            signerCertificate: signerCert,
            signerCertificateVerified: false
          });
      }
      //#endregion

      //#region Find signer's hashing algorithm

      const signerInfoHashAlgorithm = crypto.getAlgorithmByOID(signerInfo.digestAlgorithm.algorithmId);
      if (!("name" in signerInfoHashAlgorithm)) {
        throw new SignedDataVerifyError({
          date: checkDate,
          code: 7,
          message: `Unsupported signature algorithm: ${signerInfo.digestAlgorithm.algorithmId}`,
          signerCertificate: signerCert,
          signerCertificateVerified: true
        });
      }

      shaAlgorithm = signerInfoHashAlgorithm.name;
      //#endregion

      //#region Create correct data block for verification

      const eContent = this.encapContentInfo.eContent;
      if (eContent) // Attached data
      {
        if ((eContent.idBlock.tagClass === 1) &&
          (eContent.idBlock.tagNumber === 4)) {
          data = eContent.getValue();
        }
        else
          data = eContent.valueBlock.valueBeforeDecodeView;
      }
      else // Detached data
      {
        if (data.byteLength === 0) // Check that "data" already provided by function parameter
        {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 8,
            message: "Missed detached data input array",
            signerCertificate: signerCert,
            signerCertificateVerified: true
          });
        }
      }

      if (signerInfo.signedAttrs) {
        //#region Check mandatory attributes
        let foundContentType = false;
        let foundMessageDigest = false;

        for (const attribute of signerInfo.signedAttrs.attributes) {
          //#region Check that "content-type" attribute exists
          if (attribute.type === "1.2.840.113549.1.9.3")
            foundContentType = true;
          //#endregion

          //#region Check that "message-digest" attribute exists
          if (attribute.type === "1.2.840.113549.1.9.4") {
            foundMessageDigest = true;
            messageDigestValue = attribute.values[0].valueBlock.valueHex;
          }
          //#endregion

          //#region Speed-up searching
          if (foundContentType && foundMessageDigest)
            break;
          //#endregion
        }

        if (foundContentType === false) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 9,
            message: "Attribute \"content-type\" is a mandatory attribute for \"signed attributes\"",
            signerCertificate: signerCert,
            signerCertificateVerified: true
          });
        }

        if (foundMessageDigest === false) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 10,
            message: "Attribute \"message-digest\" is a mandatory attribute for \"signed attributes\"",
            signatureVerified: null,
            signerCertificate: signerCert,
            signerCertificateVerified: true
          });
        }
        //#endregion
      }
      //#endregion

      //#region Verify "message-digest" attribute in case of "signedAttrs"
      if (signerInfo.signedAttrs) {
        const messageDigest = await crypto.digest(shaAlgorithm, new Uint8Array(data));
        if (!pvutils.isEqualBuffer(messageDigest, messageDigestValue)) {
          throw new SignedDataVerifyError({
            date: checkDate,
            code: 15,
            message: "Error during verification: Message digest doesn't match",
            signatureVerified: null,
            signerCertificate: signerCert,
            timestampSerial,
            signerCertificateVerified: true
          });
        }
        data = signerInfo.signedAttrs.encodedValue;
      }
      //#endregion

      const verifyResult = await crypto.verifyWithPublicKey(data, signerInfo.signature, signerCert.subjectPublicKeyInfo, signerCert.signatureAlgorithm, shaAlgorithm);

      //#region Make a final result

      if (extendedMode) {
        return {
          date: checkDate,
          code: 14,
          message: EMPTY_STRING,
          signatureVerified: verifyResult,
          signerCertificate: signerCert,
          timestampSerial,
          signerCertificateVerified: true,
          certificatePath
        };
      } else {
        return verifyResult;
      }
    } catch (e) {
      if (e instanceof SignedDataVerifyError) {
        throw e;
      }
      throw new SignedDataVerifyError({
        date: checkDate,
        code: 15,
        message: `Error during verification: ${e instanceof Error ? e.message : e}`,
        signatureVerified: null,
        signerCertificate: signerCert,
        timestampSerial,
        signerCertificateVerified: true
      });
    }
  }

  /**
   * Signing current SignedData
   * @param privateKey Private key for "subjectPublicKeyInfo" structure
   * @param signerIndex Index number (starting from 0) of signer index to make signature for
   * @param hashAlgorithm Hashing algorithm. Default SHA-1
   * @param data Detached data
   * @param crypto Crypto engine
   */
  public async sign(privateKey: CryptoKey, signerIndex: number, hashAlgorithm = "SHA-1", data: BufferSource = (EMPTY_BUFFER), crypto = common.getCrypto(true)): Promise<void> {
    //#region Initial checking
    if (!privateKey)
      throw new Error("Need to provide a private key for signing");
    //#endregion

    //#region Simple check for supported algorithm
    const hashAlgorithmOID = crypto.getOIDByAlgorithm({ name: hashAlgorithm }, true, "hashAlgorithm");
    //#endregion

    //#region Append information about hash algorithm
    if ((this.digestAlgorithms.filter(algorithm => algorithm.algorithmId === hashAlgorithmOID)).length === 0) {
      this.digestAlgorithms.push(new AlgorithmIdentifier({
        algorithmId: hashAlgorithmOID,
        algorithmParams: new asn1js.Null()
      }));
    }

    const signerInfo = this.signerInfos[signerIndex];
    if (!signerInfo) {
      throw new RangeError("SignerInfo index is out of range");
    }
    signerInfo.digestAlgorithm = new AlgorithmIdentifier({
      algorithmId: hashAlgorithmOID,
      algorithmParams: new asn1js.Null()
    });
    //#endregion

    //#region Get a "default parameters" for current algorithm and set correct signature algorithm
    const signatureParams = await crypto.getSignatureParameters(privateKey, hashAlgorithm);
    const parameters = signatureParams.parameters;
    signerInfo.signatureAlgorithm = signatureParams.signatureAlgorithm;
    //#endregion

    //#region Create TBS data for signing
    if (signerInfo.signedAttrs) {
      if (signerInfo.signedAttrs.encodedValue.byteLength !== 0)
        data = signerInfo.signedAttrs.encodedValue;
      else {
        data = signerInfo.signedAttrs.toSchema().toBER();

        //#region Change type from "[0]" to "SET" accordingly to standard
        const view = pvtsutils.BufferSourceConverter.toUint8Array(data);
        view[0] = 0x31;
        //#endregion
      }
    }
    else {
      const eContent = this.encapContentInfo.eContent;
      if (eContent) // Attached data
      {
        if ((eContent.idBlock.tagClass === 1) &&
          (eContent.idBlock.tagNumber === 4)) {
          data = eContent.getValue();
        }
        else
          data = eContent.valueBlock.valueBeforeDecodeView;
      }
      else // Detached data
      {
        if (data.byteLength === 0) // Check that "data" already provided by function parameter
          throw new Error("Missed detached data input array");
      }
    }
    //#endregion

    //#region Signing TBS data on provided private key
    const signature = await crypto.signWithPrivateKey(data, privateKey, parameters as any);
    signerInfo.signature = new asn1js.OctetString({ valueHex: signature });
    //#endregion
  }

}



[ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ]