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[];
/** * 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 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
*/ publicstatic override defaultValues(memberName: typeof VERSION): number; publicstatic override defaultValues(memberName: typeof DIGEST_ALGORITHMS): AlgorithmIdentifier[]; publicstatic override defaultValues(memberName: typeof ENCAP_CONTENT_INFO): EncapsulatedContentInfo; publicstatic override defaultValues(memberName: typeof CERTIFICATES): CertificateSetItem[]; publicstatic override defaultValues(memberName: typeof CRLS): SignedDataCRL[]; publicstatic override defaultValues(memberName: typeof OCSPS): BasicOCSPResponse[]; publicstatic override defaultValues(memberName: typeof SIGNER_INFOS): SignerInfo[]; publicstatic override defaultValues(memberName: string): any { switch (memberName) { case VERSION: return0; case DIGEST_ALGORITHMS: return []; case ENCAP_CONTENT_INFO: returnnew EncapsulatedContentInfo(); case CERTIFICATES: return []; case CRLS: return []; case OCSPS: return []; case SIGNER_INFOS: return []; default: returnsuper.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
*/ publicstatic 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: returnsuper.defaultValues(memberName);
}
}
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) returnnew CertificateRevocationList({ 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;
} elseif (this.certificates && this.certificates.length && this.certificates.some(o => o instanceof AttributeCertificateV2)) { this.version = 4;
} elseif ((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;
}
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) { thrownew 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) { thrownew 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;
//#region Check that we do have detached data content if (data.byteLength === 0) { thrownew SignedDataVerifyError({
date: checkDate,
code: 4,
message: "Missed detached data input array",
});
} //#endregion
if (!(await tstInfo.verify({ data }, crypto))) { thrownew SignedDataVerifyError({
date: checkDate,
code: 15,
message: "Error during verification: TSTInfo verification is failed",
signatureVerified: false,
signerCertificate: signerCert,
timestampSerial,
signerCertificateVerified: true
});
}
}
//#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
{ thrownew 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) { thrownew SignedDataVerifyError({
date: checkDate,
code: 9,
message: "Attribute \"content-type\" is a mandatory attribute for \"signed attributes\"",
signerCertificate: signerCert,
signerCertificateVerified: true
});
}
if (foundMessageDigest === false) { thrownew 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)) { thrownew 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
/** * 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) thrownew Error("Need to provide a private key for signing"); //#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) { thrownew 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 thrownew 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
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-06)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.