@credo-ts/core
Version:
<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1
97 lines (93 loc) • 4.72 kB
JavaScript
import { injectable } from "../../plugins/index.mjs";
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.112.0/helpers/decorate.mjs";
import "../../agent/index.mjs";
import { CredoWebCrypto } from "../../crypto/webcrypto/CredoWebCrypto.mjs";
import "../../crypto/webcrypto/index.mjs";
import { X509Error } from "./X509Error.mjs";
import { X509Certificate } from "./X509Certificate.mjs";
import { CertificateSigningRequest } from "./CertificateSigningRequest.mjs";
import * as x509 from "@peculiar/x509";
//#region src/modules/x509/X509Service.ts
let X509Service = class X509Service {
/**
*
* Validate a chain of X.509 certificates according to RFC 5280
*
* This function requires a list of base64 encoded certificates and, optionally, a certificate that should be found in the chain.
* If no certificate is provided, it will just assume the leaf certificate
*
* The leaf certificate should be the 0th index and the root the last
*
* The Issuer of the certificate is found with the following algorithm:
* - Check if there is an AuthorityKeyIdentifierExtension
* - Go through all the other certificates and see if the SubjectKeyIdentifier is equal to the AuthorityKeyIdentifier
* - If they are equal, the certificate is verified and returned as the issuer
*
* Additional validation:
* - Make sure at least a single certificate is in the chain
* - Check whether a certificate in the chain matches with a trusted certificate
*/
static async validateCertificateChain(agentContext, { certificateChain, certificate = certificateChain[0], verificationDate = /* @__PURE__ */ new Date(), trustedCertificates }) {
if (certificateChain.length === 0) throw new X509Error("Certificate chain is empty");
const webCrypto = new CredoWebCrypto(agentContext);
let parsedLeafCertificate;
let certificatesToBuildChain;
try {
parsedLeafCertificate = new x509.X509Certificate(certificate);
certificatesToBuildChain = [...certificateChain, ...trustedCertificates ?? []].map((c) => new x509.X509Certificate(c));
} catch (error) {
throw new X509Error("Error during parsing of x509 certificate", { cause: error });
}
let parsedChain = (await new x509.X509ChainBuilder({ certificates: certificatesToBuildChain }).build(parsedLeafCertificate, webCrypto)).map((c) => X509Certificate.fromRawCertificate(new Uint8Array(c.rawData))).reverse();
if (parsedChain.length < certificateChain.length) throw new X509Error("Could not parse the full chain. Likely due to incorrect ordering");
let previousCertificate;
if (trustedCertificates) {
const parsedTrustedCertificates = trustedCertificates.map((trustedCertificate) => X509Certificate.fromEncodedCertificate(trustedCertificate));
const trustedCertificateIndex = parsedChain.findIndex((cert) => parsedTrustedCertificates.some((tCert) => cert.equal(tCert)));
if (trustedCertificateIndex === -1) throw new X509Error("No trusted certificate was found while validating the X.509 chain");
if (trustedCertificateIndex > 0) {
previousCertificate = parsedChain[trustedCertificateIndex - 1];
parsedChain = parsedChain.slice(trustedCertificateIndex);
}
}
for (let i = 0; i < parsedChain.length; i++) {
const cert = parsedChain[i];
const publicJwk = previousCertificate ? previousCertificate.publicJwk : void 0;
const skipSignatureVerification = i === 0 && trustedCertificates && !publicJwk;
await cert.verify({
publicJwk,
verificationDate,
skipSignatureVerification
}, webCrypto);
previousCertificate = cert;
}
return parsedChain;
}
/**
*
* Parses a base64-encoded X.509 certificate into a {@link X509Certificate}
*
*/
static parseCertificate(_agentContext, { encodedCertificate }) {
return X509Certificate.fromEncodedCertificate(encodedCertificate);
}
static getLeafCertificate(_agentContext, { certificateChain }) {
if (certificateChain.length === 0) throw new X509Error("Certificate chain is empty");
return X509Certificate.fromEncodedCertificate(certificateChain[0]);
}
static async createCertificate(agentContext, options) {
const webCrypto = new CredoWebCrypto(agentContext);
return await X509Certificate.create(options, webCrypto);
}
static async createCertificateSigningRequest(agentContext, options) {
const webCrypto = new CredoWebCrypto(agentContext);
return await CertificateSigningRequest.create(options, webCrypto);
}
static parseCertificateSigningRequest({ encodedCertificateSigningRequest }) {
return CertificateSigningRequest.fromEncodedCertificateRequest(encodedCertificateSigningRequest);
}
};
X509Service = __decorate([injectable()], X509Service);
//#endregion
export { X509Service };
//# sourceMappingURL=X509Service.mjs.map