import * as asn1js from "asn1js"; import * as pvutils from "pvutils"; import * as common from "./common"; import { ResponseData, ResponseDataJson, ResponseDataSchema } from "./ResponseData"; import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; import { Certificate, CertificateJson, CertificateSchema, checkCA } from "./Certificate"; import { CertID } from "./CertID"; import { RelativeDistinguishedNames } from "./RelativeDistinguishedNames"; import { CertificateChainValidationEngine } from "./CertificateChainValidationEngine"; import * as Schema from "./Schema"; import { PkiObject, PkiObjectParameters } from "./PkiObject"; import { AsnError } from "./errors"; import { EMPTY_STRING } from "./constants";
/** * Represents the BasicOCSPResponse structure described in [RFC6960](https://datatracker.ietf.org/doc/html/rfc6960)
*/
export class BasicOCSPResponse extends PkiObject implements IBasicOCSPResponse {
public tbsResponseData!: ResponseData; public signatureAlgorithm!: AlgorithmIdentifier; public signature!: asn1js.BitString; public certs?: Certificate[];
/** * Initializes a new instance of the {@link BasicOCSPResponse} class * @param parameters Initialization parameters
*/
constructor(parameters: BasicOCSPResponseParameters = {}) { super();
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 TBS_RESPONSE_DATA): ResponseData; publicstatic override defaultValues(memberName: typeof SIGNATURE_ALGORITHM): AlgorithmIdentifier; publicstatic override defaultValues(memberName: typeof SIGNATURE): asn1js.BitString; publicstatic override defaultValues(memberName: typeof CERTS): Certificate[]; publicstatic override defaultValues(memberName: string): any { switch (memberName) { case TBS_RESPONSE_DATA: returnnew ResponseData(); case SIGNATURE_ALGORITHM: returnnew AlgorithmIdentifier(); case SIGNATURE: returnnew asn1js.BitString(); case CERTS: 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"type":
{
let comparisonResult = ((ResponseData.compareWithDefault("tbs", memberValue.tbs)) &&
(ResponseData.compareWithDefault("responderID", memberValue.responderID)) &&
(ResponseData.compareWithDefault("producedAt", memberValue.producedAt)) &&
(ResponseData.compareWithDefault("responses", memberValue.responses)));
if ("responseExtensions" in memberValue)
comparisonResult = comparisonResult && (ResponseData.compareWithDefault("responseExtensions", memberValue.responseExtensions));
return comparisonResult;
} case SIGNATURE_ALGORITHM: return ((memberValue.algorithmId === EMPTY_STRING) && (("algorithmParams" in memberValue) === false)); case SIGNATURE: return (memberValue.isEqual(BasicOCSPResponse.defaultValues(memberName))); case CERTS: return (memberValue.length === 0); default: returnsuper.defaultValues(memberName);
}
}
public fromSchema(schema: Schema.SchemaType): void { // Clear input data first
pvutils.clearProps(schema, CLEAR_PROPS); //#endregion
// Check the schema is valid const asn1 = asn1js.compareSchema(schema,
schema,
BasicOCSPResponse.schema()
);
AsnError.assertSchema(asn1, this.className);
//#region Get internal properties from parsed schema this.tbsResponseData = new ResponseData({ schema: asn1.result[BASIC_OCSP_RESPONSE_TBS_RESPONSE_DATA] }); this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result[BASIC_OCSP_RESPONSE_SIGNATURE_ALGORITHM] }); this.signature = asn1.result[BASIC_OCSP_RESPONSE_SIGNATURE];
if (BASIC_OCSP_RESPONSE_CERTS in asn1.result) { this.certs = Array.from(asn1.result[BASIC_OCSP_RESPONSE_CERTS], element => new Certificate({ schema: element }));
} //#endregion
}
public toSchema(): asn1js.Sequence { //#region Create array for output sequence const outputArray = [];
if (this.certs) {
res.certs = Array.from(this.certs, o => o.toJSON());
}
return res;
}
/** * Get OCSP response status for specific certificate * @param certificate Certificate to be checked * @param issuerCertificate Certificate of issuer for certificate to be checked * @param crypto Crypto engine
*/ public async getCertificateStatus(certificate: Certificate, issuerCertificate: Certificate, crypto = common.getCrypto(true)): Promise<CertificateStatus> { //#region Initial variables const result = {
isForCertificate: false,
status: 2// 0 = good, 1 = revoked, 2 = unknown
};
const hashesObject: Record<string, number> = {};
const certIDs: CertID[] = []; //#endregion
//#region Create all "certIDs" for input certificates for (const response of this.tbsResponseData.responses) { const hashAlgorithm = crypto.getAlgorithmByOID(response.certID.hashAlgorithm.algorithmId, true, "CertID.hashAlgorithm");
if (!hashesObject[hashAlgorithm.name]) {
hashesObject[hashAlgorithm.name] = 1;
//#region Compare all response's "certIDs" with identifiers for input certificate for (const response of this.tbsResponseData.responses) { for (const id of certIDs) { if (response.certID.isEqual(id)) {
result.isForCertificate = true;
/** * Make signature for current OCSP Basic Response * @param privateKey Private key for "subjectPublicKeyInfo" structure * @param hashAlgorithm Hashing algorithm. Default SHA-1 * @param crypto Crypto engine
*/
async sign(privateKey: CryptoKey, hashAlgorithm = "SHA-1", crypto = common.getCrypto(true)): Promise<void> { // Get a private key from function parameter if (!privateKey) { thrownew Error("Need to provide a private key for signing");
}
//#region Get a "default parameters" for current algorithm and set correct signature algorithm const signatureParams = await crypto.getSignatureParameters(privateKey, hashAlgorithm);
const algorithm = signatureParams.parameters.algorithm; if (!("name" in algorithm)) { thrownew Error("Empty algorithm");
} this.signatureAlgorithm = signatureParams.signatureAlgorithm; //#endregion
//#region Create TBS data for signing this.tbsResponseData.tbsView = new Uint8Array(this.tbsResponseData.toSchema(true).toBER()); //#endregion
//#region Signing TBS data on provided private key const signature = await crypto.signWithPrivateKey(this.tbsResponseData.tbsView, privateKey, { algorithm }); this.signature = new asn1js.BitString({ valueHex: signature }); //#endregion
}
//#region Check amount of certificates if (!this.certs) { thrownew Error("No certificates attached to the BasicOCSPResponse");
} //#endregion
//#region Find correct value for "responderID" switch (true) { case (this.tbsResponseData.responderID instanceof RelativeDistinguishedNames): // [1] Name for (const [index, certificate] of this.certs.entries()) { if (certificate.subject.isEqual(this.tbsResponseData.responderID)) {
certIndex = index; break;
}
} break; case (this.tbsResponseData.responderID instanceof asn1js.OctetString): // [2] KeyHash for (const [index, cert] of this.certs.entries()) { const hash = await crypto.digest({ name: "sha-1" }, cert.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView); if (pvutils.isEqualBuffer(hash, this.tbsResponseData.responderID.valueBlock.valueHex)) {
certIndex = index; break;
}
} break; default: thrownew Error("Wrong value for responderID");
} //#endregion
//#region Make additional verification for signer's certificate if (certIndex === (-1)) thrownew Error("Correct certificate was not found in OCSP response");
signerCert = this.certs[certIndex];
const additionalCerts: Certificate[] = [signerCert]; for (const cert of this.certs) { const caCert = await checkCA(cert, signerCert); if (caCert) {
additionalCerts.push(caCert);
}
} const certChain = new CertificateChainValidationEngine({
certs: additionalCerts,
trustedCerts,
});
const verificationResult = await certChain.verify({}, crypto); if (!verificationResult.result) { thrownew Error("Validation of signer's certificate failed");
}
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.