

import { TypedArrayEncoder } from "../../utils/TypedArrayEncoder.mjs";
import "../../utils/index.mjs";
import { assymetricPublicJwkMatches } from "../kms/jwk/equals.mjs";
import "../kms/index.mjs";
import { CredoWebCryptoKey } from "../../crypto/webcrypto/CredoWebCryptoKey.mjs";
import { publicJwkToCryptoKeyAlgorithm, spkiToPublicJwk } from "../../crypto/webcrypto/utils/keyAlgorithmConversion.mjs";
import "../../crypto/webcrypto/utils/index.mjs";
import { CredoWebCrypto } from "../../crypto/webcrypto/CredoWebCrypto.mjs";
import "../../crypto/webcrypto/index.mjs";
import { createAuthorityKeyIdentifierExtension, createBasicConstraintsExtension, createCrlDistributionPointsExtension, createExtendedKeyUsagesExtension, createIssuerAlternativeNameExtension, createKeyUsagesExtension, createSubjectAlternativeNameExtension, createSubjectKeyIdentifierExtension } from "./utils/extensions.mjs";
import { X509Error } from "./X509Error.mjs";
import { convertName } from "./utils/nameConversion.mjs";
import "./utils/index.mjs";
import { AsnParser } from "@peculiar/asn1-schema";
import { SubjectPublicKeyInfo, id_ce_authorityKeyIdentifier, id_ce_extKeyUsage, id_ce_issuerAltName, id_ce_keyUsage, id_ce_subjectAltName, id_ce_subjectKeyIdentifier } from "@peculiar/asn1-x509";
import * as x509 from "@peculiar/x509";

//#region src/modules/x509/X509Certificate.ts
let X509KeyUsage = /* @__PURE__ */ function(X509KeyUsage$1) {
	X509KeyUsage$1[X509KeyUsage$1["DigitalSignature"] = 1] = "DigitalSignature";
	X509KeyUsage$1[X509KeyUsage$1["NonRepudiation"] = 2] = "NonRepudiation";
	X509KeyUsage$1[X509KeyUsage$1["KeyEncipherment"] = 4] = "KeyEncipherment";
	X509KeyUsage$1[X509KeyUsage$1["DataEncipherment"] = 8] = "DataEncipherment";
	X509KeyUsage$1[X509KeyUsage$1["KeyAgreement"] = 16] = "KeyAgreement";
	X509KeyUsage$1[X509KeyUsage$1["KeyCertSign"] = 32] = "KeyCertSign";
	X509KeyUsage$1[X509KeyUsage$1["CrlSign"] = 64] = "CrlSign";
	X509KeyUsage$1[X509KeyUsage$1["EncipherOnly"] = 128] = "EncipherOnly";
	X509KeyUsage$1[X509KeyUsage$1["DecipherOnly"] = 256] = "DecipherOnly";
	return X509KeyUsage$1;
}({});
let X509ExtendedKeyUsage = /* @__PURE__ */ function(X509ExtendedKeyUsage$1) {
	X509ExtendedKeyUsage$1["ServerAuth"] = "1.3.6.1.5.5.7.3.1";
	X509ExtendedKeyUsage$1["ClientAuth"] = "1.3.6.1.5.5.7.3.2";
	X509ExtendedKeyUsage$1["CodeSigning"] = "1.3.6.1.5.5.7.3.3";
	X509ExtendedKeyUsage$1["EmailProtection"] = "1.3.6.1.5.5.7.3.4";
	X509ExtendedKeyUsage$1["TimeStamping"] = "1.3.6.1.5.5.7.3.8";
	X509ExtendedKeyUsage$1["OcspSigning"] = "1.3.6.1.5.5.7.3.9";
	X509ExtendedKeyUsage$1["MdlDs"] = "1.0.18013.5.1.2";
	return X509ExtendedKeyUsage$1;
}({});
var X509Certificate = class X509Certificate {
	constructor(options) {
		this.publicJwk = options.publicJwk;
		this.privateKey = options.privateKey;
		this.x509Certificate = options.x509Certificate;
	}
	set keyId(keyId) {
		this.publicJwk.keyId = keyId;
	}
	get keyId() {
		return this.publicJwk.keyId;
	}
	get hasKeyId() {
		return this.publicJwk.hasKeyId;
	}
	static fromRawCertificate(rawCertificate) {
		const certificate = new x509.X509Certificate(rawCertificate);
		return X509Certificate.parseCertificate(certificate);
	}
	static fromEncodedCertificate(encodedCertificate) {
		const certificate = new x509.X509Certificate(encodedCertificate);
		return X509Certificate.parseCertificate(certificate);
	}
	static parseCertificate(certificate) {
		const spki = AsnParser.parse(certificate.publicKey.rawData, SubjectPublicKeyInfo);
		const privateKey = certificate.privateKey ? new Uint8Array(certificate.privateKey.rawData) : void 0;
		return new X509Certificate({
			publicJwk: spkiToPublicJwk(spki),
			privateKey,
			x509Certificate: certificate
		});
	}
	getMatchingExtensions(objectIdentifier) {
		return this.x509Certificate.extensions.filter((e) => e.type === objectIdentifier);
	}
	get rawCertificate() {
		return new Uint8Array(this.x509Certificate.rawData);
	}
	get subjectAlternativeNames() {
		return this.getMatchingExtensions(id_ce_subjectAltName)?.flatMap((s) => s.names.items).map((i) => ({
			type: i.type,
			value: i.value
		})) ?? [];
	}
	get issuerAlternativeNames() {
		return this.getMatchingExtensions(id_ce_issuerAltName)?.flatMap((i) => i.names.items).map((i) => ({
			type: i.type,
			value: i.value
		})) ?? [];
	}
	get sanDnsNames() {
		return this.subjectAlternativeNames.filter((san) => san.type === "dns").map((san) => san.value);
	}
	get sanUriNames() {
		return this.subjectAlternativeNames.filter((ian) => ian.type === "url").map((ian) => ian.value);
	}
	get ianDnsNames() {
		return this.issuerAlternativeNames.filter((san) => san.type === "dns").map((san) => san.value);
	}
	get ianUriNames() {
		return this.issuerAlternativeNames.filter((ian) => ian.type === "url").map((ian) => ian.value);
	}
	get authorityKeyIdentifier() {
		const keyIds = this.getMatchingExtensions(id_ce_authorityKeyIdentifier)?.map((e) => e.keyId);
		if (keyIds && keyIds.length > 1) throw new X509Error("Multiple Authority Key Identifiers are not allowed");
		return keyIds?.[0];
	}
	get subjectKeyIdentifier() {
		const keyIds = this.getMatchingExtensions(id_ce_subjectKeyIdentifier)?.map((e) => e.keyId);
		if (keyIds && keyIds.length > 1) throw new X509Error("Multiple Subject Key Identifiers are not allowed");
		return keyIds?.[0];
	}
	get keyUsage() {
		const keyUsages = this.getMatchingExtensions(id_ce_keyUsage)?.map((e) => e.usages);
		if (keyUsages && keyUsages.length > 1) throw new X509Error("Multiple Key Usages are not allowed");
		if (keyUsages) return Object.values(X509KeyUsage).filter((key) => typeof key === "number").filter((flagValue) => (keyUsages[0] & flagValue) === flagValue).map((flagValue) => flagValue);
	}
	get extendedKeyUsage() {
		const extendedKeyUsages = this.getMatchingExtensions(id_ce_extKeyUsage)?.map((e) => e.usages);
		if (extendedKeyUsages && extendedKeyUsages.length > 1) throw new X509Error("Multiple Key Usages are not allowed");
		return extendedKeyUsages?.[0];
	}
	isExtensionCritical(id) {
		const extension = this.getMatchingExtensions(id);
		if (!extension) throw new X509Error(`extension with id '${id}' is not found`);
		return !!extension[0].critical;
	}
	static async create(options, webCrypto) {
		const subjectPublicKey = options.subjectPublicKey ?? options.authorityKey;
		const isSelfSignedCertificate = assymetricPublicJwkMatches(options.authorityKey.toJson(), subjectPublicKey.toJson());
		const signingKey = new CredoWebCryptoKey(options.authorityKey, publicJwkToCryptoKeyAlgorithm(options.authorityKey), false, "private", ["sign"]);
		const publicKey = new CredoWebCryptoKey(subjectPublicKey, publicJwkToCryptoKeyAlgorithm(options.authorityKey), true, "public", ["verify"]);
		const issuerName = convertName(options.issuer);
		const extensions = [];
		extensions.push(createSubjectKeyIdentifierExtension(options.extensions?.subjectKeyIdentifier, { publicJwk: subjectPublicKey }));
		extensions.push(createKeyUsagesExtension(options.extensions?.keyUsage));
		extensions.push(createExtendedKeyUsagesExtension(options.extensions?.extendedKeyUsage));
		extensions.push(createAuthorityKeyIdentifierExtension(options.extensions?.authorityKeyIdentifier, { publicJwk: options.authorityKey }));
		extensions.push(createIssuerAlternativeNameExtension(options.extensions?.issuerAlternativeName));
		extensions.push(createSubjectAlternativeNameExtension(options.extensions?.subjectAlternativeName));
		extensions.push(createBasicConstraintsExtension(options.extensions?.basicConstraints));
		extensions.push(createCrlDistributionPointsExtension(options.extensions?.crlDistributionPoints));
		if (isSelfSignedCertificate) {
			if (options.subject) throw new X509Error("Do not provide a subject name when the certificate is supposed to be self signed");
			const certificate$1 = await x509.X509CertificateGenerator.createSelfSigned({
				keys: {
					publicKey,
					privateKey: signingKey
				},
				name: issuerName,
				notBefore: options.validity?.notBefore,
				notAfter: options.validity?.notAfter,
				extensions: extensions.filter((e) => e !== void 0),
				serialNumber: options.serialNumber
			}, webCrypto);
			const certificateInstance$1 = X509Certificate.parseCertificate(certificate$1);
			if (subjectPublicKey.hasKeyId) certificateInstance$1.publicJwk.keyId = subjectPublicKey.keyId;
			return certificateInstance$1;
		}
		if (!options.subject) throw new X509Error("Provide a subject name when the certificate is not supposed to be self signed");
		const subjectName = convertName(options.subject);
		const certificate = await x509.X509CertificateGenerator.create({
			signingKey,
			publicKey,
			issuer: issuerName,
			subject: subjectName,
			notBefore: options.validity?.notBefore,
			notAfter: options.validity?.notAfter,
			extensions: extensions.filter((e) => e !== void 0)
		}, webCrypto);
		const certificateInstance = X509Certificate.parseCertificate(certificate);
		if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId;
		return certificateInstance;
	}
	get subject() {
		return this.x509Certificate.subject;
	}
	get issuer() {
		return this.x509Certificate.issuer;
	}
	async verify({ verificationDate = /* @__PURE__ */ new Date(), publicJwk, skipSignatureVerification = false }, webCrypto) {
		let publicCryptoKey;
		if (publicJwk) publicCryptoKey = new CredoWebCryptoKey(publicJwk, publicJwkToCryptoKeyAlgorithm(publicJwk), true, "public", ["verify"]);
		const isSignatureValid = skipSignatureVerification ? true : await this.x509Certificate.verify({
			signatureOnly: true,
			publicKey: publicCryptoKey
		}, webCrypto);
		const time = verificationDate.getTime();
		const isNotBeforeValid = this.x509Certificate.notBefore.getTime() <= time;
		const isNotAfterValid = time <= this.x509Certificate.notAfter.getTime();
		if (!isSignatureValid) throw new X509Error(`Certificate: '${this.x509Certificate.subject}' has an invalid signature`);
		if (!isNotBeforeValid) throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used before it is allowed`);
		if (!isNotAfterValid) throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used after it is allowed`);
	}
	/**
	* Get the thumprint of the X509 certificate in hex format.
	*/
	async getThumprintInHex(agentContext) {
		const thumbprint = await this.x509Certificate.getThumbprint(new CredoWebCrypto(agentContext));
		return TypedArrayEncoder.toHex(new Uint8Array(thumbprint));
	}
	/**
	* Get the data elements of the x509 certificate
	*/
	get data() {
		return {
			issuerName: this.x509Certificate.issuerName.toString(),
			issuer: this.x509Certificate.issuer,
			subjectName: this.x509Certificate.subjectName.toString(),
			subject: this.x509Certificate.subject,
			serialNumber: this.x509Certificate.serialNumber,
			pem: this.x509Certificate.toString(),
			notBefore: this.x509Certificate.notBefore,
			notAfter: this.x509Certificate.notAfter
		};
	}
	getIssuerNameField(field) {
		return this.x509Certificate.issuerName.getField(field);
	}
	/**
	* @param format the format to export to, defaults to `pem`
	*/
	toString(format) {
		return this.x509Certificate.toString(format ?? "pem");
	}
	equal(certificate) {
		const parsedOther = new x509.X509Certificate(certificate.rawCertificate);
		return this.x509Certificate.equal(parsedOther);
	}
};

//#endregion
export { X509Certificate, X509ExtendedKeyUsage, X509KeyUsage };
//# sourceMappingURL=X509Certificate.mjs.map