UNPKG

@sphereon/ssi-sdk-ext.x509-utils

Version:

Sphereon SSI-SDK plugin functions for X.509 Certificate handling.

777 lines (769 loc) 29.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { JwkKeyUse: () => JwkKeyUse, PEMToBinary: () => PEMToBinary, PEMToDer: () => PEMToDer, PEMToHex: () => PEMToHex, PEMToJwk: () => PEMToJwk, RSASigner: () => RSASigner, SubjectAlternativeGeneralName: () => SubjectAlternativeGeneralName, areCertificatesEqual: () => areCertificatesEqual, assertCertificateMatchesClientIdScheme: () => assertCertificateMatchesClientIdScheme, base64ToHex: () => base64ToHex, cryptoSubtleImportRSAKey: () => cryptoSubtleImportRSAKey, derToPEM: () => derToPEM, generateRSAKeyAsPEM: () => generateRSAKeyAsPEM, getCertificateInfo: () => getCertificateInfo, getCertificateSubjectPublicKeyJWK: () => getCertificateSubjectPublicKeyJWK, getIssuerDN: () => getIssuerDN, getSubjectAlternativeNames: () => getSubjectAlternativeNames, getSubjectDN: () => getSubjectDN, getX509AlgorithmProvider: () => getX509AlgorithmProvider, hexKeyFromPEMBasedJwk: () => hexKeyFromPEMBasedJwk, hexToBase64: () => hexToBase64, hexToPEM: () => hexToPEM, jwkToPEM: () => jwkToPEM, parseCertificate: () => parseCertificate, pemCertChainTox5c: () => pemCertChainTox5c, pemOrDerToX509Certificate: () => pemOrDerToX509Certificate, privateKeyHexFromPEM: () => privateKeyHexFromPEM, publicKeyHexFromPEM: () => publicKeyHexFromPEM, signAlgorithmToSchemeAndHashAlg: () => signAlgorithmToSchemeAndHashAlg, toKeyObject: () => toKeyObject, validateCertificateChainMatchesClientIdScheme: () => validateCertificateChainMatchesClientIdScheme, validateX509CertificateChain: () => validateX509CertificateChain, x5cToPemCertChain: () => x5cToPemCertChain }); module.exports = __toCommonJS(index_exports); // src/types/index.ts var JwkKeyUse = /* @__PURE__ */ (function(JwkKeyUse2) { JwkKeyUse2["Encryption"] = "enc"; JwkKeyUse2["Signature"] = "sig"; return JwkKeyUse2; })({}); // src/x509/rsa-key.ts var u8a2 = __toESM(require("uint8arrays"), 1); // src/x509/crypto.ts var globalCrypto = /* @__PURE__ */ __name((setGlobal, suppliedCrypto) => { let webcrypto; if (typeof suppliedCrypto !== "undefined") { webcrypto = suppliedCrypto; } else if (typeof crypto !== "undefined") { webcrypto = crypto; } else if (typeof global.crypto !== "undefined") { webcrypto = global.crypto; } else { if (typeof global.window?.crypto?.subtle !== "undefined") { webcrypto = global.window.crypto; } else { webcrypto = require("crypto"); } } if (setGlobal) { global.crypto = webcrypto; } return webcrypto; }, "globalCrypto"); // src/x509/x509-utils.ts var import_pkijs = require("pkijs"); var u8a = __toESM(require("uint8arrays"), 1); var import_keyto = __toESM(require("@trust/keyto"), 1); var { fromString, toString } = u8a; function pemCertChainTox5c(cert, maxDepth) { if (!maxDepth) { maxDepth = 0; } const intermediate = cert.replace(/-----[^\n]+\n?/gm, ",").replace(/\n/g, "").replace(/\r/g, ""); let x5c = intermediate.split(",").filter(function(c) { return c.length > 0; }); if (maxDepth > 0) { x5c = x5c.splice(0, maxDepth); } return x5c; } __name(pemCertChainTox5c, "pemCertChainTox5c"); function x5cToPemCertChain(x5c, maxDepth) { if (!maxDepth) { maxDepth = 0; } const length = maxDepth === 0 ? x5c.length : Math.min(maxDepth, x5c.length); let pem = ""; for (let i = 0; i < length; i++) { pem += derToPEM(x5c[i], "CERTIFICATE"); } return pem; } __name(x5cToPemCertChain, "x5cToPemCertChain"); var pemOrDerToX509Certificate = /* @__PURE__ */ __name((cert) => { let DER = typeof cert === "string" ? cert : void 0; if (typeof cert === "object" && !(cert instanceof Uint8Array)) { return import_pkijs.Certificate.fromBER(cert.rawData); } else if (typeof cert !== "string") { return import_pkijs.Certificate.fromBER(cert); } else if (cert.includes("CERTIFICATE")) { DER = PEMToDer(cert); } if (!DER) { throw Error("Invalid cert input value supplied. PEM, DER, Bytes and X509Certificate object are supported"); } return import_pkijs.Certificate.fromBER(fromString(DER, "base64pad")); }, "pemOrDerToX509Certificate"); var areCertificatesEqual = /* @__PURE__ */ __name((cert1, cert2) => { return cert1.signatureValue.isEqual(cert2.signatureValue); }, "areCertificatesEqual"); var toKeyObject = /* @__PURE__ */ __name((PEM, visibility = "public") => { const jwk = PEMToJwk(PEM, visibility); const keyVisibility = jwk.d ? "private" : "public"; const keyHex = keyVisibility === "private" ? privateKeyHexFromPEM(PEM) : publicKeyHexFromPEM(PEM); return { pem: hexToPEM(keyHex, visibility), jwk, keyHex, keyType: keyVisibility }; }, "toKeyObject"); var jwkToPEM = /* @__PURE__ */ __name((jwk, visibility = "public") => { return import_keyto.default.from(jwk, "jwk").toString("pem", visibility === "public" ? "public_pkcs8" : "private_pkcs8"); }, "jwkToPEM"); var PEMToJwk = /* @__PURE__ */ __name((pem, visibility = "public") => { return import_keyto.default.from(pem, "pem").toJwk(visibility); }, "PEMToJwk"); var privateKeyHexFromPEM = /* @__PURE__ */ __name((PEM) => { return PEMToHex(PEM); }, "privateKeyHexFromPEM"); var hexKeyFromPEMBasedJwk = /* @__PURE__ */ __name((jwk, visibility = "public") => { if (visibility === "private") { return privateKeyHexFromPEM(jwkToPEM(jwk, "private")); } else { return publicKeyHexFromPEM(jwkToPEM(jwk, "public")); } }, "hexKeyFromPEMBasedJwk"); var publicKeyHexFromPEM = /* @__PURE__ */ __name((PEM) => { const hex = PEMToHex(PEM); if (PEM.includes("CERTIFICATE")) { throw Error("Cannot directly deduce public Key from PEM Certificate yet"); } else if (!PEM.includes("PRIVATE")) { return hex; } const publicJwk = PEMToJwk(PEM, "public"); const publicPEM = jwkToPEM(publicJwk, "public"); return PEMToHex(publicPEM); }, "publicKeyHexFromPEM"); var PEMToHex = /* @__PURE__ */ __name((PEM, headerKey) => { if (PEM.indexOf("-----BEGIN ") == -1) { throw Error(`PEM header not found: ${headerKey}`); } let strippedPem; if (headerKey) { strippedPem = PEM.replace(new RegExp("^[^]*-----BEGIN " + headerKey + "-----"), ""); strippedPem = strippedPem.replace(new RegExp("-----END " + headerKey + "-----[^]*$"), ""); } else { strippedPem = PEM.replace(/^[^]*-----BEGIN [^-]+-----/, ""); strippedPem = strippedPem.replace(/-----END [^-]+-----[^]*$/, ""); } return base64ToHex(strippedPem, "base64pad"); }, "PEMToHex"); function PEMToBinary(pem) { const pemContents = pem.replace(/^[^]*-----BEGIN [^-]+-----/, "").replace(/-----END [^-]+-----[^]*$/, "").replace(/\s/g, ""); return fromString(pemContents, "base64pad"); } __name(PEMToBinary, "PEMToBinary"); var base64ToHex = /* @__PURE__ */ __name((input, inputEncoding) => { const base64NoNewlines = input.replace(/[^0-9A-Za-z_\-~\/+=]*/g, ""); return toString(fromString(base64NoNewlines, inputEncoding ? inputEncoding : "base64pad"), "base16"); }, "base64ToHex"); var hexToBase64 = /* @__PURE__ */ __name((input, targetEncoding) => { let hex = typeof input === "string" ? input : input.toString(16); if (hex.length % 2 === 1) { hex = `0${hex}`; } return toString(fromString(hex, "base16"), targetEncoding ? targetEncoding : "base64pad"); }, "hexToBase64"); var hexToPEM = /* @__PURE__ */ __name((hex, type) => { const base64 = hexToBase64(hex, "base64pad"); const headerKey = type === "private" ? "RSA PRIVATE KEY" : "PUBLIC KEY"; if (type === "private") { const pem = derToPEM(base64, headerKey); try { PEMToJwk(pem); return pem; } catch (error) { return derToPEM(base64, "PRIVATE KEY"); } } return derToPEM(base64, headerKey); }, "hexToPEM"); function PEMToDer(pem) { return pem.replace(/(-----(BEGIN|END) CERTIFICATE-----|[\n\r])/g, ""); } __name(PEMToDer, "PEMToDer"); function derToPEM(cert, headerKey) { const key = headerKey ?? "CERTIFICATE"; if (cert.includes(key)) { return cert; } const matches = cert.match(/.{1,64}/g); if (!matches) { throw Error("Invalid cert input value supplied"); } return `-----BEGIN ${key}----- ${matches.join("\n")} -----END ${key}----- `; } __name(derToPEM, "derToPEM"); // src/x509/rsa-key.ts var { toString: toString2 } = u8a2; var usage = /* @__PURE__ */ __name((jwk) => { if (jwk.key_ops && jwk.key_ops.length > 0) { return jwk.key_ops; } if (jwk.use) { const usages = []; if (jwk.use.includes("sig")) { usages.push("sign", "verify"); } else if (jwk.use.includes("enc")) { usages.push("encrypt", "decrypt"); } if (usages.length > 0) { return usages; } } if (jwk.kty === "RSA") { if (jwk.d) { return jwk.alg?.toUpperCase()?.includes("QAEP") ? [ "encrypt" ] : [ "sign" ]; } return jwk.alg?.toUpperCase()?.includes("QAEP") ? [ "decrypt" ] : [ "verify" ]; } return jwk.d && jwk.kty !== "RSA" ? [ "sign", "decrypt", "verify", "encrypt" ] : [ "verify" ]; }, "usage"); var signAlgorithmToSchemeAndHashAlg = /* @__PURE__ */ __name((signingAlg) => { const alg = signingAlg.toUpperCase(); let scheme; if (alg.startsWith("RS")) { scheme = "RSASSA-PKCS1-V1_5"; } else if (alg.startsWith("PS")) { scheme = "RSA-PSS"; } else { throw Error(`Invalid signing algorithm supplied ${signingAlg}`); } const hashAlgorithm = `SHA-${alg.substring(2)}`; return { scheme, hashAlgorithm }; }, "signAlgorithmToSchemeAndHashAlg"); var cryptoSubtleImportRSAKey = /* @__PURE__ */ __name(async (jwk, scheme, hashAlgorithm) => { const hashName = hashAlgorithm ? hashAlgorithm : jwk.alg ? `SHA-${jwk.alg.substring(2)}` : "SHA-256"; const importParams = { name: scheme, hash: hashName }; return await globalCrypto(false).subtle.importKey("jwk", jwk, importParams, false, usage(jwk)); }, "cryptoSubtleImportRSAKey"); var generateRSAKeyAsPEM = /* @__PURE__ */ __name(async (scheme, hashAlgorithm, modulusLength) => { const hashName = hashAlgorithm ? hashAlgorithm : "SHA-256"; const params = { name: scheme, hash: hashName, modulusLength: modulusLength ? modulusLength : 2048, publicExponent: new Uint8Array([ 1, 0, 1 ]) }; const keyUsage = scheme === "RSA-PSS" || scheme === "RSASSA-PKCS1-V1_5" ? [ "sign", "verify" ] : [ "encrypt", "decrypt" ]; const keypair = await globalCrypto(false).subtle.generateKey(params, true, keyUsage); const pkcs8 = await globalCrypto(false).subtle.exportKey("pkcs8", keypair.privateKey); const uint8Array = new Uint8Array(pkcs8); return derToPEM(toString2(uint8Array, "base64pad"), "RSA PRIVATE KEY"); }, "generateRSAKeyAsPEM"); // src/x509/rsa-signer.ts var u8a3 = __toESM(require("uint8arrays"), 1); var { fromString: fromString2, toString: toString3 } = u8a3; var RSASigner = class { static { __name(this, "RSASigner"); } hashAlgorithm; jwk; key; scheme; /** * * @param key Either in PEM or JWK format (no raw hex keys here!) * @param opts The algorithm and signature/encryption schemes */ constructor(key, opts) { if (typeof key === "string") { this.jwk = PEMToJwk(key, opts?.visibility); } else { this.jwk = key; } this.hashAlgorithm = opts?.hashAlgorithm ?? "SHA-256"; this.scheme = opts?.scheme ?? "RSA-PSS"; } getImportParams() { if (this.scheme === "RSA-PSS") { return { name: this.scheme, saltLength: 32 }; } return { name: this.scheme /*, hash: this.hashAlgorithm*/ }; } async getKey() { if (!this.key) { this.key = await cryptoSubtleImportRSAKey(this.jwk, this.scheme, this.hashAlgorithm); } return this.key; } bufferToString(buf) { const uint8Array = new Uint8Array(buf); return toString3(uint8Array, "base64url"); } async sign(data) { const input = data; const key = await this.getKey(); const signature = this.bufferToString(await globalCrypto(false).subtle.sign(this.getImportParams(), key, input)); if (!signature) { throw Error("Could not sign input data"); } return signature; } async verify(data, signature) { const jws = signature.includes(".") ? signature.split(".")[2] : signature; const input = typeof data == "string" ? fromString2(data, "utf-8") : data; let key = await this.getKey(); if (!key.usages.includes("verify")) { const verifyJwk = { ...this.jwk }; delete verifyJwk.d; delete verifyJwk.use; delete verifyJwk.key_ops; key = await cryptoSubtleImportRSAKey(verifyJwk, this.scheme, this.hashAlgorithm); } const verificationResult = await globalCrypto(false).subtle.verify(this.getImportParams(), key, fromString2(jws, "base64url"), input); return verificationResult; } }; // src/x509/x509-validator.ts var import_asn1_schema = require("@peculiar/asn1-schema"); var import_asn1_x509 = require("@peculiar/asn1-x509"); var import_x509 = require("@peculiar/x509"); var import_js_x509_utils = __toESM(require("js-x509-utils"), 1); var import_pkijs2 = require("pkijs"); var import_tsyringe = require("tsyringe"); var u8a4 = __toESM(require("uint8arrays"), 1); var { fromString: fromString3, toString: toString4 } = u8a4; var defaultCryptoEngine = /* @__PURE__ */ __name(() => { const name = "crypto"; (0, import_pkijs2.setEngine)(name, new import_pkijs2.CryptoEngine({ name, crypto: globalCrypto(false) })); return (0, import_pkijs2.getCrypto)(true); }, "defaultCryptoEngine"); var getCertificateInfo = /* @__PURE__ */ __name(async (certificate, opts) => { let publicKeyJWK; try { publicKeyJWK = await getCertificateSubjectPublicKeyJWK(certificate); } catch (e) { } return { issuer: { dn: getIssuerDN(certificate) }, subject: { dn: getSubjectDN(certificate), subjectAlternativeNames: getSubjectAlternativeNames(certificate, { typeFilter: opts?.sanTypeFilter }) }, publicKeyJWK, notBefore: certificate.notBefore.value, notAfter: certificate.notAfter.value }; }, "getCertificateInfo"); var validateX509CertificateChain = /* @__PURE__ */ __name(async ({ chain: pemOrDerChain, trustAnchors, verificationTime = /* @__PURE__ */ new Date(), opts = { // If no trust anchor is found, but the chain itself checks out, allow. (defaults to false:) allowNoTrustAnchorsFound: false, trustRootWhenNoAnchors: false, allowSingleNoCAChainElement: true, blindlyTrustedAnchors: [], disallowReversedChain: false } }) => { return await validateX509CertificateChainImpl({ reversed: false, chain: [ ...pemOrDerChain ].reverse(), trustAnchors, verificationTime, opts }); }, "validateX509CertificateChain"); var validateX509CertificateChainImpl = /* @__PURE__ */ __name(async ({ reversed, chain: pemOrDerChain, trustAnchors, verificationTime: verifyAt, opts }) => { const verificationTime = typeof verifyAt === "string" ? new Date(verifyAt) : verifyAt; const { allowNoTrustAnchorsFound = false, trustRootWhenNoAnchors = false, allowSingleNoCAChainElement = true, blindlyTrustedAnchors = [], disallowReversedChain = false, client } = opts; const trustedPEMs = trustRootWhenNoAnchors && !trustAnchors ? [ pemOrDerChain[pemOrDerChain.length - 1] ] : trustAnchors; if (pemOrDerChain.length === 0) { return { error: true, critical: true, message: "Certificate chain in DER or PEM format must not be empty", verificationTime }; } defaultCryptoEngine(); const chain = await Promise.all(pemOrDerChain.map((raw) => parseCertificate(raw))); const x5cOrdereredChain = reversed ? [ ...chain ] : [ ...chain ].reverse(); const trustedCerts = trustedPEMs ? await Promise.all(trustedPEMs.map((raw) => parseCertificate(raw))) : void 0; const blindlyTrusted = (await Promise.all(blindlyTrustedAnchors.map((raw) => { try { return parseCertificate(raw); } catch (e) { console.log(`Failed to parse blindly trusted certificate ${raw}. Error: ${e.message}`); return void 0; } }))).filter((cert) => cert !== void 0) ?? []; const leafCert = x5cOrdereredChain[0]; const chainLength = chain.length; var foundTrustAnchor = void 0; for (let i = 0; i < chainLength; i++) { const currentCert = chain[i]; const previousCert = i > 0 ? chain[i - 1] : void 0; const blindlyTrustedCert = blindlyTrusted.find((trusted) => areCertificatesEqual(trusted.certificate, currentCert.certificate)); if (blindlyTrustedCert) { console.log(`Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`); return { error: false, critical: false, message: `Certificate chain validation success as single cert if blindly trusted. WARNING: ONLY USE FOR TESTING PURPOSES.`, detailMessage: `Blindly trusted certificate ${blindlyTrustedCert.certificateInfo.subject.dn.DN} was found in the chain.`, trustAnchor: blindlyTrustedCert?.certificateInfo, verificationTime, certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo), ...client && { client } }; } if (previousCert) { if (currentCert.x509Certificate.issuer !== previousCert.x509Certificate.subject) { if (!reversed && !disallowReversedChain) { return await validateX509CertificateChainImpl({ reversed: true, chain: [ ...pemOrDerChain ].reverse(), opts, verificationTime, trustAnchors }); } return { error: true, critical: true, certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo), message: `Certificate chain validation failed for ${leafCert.certificateInfo.subject.dn.DN}.`, detailMessage: `The certificate ${currentCert.certificateInfo.subject.dn.DN} with issuer ${currentCert.x509Certificate.issuer}, is not signed by the previous certificate ${previousCert?.certificateInfo.subject.dn.DN} with subject string ${previousCert?.x509Certificate.subject}.`, verificationTime, ...client && { client } }; } } const result = await currentCert.x509Certificate.verify({ date: verificationTime, publicKey: previousCert?.x509Certificate?.publicKey }, (0, import_pkijs2.getCrypto)()?.crypto ?? crypto ?? global.crypto); if (!result) { if (i == 0 && !reversed && !disallowReversedChain) { return await validateX509CertificateChainImpl({ reversed: true, chain: [ ...pemOrDerChain ].reverse(), opts, verificationTime, trustAnchors }); } return { error: true, critical: true, message: `Certificate chain validation failed for ${leafCert.certificateInfo.subject.dn.DN}.`, certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo), detailMessage: `Verification of the certificate ${currentCert.certificateInfo.subject.dn.DN} with issuer ${currentCert.x509Certificate.issuer} failed. Public key: ${JSON.stringify(currentCert.certificateInfo.publicKeyJWK)}.`, verificationTime, ...client && { client } }; } foundTrustAnchor = foundTrustAnchor ?? trustedCerts?.find((trusted) => isSameCertificate(trusted.x509Certificate, currentCert.x509Certificate)); if (i === 0 && chainLength === 1 && allowSingleNoCAChainElement) { return { error: false, critical: false, message: `Certificate chain succeeded as allow single cert result is allowed: ${leafCert.certificateInfo.subject.dn.DN}.`, certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo), trustAnchor: foundTrustAnchor?.certificateInfo, verificationTime, ...client && { client } }; } } if (foundTrustAnchor?.certificateInfo || allowNoTrustAnchorsFound) { return { error: false, critical: false, message: `Certificate chain was valid`, certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo), detailMessage: foundTrustAnchor ? `The leaf certificate ${leafCert.certificateInfo.subject.dn.DN} is part of a chain with trust anchor ${foundTrustAnchor?.certificateInfo.subject.dn.DN}.` : `The leaf certificate ${leafCert.certificateInfo.subject.dn.DN} and chain were valid, but no trust anchor has been found. Ignoring as user allowed (allowNoTrustAnchorsFound: ${allowNoTrustAnchorsFound}).)`, trustAnchor: foundTrustAnchor?.certificateInfo, verificationTime, ...client && { client } }; } return { error: true, critical: true, message: `Certificate chain validation failed for ${leafCert.certificateInfo.subject.dn.DN}.`, certificateChain: x5cOrdereredChain.map((cert) => cert.certificateInfo), detailMessage: `No trust anchor was found in the chain. between (intermediate) CA ${x5cOrdereredChain[chain.length - 1].certificateInfo.subject.dn.DN} and leaf ${x5cOrdereredChain[0].certificateInfo.subject.dn.DN}.`, verificationTime, ...client && { client } }; }, "validateX509CertificateChainImpl"); var isSameCertificate = /* @__PURE__ */ __name((cert1, cert2) => { return cert1.rawData.toString() === cert2.rawData.toString(); }, "isSameCertificate"); var algorithmProvider = import_tsyringe.container.resolve(import_x509.AlgorithmProvider); var getX509AlgorithmProvider = /* @__PURE__ */ __name(() => { return algorithmProvider; }, "getX509AlgorithmProvider"); var parseCertificate = /* @__PURE__ */ __name(async (rawCert) => { const x509Certificate = new import_x509.X509Certificate(rawCert); const publicKeyInfo = import_asn1_schema.AsnParser.parse(x509Certificate.publicKey.rawData, import_asn1_x509.SubjectPublicKeyInfo); const publicKeyRaw = new Uint8Array(publicKeyInfo.subjectPublicKey); let publicKeyJwk = void 0; try { publicKeyJwk = await getCertificateSubjectPublicKeyJWK(new Uint8Array(x509Certificate.rawData)); } catch (e) { console.error(e.message); } const certificate = pemOrDerToX509Certificate(rawCert); const certificateInfo = await getCertificateInfo(certificate); const publicKeyAlgorithm = getX509AlgorithmProvider().toWebAlgorithm(publicKeyInfo.algorithm); return { publicKeyAlgorithm, publicKeyInfo, publicKeyJwk, publicKeyRaw, certificateInfo, certificate, x509Certificate }; }, "parseCertificate"); var rdnmap = { "2.5.4.6": "C", "2.5.4.10": "O", "2.5.4.11": "OU", "2.5.4.3": "CN", "2.5.4.7": "L", "2.5.4.8": "ST", "2.5.4.12": "T", "2.5.4.42": "GN", "2.5.4.43": "I", "2.5.4.4": "SN", "1.2.840.113549.1.9.1": "E-mail" }; var getIssuerDN = /* @__PURE__ */ __name((cert) => { return { DN: getDNString(cert.issuer.typesAndValues), attributes: getDNObject(cert.issuer.typesAndValues) }; }, "getIssuerDN"); var getSubjectDN = /* @__PURE__ */ __name((cert) => { return { DN: getDNString(cert.subject.typesAndValues), attributes: getDNObject(cert.subject.typesAndValues) }; }, "getSubjectDN"); var getDNObject = /* @__PURE__ */ __name((typesAndValues) => { const DN = {}; for (const typeAndValue of typesAndValues) { const type = rdnmap[typeAndValue.type] ?? typeAndValue.type; DN[type] = typeAndValue.value.getValue(); } return DN; }, "getDNObject"); var getDNString = /* @__PURE__ */ __name((typesAndValues) => { return Object.entries(getDNObject(typesAndValues)).map(([key, value]) => `${key}=${value}`).join(","); }, "getDNString"); var getCertificateSubjectPublicKeyJWK = /* @__PURE__ */ __name(async (pemOrDerCert) => { const pemOrDerStr = typeof pemOrDerCert === "string" ? toString4(fromString3(pemOrDerCert, "base64pad"), "base64pad") : pemOrDerCert instanceof Uint8Array ? toString4(pemOrDerCert, "base64pad") : toString4(fromString3(pemOrDerCert.toString("base64"), "base64pad"), "base64pad"); const pem = derToPEM(pemOrDerStr); const certificate = pemOrDerToX509Certificate(pem); var jwk; try { const subtle = (0, import_pkijs2.getCrypto)(true).subtle; const pk = await certificate.getPublicKey(void 0, defaultCryptoEngine()); jwk = await subtle.exportKey("jwk", pk); } catch (error) { console.log(`Error in primary get JWK from cert:`, error?.message); } if (!jwk) { try { jwk = await import_js_x509_utils.default.toJwk(pem, "pem"); } catch (error) { console.log(`Error in secondary get JWK from cert as well:`, error?.message); } } if (!jwk) { throw Error(`Failed to get JWK from certificate ${pem}`); } return jwk; }, "getCertificateSubjectPublicKeyJWK"); var SubjectAlternativeGeneralName = /* @__PURE__ */ (function(SubjectAlternativeGeneralName2) { SubjectAlternativeGeneralName2[SubjectAlternativeGeneralName2["rfc822Name"] = 1] = "rfc822Name"; SubjectAlternativeGeneralName2[SubjectAlternativeGeneralName2["dnsName"] = 2] = "dnsName"; SubjectAlternativeGeneralName2[SubjectAlternativeGeneralName2["uniformResourceIdentifier"] = 6] = "uniformResourceIdentifier"; SubjectAlternativeGeneralName2[SubjectAlternativeGeneralName2["ipAddress"] = 7] = "ipAddress"; return SubjectAlternativeGeneralName2; })({}); var assertCertificateMatchesClientIdScheme = /* @__PURE__ */ __name((certificate, clientId, clientIdScheme) => { const sans = getSubjectAlternativeNames(certificate, { clientIdSchemeFilter: clientIdScheme }); const clientIdMatches = sans.find((san) => san.value === clientId); if (!clientIdMatches) { throw Error(`Client id scheme ${clientIdScheme} used had no matching subject alternative names in certificate with DN ${getSubjectDN(certificate).DN}. SANS: ${sans.map((san) => san.value).join(",")}`); } }, "assertCertificateMatchesClientIdScheme"); var validateCertificateChainMatchesClientIdScheme = /* @__PURE__ */ __name(async (certificate, clientId, clientIdScheme) => { const result = { error: true, critical: true, message: `Client Id ${clientId} was not present in certificate using scheme ${clientIdScheme}`, client: { clientId, clientIdScheme }, certificateChain: [ await getCertificateInfo(certificate) ], verificationTime: /* @__PURE__ */ new Date() }; try { assertCertificateMatchesClientIdScheme(certificate, clientId, clientIdScheme); } catch (error) { return result; } result.error = false; result.message = `Client Id ${clientId} was present in certificate using scheme ${clientIdScheme}`; return result; }, "validateCertificateChainMatchesClientIdScheme"); var getSubjectAlternativeNames = /* @__PURE__ */ __name((certificate, opts) => { let typeFilter; if (opts?.clientIdSchemeFilter) { typeFilter = opts.clientIdSchemeFilter === "x509_san_dns" ? [ 2 ] : [ 6 ]; } else if (opts?.typeFilter) { typeFilter = Array.isArray(opts.typeFilter) ? opts.typeFilter : [ opts.typeFilter ]; } else { typeFilter = [ 2, 6 ]; } const parsedValue = certificate.extensions?.find((ext) => ext.extnID === import_pkijs2.id_SubjectAltName)?.parsedValue; if (!parsedValue) { return []; } const altNames = parsedValue.toJSON().altNames; return altNames.filter((altName) => typeFilter.includes(altName.type)).map((altName) => { return { type: altName.type, value: altName.value }; }); }, "getSubjectAlternativeNames"); //# sourceMappingURL=index.cjs.map