{"version":3,"sources":["../lib/ca/certificate_authority.ts","../lib/pki/toolbox_pfx.ts","../lib/toolbox/common.ts","../lib/toolbox/common2.ts","../lib/toolbox/config.ts","../lib/toolbox/debug.ts","../lib/toolbox/with_openssl/execute_openssl.ts","../lib/toolbox/with_openssl/_env.ts","../lib/toolbox/with_openssl/install_prerequisite.ts","../lib/toolbox/display.ts","../lib/toolbox/with_openssl/index.ts","../lib/toolbox/with_openssl/create_certificate_signing_request.ts","../lib/misc/subject.ts","../lib/toolbox/with_openssl/toolbox.ts","../lib/pki/templates/simple_config_template.cnf.ts","../lib/ca/templates/ca_config_template.cnf.ts","../lib/pki/certificate_manager.ts","../lib/toolbox/without_openssl/create_certificate_signing_request.ts","../lib/toolbox/without_openssl/create_self_signed_certificate.ts"],"sourcesContent":["// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n// tslint:disable:no-shadowed-variable\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n    CertificatePurpose,\n    certificateMatchesPrivateKey,\n    convertPEMtoDER,\n    exploreCertificate,\n    exploreCertificateSigningRequest,\n    generatePrivateKeyFile,\n    type PrivateKey,\n    readCertificatePEM,\n    readCertificateSigningRequest,\n    readPrivateKey,\n    Subject,\n    type SubjectOptions,\n    toPem\n} from \"node-opcua-crypto\";\nimport { createPFX } from \"../pki/toolbox_pfx\";\nimport {\n    adjustApplicationUri,\n    adjustDate,\n    certificateFileExist,\n    debugLog,\n    displaySubtitle,\n    displayTitle,\n    type Filename,\n    type KeySize,\n    makePath,\n    mkdirRecursiveSync,\n    type Params,\n    type ProcessAltNamesParam,\n    quote\n} from \"../toolbox\";\nimport {\n    createCertificateSigningRequestWithOpenSSL,\n    type ExecuteOpenSSLOptions,\n    type ExecuteOptions,\n    ensure_openssl_installed,\n    execute_openssl,\n    execute_openssl_no_failure,\n    generateStaticConfig,\n    processAltNames,\n    setEnv,\n    x509Date\n} from \"../toolbox/with_openssl\";\n\n/** Default X.500 subject used when no custom subject is provided. */\nexport const defaultSubject = \"/C=FR/ST=IDF/L=Paris/O=Local NODE-OPCUA Certificate Authority/CN=NodeOPCUA-CA\";\n\nimport _simple_config_template from \"../pki/templates/simple_config_template.cnf\";\nimport _ca_config_template from \"./templates/ca_config_template.cnf\";\n\n// tslint:disable-next-line:variable-name\nexport const configurationFileTemplate: string = _ca_config_template;\nconst configurationFileSimpleTemplate: string = _simple_config_template;\n\nconst config = {\n    certificateDir: \"INVALID\",\n    forceCA: false,\n    pkiDir: \"INVALID\"\n};\n\nconst n = makePath;\nconst q = quote;\n\n// convert 'c07b9179'  to    \"192.123.145.121\"\nfunction octetStringToIpAddress(a: string) {\n    return (\n        parseInt(a.substring(0, 2), 16).toString() +\n        \".\" +\n        parseInt(a.substring(2, 4), 16).toString() +\n        \".\" +\n        parseInt(a.substring(4, 6), 16).toString() +\n        \".\" +\n        parseInt(a.substring(6, 8), 16).toString()\n    );\n}\nassert(octetStringToIpAddress(\"c07b9179\") === \"192.123.145.121\");\nasync function construct_CertificateAuthority(certificateAuthority: CertificateAuthority): Promise<void> {\n    // create the CA directory store\n    // create the CA directory store\n    //\n    // PKI/CA\n    //     |\n    //     +-+> private\n    //     |\n    //     +-+> public\n    //     |\n    //     +-+> certs\n    //     |\n    //     +-+> crl\n    //     |\n    //     +-+> conf\n    //     |\n    //     +-f: serial\n    //     +-f: crlNumber\n    //     +-f: index.txt\n    //\n\n    const subject = certificateAuthority.subject;\n\n    const caRootDir = path.resolve(certificateAuthority.rootDir);\n\n    async function make_folders() {\n        mkdirRecursiveSync(caRootDir);\n        mkdirRecursiveSync(path.join(caRootDir, \"private\"));\n        mkdirRecursiveSync(path.join(caRootDir, \"public\"));\n        // xx execute(\"chmod 700 private\");\n        mkdirRecursiveSync(path.join(caRootDir, \"certs\"));\n        mkdirRecursiveSync(path.join(caRootDir, \"crl\"));\n        mkdirRecursiveSync(path.join(caRootDir, \"conf\"));\n    }\n    await make_folders();\n\n    async function construct_default_files() {\n        const serial = path.join(caRootDir, \"serial\");\n        if (!fs.existsSync(serial)) {\n            await fs.promises.writeFile(serial, \"1000\");\n        }\n\n        const crlNumber = path.join(caRootDir, \"crlnumber\");\n        if (!fs.existsSync(crlNumber)) {\n            await fs.promises.writeFile(crlNumber, \"1000\");\n        }\n\n        const indexFile = path.join(caRootDir, \"index.txt\");\n        if (!fs.existsSync(indexFile)) {\n            await fs.promises.writeFile(indexFile, \"\");\n        }\n    }\n\n    await construct_default_files();\n\n    const caKeyExists = fs.existsSync(path.join(caRootDir, \"private/cakey.pem\"));\n    const caCertExists = fs.existsSync(path.join(caRootDir, \"public/cacert.pem\"));\n    if (caKeyExists && caCertExists && !config.forceCA) {\n        // CA is fully initialized => do not overwrite\n        debugLog(\"CA private key and certificate already exist ... skipping\");\n        return;\n    }\n    if (caKeyExists && !caCertExists) {\n        // Partial init: key exists but certificate does not.\n        // This can happen when a previous CA creation failed\n        // (e.g. OpenSSL 3.5 authorityKeyIdentifier error).\n        // Remove the stale key so the CA is rebuilt from scratch.\n        debugLog(\"CA private key exists but cacert.pem is missing — rebuilding CA\");\n        fs.unlinkSync(path.join(caRootDir, \"private/cakey.pem\"));\n        // Also remove the stale CSR if present\n        const staleCsr = path.join(caRootDir, \"private/cakey.csr\");\n        if (fs.existsSync(staleCsr)) {\n            fs.unlinkSync(staleCsr);\n        }\n    }\n\n    // tslint:disable:no-empty\n    displayTitle(\"Create Certificate Authority (CA)\");\n\n    const indexFileAttr = path.join(caRootDir, \"index.txt.attr\");\n    if (!fs.existsSync(indexFileAttr)) {\n        await fs.promises.writeFile(indexFileAttr, \"unique_subject = no\");\n    }\n\n    const caConfigFile = certificateAuthority.configFile;\n    if (1 || !fs.existsSync(caConfigFile)) {\n        let data = configurationFileTemplate; // inlineText(configurationFile);\n        data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));\n\n        await fs.promises.writeFile(caConfigFile, data);\n    }\n\n    // http://www.akadia.com/services/ssh_test_certificate.html\n    const subjectOpt = ` -subj \"${subject.toString()}\" `;\n    processAltNames({} as Params);\n\n    const options = { cwd: caRootDir };\n    const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options);\n    const configOption = ` -config ${q(n(configFile))}`;\n\n    const keySize = certificateAuthority.keySize;\n\n    const privateKeyFilename = path.join(caRootDir, \"private/cakey.pem\");\n    const csrFilename = path.join(caRootDir, \"private/cakey.csr\");\n\n    displayTitle(`Generate the CA private Key - ${keySize}`);\n    // The first step is to create your RSA Private Key.\n    // This key is a 1025,2048,3072 or 2038 bit RSA key which is encrypted using\n    // Triple-DES and stored in a PEM format so that it is readable as ASCII text.\n    await generatePrivateKeyFile(privateKeyFilename, keySize);\n    displayTitle(\"Generate a certificate request for the CA key\");\n    // Once the private key is generated a Certificate Signing Request can be generated.\n    // The CSR is then used in one of two ways. Ideally, the CSR will be sent to a Certificate Authority, such as\n    // Thawte or Verisign who will verify the identity of the requestor and issue a signed certificate.\n    // The second option is to self-sign the CSR, which will be demonstrated in the next section\n    await execute_openssl(\n        \"req -new\" +\n            \" -sha256 \" +\n            \" -text \" +\n            \" -extensions v3_ca_req\" +\n            configOption +\n            \" -key \" +\n            q(n(privateKeyFilename)) +\n            \" -out \" +\n            q(n(csrFilename)) +\n            \" \" +\n            subjectOpt,\n        options\n    );\n\n    // xx // Step 3: Remove Passphrase from Key\n    // xx execute(\"cp private/cakey.pem private/cakey.pem.org\");\n    // xx execute(openssl_path + \" rsa -in private/cakey.pem.org -out private/cakey.pem -passin pass:\"+paraphrase);\n\n    const issuerCA = certificateAuthority._issuerCA;\n    if (issuerCA) {\n        // Subordinate (intermediate) CA — signed by the parent CA\n        displayTitle(\"Generate CA Certificate (signed by issuer CA)\");\n        const issuerCert = path.resolve(issuerCA.caCertificate);\n        const issuerKey = path.resolve(issuerCA.rootDir, \"private/cakey.pem\");\n        const issuerSerial = path.resolve(issuerCA.rootDir, \"serial\");\n        await execute_openssl(\n            \" x509 -sha256 -req -days 3650 \" +\n                \" -text \" +\n                \" -extensions v3_ca\" +\n                \" -extfile \" +\n                q(n(configFile)) +\n                \" -in private/cakey.csr \" +\n                \" -CA \" +\n                q(n(issuerCert)) +\n                \" -CAkey \" +\n                q(n(issuerKey)) +\n                \" -CAserial \" +\n                q(n(issuerSerial)) +\n                \" -out public/cacert.pem\",\n            options\n        );\n    } else {\n        // Root CA — self-signed\n        displayTitle(\"Generate CA Certificate (self-signed)\");\n        await execute_openssl(\n            \" x509 -sha256 -req -days 3650 \" +\n                \" -text \" +\n                \" -extensions v3_ca\" +\n                \" -extfile \" +\n                q(n(configFile)) +\n                \" -in private/cakey.csr \" +\n                \" -signkey \" +\n                q(n(privateKeyFilename)) +\n                \" -out public/cacert.pem\",\n            options\n        );\n    }\n    displaySubtitle(\"generate initial CRL (Certificate Revocation List)\");\n    await regenerateCrl(certificateAuthority.revocationList, configOption, options);\n    displayTitle(\"Create Certificate Authority (CA) ---> DONE\");\n}\n\nasync function regenerateCrl(revocationList: string, configOption: string, options: ExecuteOpenSSLOptions) {\n    // produce a CRL in PEM format\n    displaySubtitle(\"regenerate CRL (Certificate Revocation List)\");\n    await execute_openssl(`ca -gencrl ${configOption} -out crl/revocation_list.crl`, options);\n    await execute_openssl(\"crl \" + \" -in  crl/revocation_list.crl -out  crl/revocation_list.der \" + \" -outform der\", options);\n\n    displaySubtitle(\"Display (Certificate Revocation List)\");\n    await execute_openssl(`crl  -in ${q(n(revocationList))} -text  -noout`, options);\n}\n\n/**\n * Result of {@link CertificateAuthority.initializeCSR}.\n *\n * - `\"ready\"` — the CA certificate already exists and is valid.\n * - `\"pending\"` — key + CSR exist but no cert; waiting for external signing.\n * - `\"created\"` — a fresh key + CSR were just generated.\n * - `\"expired\"` — the CA certificate has expired (or will expire within\n *   the configured threshold). A new CSR has been generated for renewal\n *   while preserving the existing private key.\n */\nexport type InitializeCSRResult =\n    | { status: \"ready\" }\n    | { status: \"pending\"; csrPath: string }\n    | { status: \"created\"; csrPath: string }\n    | { status: \"expired\"; csrPath: string; expiryDate: Date };\n\n/**\n * Result of {@link CertificateAuthority.installCACertificate}.\n *\n * - `\"success\"` — the certificate was installed and CRL generated.\n * - `\"error\"` — the certificate was rejected (see `reason`).\n */\nexport type InstallCACertificateResult = { status: \"success\" } | { status: \"error\"; reason: string; message: string };\n\n/**\n * Options for creating a {@link CertificateAuthority}.\n */\nexport interface CertificateAuthorityOptions {\n    /** RSA key size for the CA private key. */\n    keySize: KeySize;\n    /** Filesystem path where the CA directory structure is stored. */\n    location: string;\n    /**\n     * X.500 subject for the CA certificate.\n     * Accepts a slash-delimited string (e.g. `\"/CN=My CA/O=Acme\"`) or\n     * a structured {@link SubjectOptions} object.\n     *\n     * @defaultValue {@link defaultSubject}\n     */\n    subject?: string | SubjectOptions;\n    /**\n     * Parent CA that will sign this CA's certificate.\n     * If omitted, the CA is self-signed (root CA).\n     * The parent CA must be initialized before this CA.\n     */\n    issuerCA?: CertificateAuthority;\n}\n\n/**\n * An OpenSSL-based Certificate Authority (CA) that can create,\n * sign, and revoke X.509 certificates.\n *\n * The CA maintains a standard OpenSSL directory layout under\n * {@link CertificateAuthority.rootDir | rootDir}:\n *\n * ```\n * <location>/\n *   ├── conf/           OpenSSL configuration\n *   ├── private/        CA private key (cakey.pem)\n *   ├── public/         CA certificate  (cacert.pem)\n *   ├── certs/          Signed certificates\n *   ├── crl/            Revocation lists\n *   ├── serial          Next serial number\n *   ├── crlnumber       Next CRL number\n *   └── index.txt       Certificate database\n * ```\n *\n * @example\n * ```ts\n * const ca = new CertificateAuthority({\n *     keySize: 2048,\n *     location: \"/var/pki/CA\"\n * });\n * await ca.initialize();\n * ```\n */\n\n// ---------------------------------------------------------------\n// Certificate database types (US-057)\n// ---------------------------------------------------------------\n\n/**\n * A record from the OpenSSL CA certificate database\n * (`index.txt`).\n */\nexport interface IssuedCertificateRecord {\n    /** Hex-encoded serial number (e.g. `\"1000\"`). */\n    serial: string;\n    /** Certificate status. */\n    status: \"valid\" | \"revoked\" | \"expired\";\n    /** X.500 subject string (slash-delimited). */\n    subject: string;\n    /** Certificate expiry date as ISO-8601 string. */\n    expiryDate: string;\n    /**\n     * Revocation date as ISO-8601 string.\n     * Only present when `status === \"revoked\"`.\n     */\n    revocationDate?: string;\n}\n\n/**\n * Parse an OpenSSL date string (`YYMMDDHHmmssZ`) into an\n * ISO-8601 string.\n */\nfunction parseOpenSSLDate(dateStr: string): string {\n    // Revocation dates may have a reason suffix: \"YYMMDDHHmmssZ,reason\"\n    // Strip anything after the first comma.\n    const raw = dateStr?.split(\",\")[0] ?? \"\";\n    if (raw.length < 12) return \"\";\n    // OpenSSL uses 2-digit year; 70+ is 19xx, <70 is 20xx\n    const yy = parseInt(raw.substring(0, 2), 10);\n    const year = yy >= 70 ? 1900 + yy : 2000 + yy;\n    const month = raw.substring(2, 4);\n    const day = raw.substring(4, 6);\n    const hour = raw.substring(6, 8);\n    const min = raw.substring(8, 10);\n    const sec = raw.substring(10, 12);\n    return `${year}-${month}-${day}T${hour}:${min}:${sec}Z`;\n}\n\n/**\n * Options for {@link CertificateAuthority.signCertificateRequestFromDER}.\n *\n * All fields are optional. When provided, they override the\n * corresponding values from the CSR.\n */\nexport interface SignCertificateOptions {\n    /** Certificate validity in days (default: 365). */\n    validity?: number;\n    /** Override the certificate start date. */\n    startDate?: Date;\n    /** Override DNS SANs. */\n    dns?: string[];\n    /** Override IP SANs. */\n    ip?: string[];\n    /** Override the application URI SAN. */\n    applicationUri?: string;\n    /** Override the X.500 subject. */\n    subject?: SubjectOptions | string;\n}\n\n/**\n * Options for {@link CertificateAuthority.generateKeyPairAndSignDER}.\n */\nexport interface GenerateKeyPairAndSignOptions {\n    /** OPC UA application URI (required). */\n    applicationUri: string;\n    /** X.500 subject for the certificate (e.g. \"CN=MyApp\"). */\n    subject?: SubjectOptions | string;\n    /** DNS host names for the SAN extension. */\n    dns?: string[];\n    /** IP addresses for the SAN extension. */\n    ip?: string[];\n    /** Certificate validity in days (default: 365). */\n    validity?: number;\n    /** Certificate start date (default: now). */\n    startDate?: Date;\n    /** RSA key size in bits (default: 2048). */\n    keySize?: KeySize;\n}\n\n/**\n * Options for {@link CertificateAuthority.generateKeyPairAndSignPFX}.\n *\n * Extends the DER options with an optional `passphrase` to protect\n * the PFX bundle.\n */\nexport interface GenerateKeyPairAndSignPFXOptions extends GenerateKeyPairAndSignOptions {\n    /**\n     * Passphrase to protect the PFX file.\n     * If omitted, the PFX is created without a password.\n     */\n    passphrase?: string;\n}\n\nexport class CertificateAuthority {\n    /** RSA key size used when generating the CA private key. */\n    public readonly keySize: KeySize;\n    /** Root filesystem path of the CA directory structure. */\n    public readonly location: string;\n    /** X.500 subject of the CA certificate. */\n    public readonly subject: Subject;\n\n    /** @internal Parent CA (undefined for root CAs). */\n    readonly _issuerCA?: CertificateAuthority;\n\n    constructor(options: CertificateAuthorityOptions) {\n        assert(Object.prototype.hasOwnProperty.call(options, \"location\"));\n        assert(Object.prototype.hasOwnProperty.call(options, \"keySize\"));\n        this.location = options.location;\n        this.keySize = options.keySize || 2048;\n        this.subject = new Subject(options.subject || defaultSubject);\n        this._issuerCA = options.issuerCA;\n    }\n\n    /** Absolute path to the CA root directory (alias for {@link location}). */\n    public get rootDir() {\n        return this.location;\n    }\n\n    /** Path to the OpenSSL configuration file (`conf/caconfig.cnf`). */\n    public get configFile() {\n        return path.normalize(path.join(this.rootDir, \"./conf/caconfig.cnf\"));\n    }\n\n    /** Path to the CA certificate in PEM format (`public/cacert.pem`). */\n    public get caCertificate() {\n        // the Certificate Authority Certificate\n        return makePath(this.rootDir, \"./public/cacert.pem\");\n    }\n\n    /**\n     * Path to the issuer certificate chain (`public/issuer_chain.pem`).\n     *\n     * This file is created by {@link installCACertificate} when the\n     * provided cert file contains additional issuer certificates\n     * (e.g. intermediate + root). It is appended to signed certs\n     * by {@link constructCertificateChain} to produce a full chain\n     * per OPC UA Part 6 §6.2.6.\n     */\n    public get issuerCertificateChain() {\n        return makePath(this.rootDir, \"./public/issuer_chain.pem\");\n    }\n\n    /**\n     * Path to the current Certificate Revocation List in DER format.\n     * (`crl/revocation_list.der`)\n     */\n    public get revocationListDER() {\n        return makePath(this.rootDir, \"./crl/revocation_list.der\");\n    }\n\n    /**\n     * Path to the current Certificate Revocation List in PEM format.\n     * (`crl/revocation_list.crl`)\n     */\n    public get revocationList() {\n        return makePath(this.rootDir, \"./crl/revocation_list.crl\");\n    }\n\n    /**\n     * Path to the concatenated CA certificate + CRL file.\n     * Used by OpenSSL for CRL-based verification.\n     */\n    public get caCertificateWithCrl() {\n        return makePath(this.rootDir, \"./public/cacertificate_with_crl.pem\");\n    }\n\n    // ---------------------------------------------------------------\n    // Buffer-based accessors (US-059)\n    // ---------------------------------------------------------------\n\n    /**\n     * Return the CA certificate as a DER-encoded buffer.\n     *\n     * @throws if the CA certificate file does not exist\n     *   (call {@link initialize} first).\n     */\n    public getCACertificateDER(): Buffer {\n        const pem = readCertificatePEM(this.caCertificate);\n        return convertPEMtoDER(pem);\n    }\n\n    /**\n     * Return the CA certificate as a PEM-encoded string.\n     *\n     * @throws if the CA certificate file does not exist\n     *   (call {@link initialize} first).\n     */\n    public getCACertificatePEM(): string {\n        const raw = readCertificatePEM(this.caCertificate);\n        // OpenSSL CA cert files may include a human-readable text\n        // dump before the PEM block — strip it.\n        const beginMarker = \"-----BEGIN CERTIFICATE-----\";\n        const idx = raw.indexOf(beginMarker);\n        if (idx > 0) {\n            return raw.substring(idx);\n        }\n        return raw;\n    }\n\n    /**\n     * Return the current Certificate Revocation List as a\n     * DER-encoded buffer.\n     *\n     * Returns an empty buffer if no CRL has been generated yet.\n     */\n    public getCRLDER(): Buffer {\n        const crlPath = this.revocationListDER;\n        if (!fs.existsSync(crlPath)) {\n            return Buffer.alloc(0);\n        }\n        return fs.readFileSync(crlPath);\n    }\n\n    /**\n     * Return the current Certificate Revocation List as a\n     * PEM-encoded string.\n     *\n     * Returns an empty string if no CRL has been generated yet.\n     */\n    public getCRLPEM(): string {\n        const crlPath = this.revocationList;\n        if (!fs.existsSync(crlPath)) {\n            return \"\";\n        }\n        const raw = fs.readFileSync(crlPath, \"utf-8\");\n        // OpenSSL CRL files may include a human-readable text\n        // dump before the PEM block — strip it.\n        const beginMarker = \"-----BEGIN X509 CRL-----\";\n        const idx = raw.indexOf(beginMarker);\n        if (idx > 0) {\n            return raw.substring(idx);\n        }\n        return raw;\n    }\n\n    // ---------------------------------------------------------------\n    // Certificate database API (US-057)\n    // ---------------------------------------------------------------\n\n    /**\n     * Return a list of all issued certificates recorded in the\n     * OpenSSL `index.txt` database.\n     *\n     * Each entry includes the serial number, subject, status,\n     * expiry date, and (for revoked certs) the revocation date.\n     */\n    public getIssuedCertificates(): IssuedCertificateRecord[] {\n        return this._parseIndexTxt();\n    }\n\n    /**\n     * Return the total number of certificates recorded in\n     * `index.txt`.\n     */\n    public getIssuedCertificateCount(): number {\n        return this._parseIndexTxt().length;\n    }\n\n    /**\n     * Return the status of a certificate by its serial number.\n     *\n     * @param serial - hex-encoded serial number (e.g. `\"1000\"`)\n     * @returns `\"valid\"`, `\"revoked\"`, `\"expired\"`, or\n     *   `undefined` if not found\n     */\n    public getCertificateStatus(serial: string): \"valid\" | \"revoked\" | \"expired\" | undefined {\n        const upper = serial.toUpperCase();\n        const record = this._parseIndexTxt().find((r) => r.serial.toUpperCase() === upper);\n        return record?.status;\n    }\n\n    /**\n     * Read a specific issued certificate by serial number and\n     * return its content as a DER-encoded buffer.\n     *\n     * OpenSSL stores signed certificates in the `certs/`\n     * directory using the naming convention `<SERIAL>.pem`.\n     *\n     * @param serial - hex-encoded serial number (e.g. `\"1000\"`)\n     * @returns the DER buffer, or `undefined` if not found\n     */\n    public getCertificateBySerial(serial: string): Buffer | undefined {\n        const upper = serial.toUpperCase();\n        const certFile = path.join(this.rootDir, \"certs\", `${upper}.pem`);\n        if (!fs.existsSync(certFile)) {\n            return undefined;\n        }\n        const pem = readCertificatePEM(certFile);\n        return convertPEMtoDER(pem);\n    }\n\n    /**\n     * Path to the OpenSSL certificate database file.\n     */\n    public get indexFile(): string {\n        return path.join(this.rootDir, \"index.txt\");\n    }\n\n    /**\n     * Parse the OpenSSL `index.txt` certificate database.\n     *\n     * Each line has tab-separated fields:\n     * ```\n     * status  expiry  [revocationDate]  serial  unknown  subject\n     * ```\n     *\n     * - status: `V` (valid), `R` (revoked), `E` (expired)\n     * - expiry: `YYMMDDHHmmssZ`\n     * - revocationDate: present only for revoked certs\n     * - serial: hex string\n     * - unknown: always `\"unknown\"`\n     * - subject: X.500 slash-delimited string\n     */\n    private _parseIndexTxt(): IssuedCertificateRecord[] {\n        const indexPath = this.indexFile;\n        if (!fs.existsSync(indexPath)) {\n            return [];\n        }\n\n        const content = fs.readFileSync(indexPath, \"utf-8\");\n        const lines = content.split(\"\\n\").filter((l) => l.trim().length > 0);\n        const records: IssuedCertificateRecord[] = [];\n\n        for (const line of lines) {\n            const fields = line.split(\"\\t\");\n            if (fields.length < 4) continue;\n\n            const statusChar = fields[0];\n            const expiryStr = fields[1];\n\n            let serial: string;\n            let subject: string;\n            let revocationDate: string | undefined;\n\n            if (statusChar === \"R\") {\n                // Revoked: status  expiry  revocationDate  serial  unknown  subject\n                revocationDate = fields[2];\n                serial = fields[3];\n                subject = fields.length >= 6 ? fields[5] : \"\";\n            } else {\n                // Valid/Expired: status  expiry  (empty)  serial  unknown  subject\n                serial = fields[3];\n                subject = fields.length >= 6 ? fields[5] : \"\";\n            }\n\n            let status: \"valid\" | \"revoked\" | \"expired\";\n            switch (statusChar) {\n                case \"V\":\n                    status = \"valid\";\n                    break;\n                case \"R\":\n                    status = \"revoked\";\n                    break;\n                case \"E\":\n                    status = \"expired\";\n                    break;\n                default:\n                    continue; // skip unknown status\n            }\n\n            records.push({\n                serial,\n                status,\n                subject,\n                expiryDate: parseOpenSSLDate(expiryStr),\n                revocationDate: revocationDate ? parseOpenSSLDate(revocationDate) : undefined\n            });\n        }\n\n        return records;\n    }\n\n    // ---------------------------------------------------------------\n    // Buffer-based CA operations (US-058)\n    // ---------------------------------------------------------------\n\n    /**\n     * Sign a DER-encoded Certificate Signing Request and return\n     * the signed certificate as a DER buffer.\n     *\n     * This method handles temp-file creation and cleanup\n     * internally so that callers can work with in-memory\n     * buffers only.\n     *\n     * The CA can override fields from the CSR by passing\n     * `options.dns`, `options.ip`, `options.applicationUri`,\n     * `options.startDate`, or `options.subject`.\n     *\n     * @param csrDer - the CSR as a DER-encoded buffer\n     * @param options - signing options and CA overrides\n     * @returns the signed certificate as a DER-encoded buffer\n     */\n    public async signCertificateRequestFromDER(csrDer: Buffer, options?: SignCertificateOptions): Promise<Buffer> {\n        const validity = options?.validity ?? 365;\n        const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"pki-sign-\"));\n\n        try {\n            const csrFile = path.join(tmpDir, \"request.csr\");\n            const certFile = path.join(tmpDir, \"certificate.pem\");\n\n            // Write CSR as PEM\n            const csrPem = toPem(csrDer, \"CERTIFICATE REQUEST\");\n            await fs.promises.writeFile(csrFile, csrPem, \"utf-8\");\n\n            // Build signing parameters — CA overrides take precedence\n            const signingParams: Params = { validity };\n            if (options?.startDate) signingParams.startDate = options.startDate;\n            if (options?.dns) signingParams.dns = options.dns;\n            if (options?.ip) signingParams.ip = options.ip;\n            if (options?.applicationUri) signingParams.applicationUri = options.applicationUri;\n            if (options?.subject) signingParams.subject = options.subject;\n\n            // Delegate to the existing file-based method\n            await this.signCertificateRequest(certFile, csrFile, signingParams);\n\n            // Read the signed certificate and convert to DER\n            const certPem = readCertificatePEM(certFile);\n            return convertPEMtoDER(certPem);\n        } finally {\n            await fs.promises.rm(tmpDir, {\n                recursive: true,\n                force: true\n            });\n        }\n    }\n\n    /**\n     * Generate a new RSA key pair, create an internal CSR, sign it\n     * with this CA, and return both the certificate and private key\n     * as DER-encoded buffers.\n     *\n     * The private key is **never stored** by the CA — it exists only\n     * in a temporary directory that is cleaned up after the operation.\n     *\n     * This is used by `StartNewKeyPairRequest` (OPC UA Part 12) for\n     * constrained devices that cannot generate their own keys.\n     *\n     * @param options - key generation and certificate parameters\n     * @returns `{ certificateDer, privateKey }` — certificate as DER,\n     *   private key as a branded `PrivateKey` buffer\n     */\n    public async generateKeyPairAndSignDER(options: GenerateKeyPairAndSignOptions): Promise<{\n        certificateDer: Buffer;\n        privateKey: PrivateKey;\n    }> {\n        const keySize = options.keySize ?? 2048;\n        const validity = options.validity ?? 365;\n        const startDate = options.startDate ?? new Date();\n        const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"pki-keygen-\"));\n\n        try {\n            // 1. Generate ephemeral private key\n            const privateKeyFile = path.join(tmpDir, \"private_key.pem\");\n            await generatePrivateKeyFile(privateKeyFile, keySize);\n\n            // 2. Create a minimal OpenSSL config for CSR generation\n            const configFile = path.join(tmpDir, \"openssl.cnf\");\n            await fs.promises.writeFile(configFile, configurationFileSimpleTemplate, \"utf-8\");\n\n            // 3. Create CSR using the ephemeral key\n            const csrFile = path.join(tmpDir, \"request.csr\");\n            await createCertificateSigningRequestWithOpenSSL(csrFile, {\n                rootDir: tmpDir,\n                configFile,\n                privateKey: privateKeyFile,\n                applicationUri: options.applicationUri,\n                subject: options.subject,\n                dns: options.dns ?? [],\n                ip: options.ip ?? [],\n                purpose: CertificatePurpose.ForApplication\n            });\n\n            // 4. Sign the CSR with this CA\n            const certFile = path.join(tmpDir, \"certificate.pem\");\n            await this.signCertificateRequest(certFile, csrFile, {\n                applicationUri: options.applicationUri,\n                dns: options.dns,\n                ip: options.ip,\n                startDate,\n                validity\n            });\n\n            // 5. Read results\n            const certPem = readCertificatePEM(certFile);\n            const certificateDer = convertPEMtoDER(certPem);\n            const privateKey = readPrivateKey(privateKeyFile);\n\n            return { certificateDer, privateKey };\n        } finally {\n            // 6. Securely clean up — private key is never persisted\n            await fs.promises.rm(tmpDir, {\n                recursive: true,\n                force: true\n            });\n        }\n    }\n\n    /**\n     * Generate a new RSA key pair, create an internal CSR, sign it\n     * with this CA, and return the result as a PKCS#12 (PFX)\n     * buffer bundling the certificate, private key, and CA chain.\n     *\n     * The private key is **never stored** by the CA — it exists only\n     * in a temporary directory that is cleaned up after the operation.\n     *\n     * @param options - key generation, certificate, and PFX options\n     * @returns the PFX as a `Buffer`\n     */\n    public async generateKeyPairAndSignPFX(options: GenerateKeyPairAndSignPFXOptions): Promise<Buffer> {\n        const keySize = options.keySize ?? 2048;\n        const validity = options.validity ?? 365;\n        const startDate = options.startDate ?? new Date();\n        const passphrase = options.passphrase ?? \"\";\n        const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"pki-keygen-pfx-\"));\n\n        try {\n            // 1. Generate ephemeral private key\n            const privateKeyFile = path.join(tmpDir, \"private_key.pem\");\n            await generatePrivateKeyFile(privateKeyFile, keySize);\n\n            // 2. Create a minimal OpenSSL config for CSR generation\n            const configFile = path.join(tmpDir, \"openssl.cnf\");\n            await fs.promises.writeFile(configFile, configurationFileSimpleTemplate, \"utf-8\");\n\n            // 3. Create CSR using the ephemeral key\n            const csrFile = path.join(tmpDir, \"request.csr\");\n            await createCertificateSigningRequestWithOpenSSL(csrFile, {\n                rootDir: tmpDir,\n                configFile,\n                privateKey: privateKeyFile,\n                applicationUri: options.applicationUri,\n                subject: options.subject,\n                dns: options.dns ?? [],\n                ip: options.ip ?? [],\n                purpose: CertificatePurpose.ForApplication\n            });\n\n            // 4. Sign the CSR with this CA\n            const certFile = path.join(tmpDir, \"certificate.pem\");\n            await this.signCertificateRequest(certFile, csrFile, {\n                applicationUri: options.applicationUri,\n                dns: options.dns,\n                ip: options.ip,\n                startDate,\n                validity\n            });\n\n            // 5. Bundle into PFX (include CA cert chain)\n            const pfxFile = path.join(tmpDir, \"bundle.pfx\");\n            await createPFX({\n                certificateFile: certFile,\n                privateKeyFile,\n                outputFile: pfxFile,\n                passphrase,\n                caCertificateFiles: [this.caCertificate]\n            });\n\n            // 6. Read the PFX buffer\n            return await fs.promises.readFile(pfxFile);\n        } finally {\n            // 7. Securely clean up — private key is never persisted\n            await fs.promises.rm(tmpDir, {\n                recursive: true,\n                force: true\n            });\n        }\n    }\n\n    /**\n     * Revoke a DER-encoded certificate and regenerate the CRL.\n     *\n     * Extracts the serial number from the certificate, then\n     * uses the stored cert file at `certs/<serial>.pem` for\n     * revocation — avoiding temp-file PEM format mismatches.\n     *\n     * @param certDer - the certificate as a DER-encoded buffer\n     * @param reason - CRL reason code\n     *   (default: `\"keyCompromise\"`)\n     * @throws if the certificate's serial is not found in the CA\n     */\n    public async revokeCertificateDER(certDer: Buffer, reason?: string): Promise<void> {\n        // 1. Extract serial from the DER certificate\n        const info = exploreCertificate(certDer);\n        // exploreCertificate returns serial as \"10:00\" (colon-hex)\n        // openssl stores cert files as \"1000.pem\" (plain hex upper)\n        const serial = info.tbsCertificate.serialNumber.replace(/:/g, \"\").toUpperCase();\n\n        // 2. Use the cert file that openssl ca already stored\n        const storedCertFile = path.join(this.rootDir, \"certs\", `${serial}.pem`);\n        if (!fs.existsSync(storedCertFile)) {\n            throw new Error(`Cannot revoke: no stored certificate found for serial ${serial} at ${storedCertFile}`);\n        }\n\n        // 3. Delegate to the existing file-based method\n        await this.revokeCertificate(storedCertFile, {\n            reason: reason ?? \"keyCompromise\"\n        });\n    }\n\n    /**\n     * Initialize the CA directory structure, generate the CA\n     * private key and self-signed certificate if they do not\n     * already exist.\n     */\n    public async initialize(): Promise<void> {\n        await construct_CertificateAuthority(this);\n    }\n\n    /**\n     * Initialize the CA directory structure and generate the\n     * private key + CSR **without signing**.\n     *\n     * Use this when the CA certificate will be signed by an\n     * external (third-party) root CA.  After receiving the signed\n     * certificate, call {@link installCACertificate} to complete\n     * the setup.\n     *\n     * **Idempotent / restart-safe:**\n     * - If the CA certificate exists and is valid → `{ status: \"ready\" }`\n     * - If the CA certificate has expired → `{ status: \"expired\", csrPath, expiryDate }`\n     *   (a new CSR is generated, preserving the existing private key)\n     * - If key + CSR exist but no cert (restart before install) →\n     *   `{ status: \"pending\", csrPath }` without regenerating\n     * - Otherwise → generates key + CSR → `{ status: \"created\", csrPath }`\n     *\n     * @returns an {@link InitializeCSRResult} describing the CA state\n     */\n    public async initializeCSR(): Promise<InitializeCSRResult> {\n        const caRootDir = path.resolve(this.rootDir);\n\n        // Ensure directory structure always exists\n        mkdirRecursiveSync(caRootDir);\n        for (const dir of [\"private\", \"public\", \"certs\", \"crl\", \"conf\"]) {\n            mkdirRecursiveSync(path.join(caRootDir, dir));\n        }\n\n        const caCertFile = this.caCertificate;\n        const privateKeyFile = path.join(caRootDir, \"private/cakey.pem\");\n        const csrFile = path.join(caRootDir, \"private/cakey.csr\");\n\n        // ── Case 1: cert already exists ──\n        if (fs.existsSync(caCertFile)) {\n            // Check if the certificate has expired\n            const certDer = convertPEMtoDER(readCertificatePEM(caCertFile));\n            const certInfo = exploreCertificate(certDer);\n            const notAfter = certInfo.tbsCertificate.validity.notAfter;\n            if (notAfter.getTime() < Date.now()) {\n                // Certificate has expired — regenerate CSR for renewal\n                debugLog(\"CA certificate has expired — generating renewal CSR\");\n                await this._generateCSR(caRootDir, privateKeyFile, csrFile);\n                return { status: \"expired\", csrPath: csrFile, expiryDate: notAfter };\n            }\n            debugLog(\"CA certificate already exists and is valid — ready\");\n            return { status: \"ready\" };\n        }\n\n        // ── Case 2: key + CSR exist but no cert → pending state ──\n        //    (restart between initializeCSR and installCACertificate)\n        if (fs.existsSync(privateKeyFile) && fs.existsSync(csrFile)) {\n            debugLog(\"CA key + CSR already exist — pending external signing\");\n            return { status: \"pending\", csrPath: csrFile };\n        }\n\n        // ── Case 3: fresh setup — generate key + CSR ──\n        // Create default files (serial, crlnumber, index.txt)\n        const serial = path.join(caRootDir, \"serial\");\n        if (!fs.existsSync(serial)) {\n            await fs.promises.writeFile(serial, \"1000\");\n        }\n        const crlNumber = path.join(caRootDir, \"crlnumber\");\n        if (!fs.existsSync(crlNumber)) {\n            await fs.promises.writeFile(crlNumber, \"1000\");\n        }\n        const indexFile = path.join(caRootDir, \"index.txt\");\n        if (!fs.existsSync(indexFile)) {\n            await fs.promises.writeFile(indexFile, \"\");\n        }\n        const indexFileAttr = path.join(caRootDir, \"index.txt.attr\");\n        if (!fs.existsSync(indexFileAttr)) {\n            await fs.promises.writeFile(indexFileAttr, \"unique_subject = no\");\n        }\n\n        // Write OpenSSL config\n        const caConfigFile = this.configFile;\n        let data = configurationFileTemplate;\n        data = makePath(data.replace(/%%ROOT_FOLDER%%/, caRootDir));\n        await fs.promises.writeFile(caConfigFile, data);\n\n        // Generate private key\n        if (!fs.existsSync(privateKeyFile)) {\n            await generatePrivateKeyFile(privateKeyFile, this.keySize);\n        }\n\n        // Generate CSR\n        await this._generateCSR(caRootDir, privateKeyFile, csrFile);\n        return { status: \"created\", csrPath: csrFile };\n    }\n\n    /**\n     * Check whether the CA certificate needs renewal and, if so,\n     * generate a new CSR for re-signing by the external root CA.\n     *\n     * Use this while the CA is running to detect upcoming expiry\n     * **before** it actually expires. The existing private key is\n     * preserved so previously issued certs remain valid.\n     *\n     * @param thresholdDays - number of days before expiry at which\n     *   to trigger renewal (default: 30)\n     * @returns an {@link InitializeCSRResult} — `\"expired\"` if\n     *   renewal is needed, `\"ready\"` if the cert is still valid\n     */\n    public async renewCSR(thresholdDays = 30): Promise<InitializeCSRResult> {\n        const caRootDir = path.resolve(this.rootDir);\n        const caCertFile = this.caCertificate;\n        const privateKeyFile = path.join(caRootDir, \"private/cakey.pem\");\n        const csrFile = path.join(caRootDir, \"private/cakey.csr\");\n\n        if (!fs.existsSync(caCertFile)) {\n            // No cert at all — delegate to initializeCSR\n            return this.initializeCSR();\n        }\n\n        const certDer = convertPEMtoDER(readCertificatePEM(caCertFile));\n        const certInfo = exploreCertificate(certDer);\n        const notAfter = certInfo.tbsCertificate.validity.notAfter;\n\n        const thresholdMs = thresholdDays * 24 * 60 * 60 * 1000;\n        if (notAfter.getTime() - Date.now() < thresholdMs) {\n            debugLog(`CA certificate expires within ${thresholdDays} days — generating renewal CSR`);\n            await this._generateCSR(caRootDir, privateKeyFile, csrFile);\n            return { status: \"expired\", csrPath: csrFile, expiryDate: notAfter };\n        }\n\n        return { status: \"ready\" };\n    }\n\n    /**\n     * Generate a CSR using the existing private key.\n     * @internal\n     */\n    private async _generateCSR(caRootDir: string, privateKeyFile: string, csrFile: string): Promise<void> {\n        const subjectOpt = ` -subj \"${this.subject.toString()}\" `;\n        // Reset global SAN state — required by generateStaticConfig\n        processAltNames({} as Params);\n        const options = { cwd: caRootDir };\n        const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options);\n        const configOption = ` -config ${q(n(configFile))}`;\n\n        await execute_openssl(\n            \"req -new\" +\n                \" -sha256 \" +\n                \" -text \" +\n                \" -extensions v3_ca_req\" +\n                configOption +\n                \" -key \" +\n                q(n(privateKeyFile)) +\n                \" -out \" +\n                q(n(csrFile)) +\n                \" \" +\n                subjectOpt,\n            options\n        );\n    }\n\n    /**\n     * Install an externally-signed CA certificate and generate\n     * the initial CRL.\n     *\n     * Call this after {@link initializeCSR} once the external\n     * root CA has signed the CSR.\n     *\n     * **Safety checks:**\n     * - Verifies that the certificate's public key matches the\n     *   CA private key before installing.\n     *\n     * @param signedCertFile - path to the PEM-encoded signed\n     *   CA certificate (issued by the external root CA)\n     * @returns an {@link InstallCACertificateResult} with\n     *   `status: \"success\"` or `status: \"error\"` and a `reason`\n     */\n    public async installCACertificate(signedCertFile: string): Promise<InstallCACertificateResult> {\n        const caRootDir = path.resolve(this.rootDir);\n        const caCertFile = this.caCertificate;\n        const privateKeyFile = path.join(caRootDir, \"private/cakey.pem\");\n\n        // Read the full content once — may contain a chain\n        const fullPem = await fs.promises.readFile(signedCertFile, \"utf8\");\n\n        // Split PEM blocks: first cert → cacert.pem, rest → issuer_chain.pem\n        const pemBlocks = fullPem.match(/-----BEGIN CERTIFICATE-----[\\s\\S]*?-----END CERTIFICATE-----/g);\n        if (!pemBlocks || pemBlocks.length === 0) {\n            return {\n                status: \"error\",\n                reason: \"no_certificate_found\",\n                message: \"The provided file does not contain any PEM-encoded certificate.\"\n            };\n        }\n\n        // Verify the first certificate matches the CA private key\n        const certDer = convertPEMtoDER(pemBlocks[0]);\n        const privateKey = readPrivateKey(privateKeyFile);\n        if (!certificateMatchesPrivateKey(certDer, privateKey)) {\n            return {\n                status: \"error\",\n                reason: \"certificate_key_mismatch\",\n                message:\n                    \"The provided certificate does not match the CA \" +\n                    \"private key. Ensure the certificate was signed \" +\n                    \"from the CSR generated by initializeCSR().\"\n            };\n        }\n\n        // Write the first cert (the CA cert itself)\n        await fs.promises.writeFile(caCertFile, `${pemBlocks[0]}\\n`);\n\n        // Write any additional issuer certs to the chain file\n        const issuerChainFile = this.issuerCertificateChain;\n        if (pemBlocks.length > 1) {\n            const issuerPem = `${pemBlocks.slice(1).join(\"\\n\")}\\n`;\n            await fs.promises.writeFile(issuerChainFile, issuerPem);\n            debugLog(`Stored ${pemBlocks.length - 1} issuer certificate(s) in issuer_chain.pem`);\n        } else {\n            // No issuer chain — remove stale file if present\n            if (fs.existsSync(issuerChainFile)) {\n                await fs.promises.unlink(issuerChainFile);\n            }\n        }\n\n        // Generate initial CRL\n        const options = { cwd: caRootDir };\n        const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options);\n        const configOption = ` -config ${q(n(configFile))}`;\n        await regenerateCrl(this.revocationList, configOption, options);\n\n        return { status: \"success\" };\n    }\n\n    /**\n     * Sign a CSR with CA extensions (`v3_ca`), producing a\n     * subordinate CA certificate.\n     *\n     * Unlike {@link signCertificateRequest} which signs with\n     * end-entity extensions (SANs, etc.), this method signs\n     * with `basicConstraints = CA:TRUE` and `keyUsage =\n     * keyCertSign, cRLSign`.\n     *\n     * @param certFile - output path for the signed CA cert (PEM)\n     * @param csrFile - path to the subordinate CA's CSR\n     * @param params - signing parameters\n     */\n    public async signCACertificateRequest(certFile: string, csrFile: string, params: { validity?: number }): Promise<void> {\n        const caRootDir = path.resolve(this.rootDir);\n        const options = { cwd: caRootDir };\n        const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options);\n        const validity = params.validity ?? 3650;\n\n        await execute_openssl(\n            ` x509 -sha256 -req -days ${validity}` +\n                \" -text \" +\n                \" -extensions v3_ca\" +\n                \" -extfile \" +\n                q(n(configFile)) +\n                \" -in \" +\n                q(n(csrFile)) +\n                \" -CA \" +\n                q(n(this.caCertificate)) +\n                \" -CAkey \" +\n                q(n(path.join(caRootDir, \"private/cakey.pem\"))) +\n                \" -CAserial \" +\n                q(n(path.join(caRootDir, \"serial\"))) +\n                \" -out \" +\n                q(n(certFile)),\n            options\n        );\n\n        // Append this CA's cert chain to the output so the caller\n        // receives a complete chain file ready for installCACertificate.\n        // Chain format: [signedSubordinateCert, thisCA, thisCA's issuers...]\n        await this.constructCertificateChain(certFile);\n    }\n\n    /**\n     * Rebuild the combined CA certificate + CRL file.\n     *\n     * This concatenates the CA certificate with the current\n     * revocation list so that OpenSSL can verify certificates\n     * with CRL checking enabled.\n     */\n    public async constructCACertificateWithCRL(): Promise<void> {\n        const cacertWithCRL = this.caCertificateWithCrl;\n\n        // note : in order to check if the certificate is revoked,\n        // you need to specify -crl_check and have both the CA cert and the (applicable) CRL in your trust store.\n        // There are two ways to do that:\n        // 1. concatenate cacert.pem and crl.pem into one file and use that for -CAfile.\n        // 2. use some linked\n        // ( from http://security.stackexchange.com/a/58305/59982)\n\n        if (fs.existsSync(this.revocationList)) {\n            await fs.promises.writeFile(\n                cacertWithCRL,\n                fs.readFileSync(this.caCertificate, \"utf8\") + fs.readFileSync(this.revocationList, \"utf8\")\n            );\n        } else {\n            // there is no revocation list yet\n            await fs.promises.writeFile(cacertWithCRL, fs.readFileSync(this.caCertificate));\n        }\n    }\n\n    /**\n     * Append the CA certificate to a signed certificate file,\n     * creating a PEM certificate chain.\n     *\n     * @param certificate - path to the certificate file to extend\n     */\n    public async constructCertificateChain(certificate: Filename): Promise<void> {\n        assert(fs.existsSync(certificate));\n        assert(fs.existsSync(this.caCertificate));\n\n        debugLog(chalk.yellow(\"        certificate file :\"), chalk.cyan(certificate));\n\n        // Build chain: cert + this CA cert + issuer chain (if available)\n        let chain = await fs.promises.readFile(certificate, \"utf8\");\n        chain += await fs.promises.readFile(this.caCertificate, \"utf8\");\n\n        // Append the issuer certificate chain (e.g. root CA cert)\n        // to produce a complete chain per OPC UA Part 6 §6.2.6\n        if (fs.existsSync(this.issuerCertificateChain)) {\n            chain += await fs.promises.readFile(this.issuerCertificateChain, \"utf8\");\n        }\n\n        await fs.promises.writeFile(certificate, chain);\n    }\n\n    /**\n     * Create a self-signed certificate using OpenSSL.\n     *\n     * @param certificateFile - output path for the signed certificate\n     * @param privateKey - path to the private key file\n     * @param params - certificate parameters (subject, validity, SANs)\n     */\n    public async createSelfSignedCertificate(certificateFile: Filename, privateKey: Filename, params: Params): Promise<void> {\n        assert(typeof privateKey === \"string\");\n        assert(fs.existsSync(privateKey));\n\n        if (!certificateFileExist(certificateFile)) {\n            return;\n        }\n\n        adjustDate(params);\n        adjustApplicationUri(params);\n        processAltNames(params);\n\n        const csrFile = `${certificateFile}_csr`;\n        assert(csrFile);\n        const configFile = generateStaticConfig(this.configFile, { cwd: this.rootDir });\n\n        const options = {\n            cwd: this.rootDir,\n            openssl_conf: makePath(configFile)\n        };\n\n        const configOption = \"\";\n\n        const subject = params.subject ? new Subject(params.subject).toString() : \"\";\n        const subjectOptions = subject && subject.length > 1 ? ` -subj ${subject} ` : \"\";\n\n        displaySubtitle(\"- the certificate signing request\");\n        await execute_openssl(\n            \"req \" +\n                \" -new -sha256 -text \" +\n                configOption +\n                subjectOptions +\n                \" -batch -key \" +\n                q(n(privateKey)) +\n                \" -out \" +\n                q(n(csrFile)),\n            options\n        );\n\n        displaySubtitle(\"- creating the self-signed certificate\");\n        await execute_openssl(\n            \"ca \" +\n                \" -selfsign \" +\n                \" -keyfile \" +\n                q(n(privateKey)) +\n                \" -startdate \" +\n                x509Date(params.startDate) +\n                \" -enddate \" +\n                x509Date(params.endDate) +\n                \" -batch -out \" +\n                q(n(certificateFile)) +\n                \" -in \" +\n                q(n(csrFile)),\n            options\n        );\n\n        displaySubtitle(\"- dump the certificate for a check\");\n\n        await execute_openssl(`x509 -in ${q(n(certificateFile))}  -dates -fingerprint -purpose -noout`, {});\n\n        displaySubtitle(\"- verify self-signed certificate\");\n        await execute_openssl_no_failure(`verify -verbose -CAfile ${q(n(certificateFile))} ${q(n(certificateFile))}`, options);\n\n        await fs.promises.unlink(csrFile);\n    }\n\n    /**\n     * Revoke a certificate and regenerate the CRL.\n     *\n     * @param certificate - path to the certificate file to revoke\n     * @param params - revocation parameters\n     * @param params.reason - CRL reason code\n     *   (default `\"keyCompromise\"`)\n     */\n    public async revokeCertificate(certificate: Filename, params: Params): Promise<void> {\n        const crlReasons = [\n            \"unspecified\",\n            \"keyCompromise\",\n            \"CACompromise\",\n            \"affiliationChanged\",\n            \"superseded\",\n            \"cessationOfOperation\",\n            \"certificateHold\",\n            \"removeFromCRL\"\n        ];\n\n        const configFile = generateStaticConfig(\"conf/caconfig.cnf\", { cwd: this.rootDir });\n\n        const options = {\n            cwd: this.rootDir,\n            openssl_conf: makePath(configFile)\n        };\n\n        setEnv(\"ALTNAME\", \"\");\n        const randomFile = path.join(this.rootDir, \"random.rnd\");\n        setEnv(\"RANDFILE\", randomFile);\n\n        // // tslint:disable-next-line:no-string-literal\n        // if (!fs.existsSync((process.env as any)[\"OPENSSL_CONF\"])) {\n        //     throw new Error(\"Cannot find OPENSSL_CONF\");\n        // }\n\n        const configOption = ` -config ${q(n(configFile))}`;\n\n        const reason = params.reason || \"keyCompromise\";\n        assert(crlReasons.indexOf(reason) >= 0);\n\n        displayTitle(`Revoking certificate  ${certificate}`);\n\n        displaySubtitle(\"Revoke certificate\");\n\n        await execute_openssl_no_failure(`ca -verbose ${configOption} -revoke ${q(certificate)} -crl_reason ${reason}`, options);\n        // regenerate CRL (Certificate Revocation List)\n        await regenerateCrl(this.revocationList, configOption, options);\n\n        displaySubtitle(\"Verify that certificate is revoked\");\n\n        await execute_openssl_no_failure(\n            \"verify -verbose\" +\n                // configOption +\n                \" -CRLfile \" +\n                q(n(this.revocationList)) +\n                \" -CAfile \" +\n                q(n(this.caCertificate)) +\n                \" -crl_check \" +\n                q(n(certificate)),\n            options\n        );\n\n        // produce CRL in DER format\n        displaySubtitle(\"Produce CRL in DER form \");\n        await execute_openssl(`crl  -in ${q(n(this.revocationList))} -out crl/revocation_list.der  -outform der`, options);\n        // produce CRL in PEM format with text\n        displaySubtitle(\"Produce CRL in PEM form \");\n\n        await execute_openssl(`crl  -in ${q(n(this.revocationList))} -out crl/revocation_list.pem  -outform pem -text `, options);\n    }\n\n    /**\n     * Sign a Certificate Signing Request (CSR) with this CA.\n     *\n     * The signed certificate is written to `certificate`, and the\n     * CA certificate chain plus CRL are appended to form a\n     * complete certificate chain.\n     *\n     * @param certificate - output path for the signed certificate\n     * @param certificateSigningRequestFilename - path to the CSR\n     * @param params1 - signing parameters (validity, dates, SANs)\n     * @returns the path to the signed certificate\n     */\n    public async signCertificateRequest(\n        certificate: Filename,\n        certificateSigningRequestFilename: Filename,\n        params1: Params\n    ): Promise<Filename> {\n        await ensure_openssl_installed();\n        assert(fs.existsSync(certificateSigningRequestFilename));\n        if (!certificateFileExist(certificate)) {\n            return \"\";\n        }\n        adjustDate(params1);\n        adjustApplicationUri(params1);\n        processAltNames(params1);\n\n        const options: ExecuteOptions = { cwd: this.rootDir };\n\n        // note :\n        // subjectAltName is not copied across\n        //  see https://github.com/openssl/openssl/issues/10458\n        const csr = await readCertificateSigningRequest(certificateSigningRequestFilename);\n        const csrInfo = exploreCertificateSigningRequest(csr);\n\n        const applicationUri = csrInfo.extensionRequest.subjectAltName.uniformResourceIdentifier\n            ? csrInfo.extensionRequest.subjectAltName.uniformResourceIdentifier[0]\n            : undefined;\n        if (typeof applicationUri !== \"string\") {\n            throw new Error(\"Cannot find applicationUri in CSR\");\n        }\n\n        const dns = csrInfo.extensionRequest.subjectAltName.dNSName || [];\n        let ip = csrInfo.extensionRequest.subjectAltName.iPAddress || [];\n        ip = ip.map(octetStringToIpAddress);\n\n        const params: ProcessAltNamesParam = {\n            applicationUri,\n            dns,\n            ip\n        };\n\n        processAltNames(params);\n\n        const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options);\n\n        displaySubtitle(\"- then we ask the authority to sign the certificate signing request\");\n\n        const configOption = ` -config ${configFile}`;\n        await execute_openssl(\n            \"ca \" +\n                configOption +\n                \" -startdate \" +\n                x509Date(params1.startDate) +\n                \" -enddate \" +\n                x509Date(params1.endDate) +\n                \" -batch -out \" +\n                q(n(certificate)) +\n                \" -in \" +\n                q(n(certificateSigningRequestFilename)),\n            options\n        );\n\n        displaySubtitle(\"- dump the certificate for a check\");\n        await execute_openssl(`x509 -in ${q(n(certificate))}  -dates -fingerprint -purpose -noout`, options);\n\n        displaySubtitle(\"- construct CA certificate with CRL\");\n        await this.constructCACertificateWithCRL();\n\n        // construct certificate chain\n        //   concatenate certificate with CA Certificate and revocation list\n        displaySubtitle(\"- construct certificate chain\");\n        await this.constructCertificateChain(certificate);\n        // todo\n        displaySubtitle(\"- verify certificate against the root CA\");\n        await this.verifyCertificate(certificate);\n\n        return certificate;\n    }\n\n    /**\n     * Verify a certificate against this CA.\n     *\n     * @param certificate - path to the certificate file to verify\n     */\n    public async verifyCertificate(certificate: Filename): Promise<void> {\n        // openssl verify crashes on windows! we cannot use it reliably\n        // istanbul ignore next\n        const isImplemented = false;\n\n        // istanbul ignore next\n        if (isImplemented) {\n            const options = { cwd: this.rootDir };\n            const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options);\n\n            setEnv(\"OPENSSL_CONF\", makePath(configFile));\n            const _configOption = ` -config ${configFile}`;\n            _configOption;\n            await execute_openssl_no_failure(\n                `verify -verbose  -CAfile ${q(n(this.caCertificateWithCrl))} ${q(n(certificate))}`,\n                options\n            );\n        }\n    }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\n\nimport type { Filename } from \"../toolbox/common\";\nimport { quote } from \"../toolbox/common\";\nimport { makePath } from \"../toolbox/common2\";\nimport { execute_openssl } from \"../toolbox/with_openssl/execute_openssl\";\n\nconst q = quote;\nconst n = makePath;\n\n// ── Types ──────────────────────────────────────────────────────\n\n/**\n * Options for creating a PFX (PKCS#12) file.\n */\nexport interface CreatePFXOptions {\n    /** Path to the certificate file (PEM or DER). */\n    certificateFile: Filename;\n\n    /** Path to the private key file (PEM). */\n    privateKeyFile: Filename;\n\n    /** Output path for the generated PFX file. */\n    outputFile: Filename;\n\n    /**\n     * Optional passphrase to protect the PFX file.\n     * If omitted, the PFX is created without a password.\n     */\n    passphrase?: string;\n\n    /**\n     * Optional path(s) to CA / intermediate certificate files\n     * to include in the PFX bundle.\n     */\n    caCertificateFiles?: Filename[];\n}\n\n/**\n * Options for extracting data from a PFX (PKCS#12) file.\n */\nexport interface ExtractPFXOptions {\n    /** Path to the PFX file. */\n    pfxFile: Filename;\n\n    /**\n     * Passphrase used when the PFX was created.\n     * Pass an empty string for unprotected PFX files.\n     */\n    passphrase?: string;\n}\n\n/**\n * Result of extracting data from a PFX file.\n */\nexport interface ExtractPFXResult {\n    /** The certificate in PEM format. */\n    certificate: string;\n\n    /** The private key in PEM format. */\n    privateKey: string;\n\n    /**\n     * The CA / intermediate certificates in PEM format\n     * (empty string if none).\n     */\n    caCertificates: string;\n}\n\n// ── Create PFX ─────────────────────────────────────────────────\n\n/**\n * Create a PFX (PKCS#12) file from a certificate and private key.\n *\n * Wraps:\n * ```\n * openssl pkcs12 -export\n *   -in <cert> -inkey <key>\n *   [-certfile <ca>]\n *   -out <pfx>\n *   -passout pass:<passphrase>\n * ```\n *\n * @param options — see {@link CreatePFXOptions}\n */\nexport async function createPFX(options: CreatePFXOptions): Promise<void> {\n    const { certificateFile, privateKeyFile, outputFile, passphrase = \"\", caCertificateFiles } = options;\n\n    assert(fs.existsSync(certificateFile), `Certificate file does not exist: ${certificateFile}`);\n    assert(fs.existsSync(privateKeyFile), `Private key file does not exist: ${privateKeyFile}`);\n\n    let cmd = `pkcs12 -export`;\n    cmd += ` -in ${q(n(certificateFile))}`;\n    cmd += ` -inkey ${q(n(privateKeyFile))}`;\n\n    if (caCertificateFiles) {\n        for (const caFile of caCertificateFiles) {\n            assert(fs.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);\n            cmd += ` -certfile ${q(n(caFile))}`;\n        }\n    }\n\n    cmd += ` -out ${q(n(outputFile))}`;\n    cmd += ` -passout pass:${passphrase}`;\n\n    await execute_openssl(cmd, {});\n}\n\n// ── Extract certificate from PFX ───────────────────────────────\n\n/**\n * Extract the client/server certificate from a PFX file.\n *\n * Wraps:\n * ```\n * openssl pkcs12 -in <pfx> -clcerts -nokeys\n *   -passin pass:<passphrase>\n * ```\n *\n * @returns the certificate in PEM format.\n */\nexport async function extractCertificateFromPFX(options: ExtractPFXOptions): Promise<string> {\n    const { pfxFile, passphrase = \"\" } = options;\n\n    assert(fs.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);\n\n    const cmd = `pkcs12 -in ${q(n(pfxFile))} -clcerts -nokeys -nodes -passin pass:${passphrase}`;\n\n    return await execute_openssl(cmd, {});\n}\n\n// ── Extract private key from PFX ───────────────────────────────\n\n/**\n * Extract the private key from a PFX file.\n *\n * Wraps:\n * ```\n * openssl pkcs12 -in <pfx> -nocerts -nodes\n *   -passin pass:<passphrase>\n * ```\n *\n * @returns the private key in PEM format.\n */\nexport async function extractPrivateKeyFromPFX(options: ExtractPFXOptions): Promise<string> {\n    const { pfxFile, passphrase = \"\" } = options;\n\n    assert(fs.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);\n\n    const cmd = `pkcs12 -in ${q(n(pfxFile))} -nocerts -nodes -passin pass:${passphrase}`;\n\n    return await execute_openssl(cmd, {});\n}\n\n// ── Extract CA certificates from PFX ───────────────────────────\n\n/**\n * Extract the CA / intermediate certificates from a PFX file.\n *\n * Wraps:\n * ```\n * openssl pkcs12 -in <pfx> -cacerts -nokeys -nodes\n *   -passin pass:<passphrase>\n * ```\n *\n * @returns the CA certificates in PEM format\n *          (empty string if none are present).\n */\nexport async function extractCACertificatesFromPFX(options: ExtractPFXOptions): Promise<string> {\n    const { pfxFile, passphrase = \"\" } = options;\n\n    assert(fs.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);\n\n    const cmd = `pkcs12 -in ${q(n(pfxFile))} -cacerts -nokeys -nodes -passin pass:${passphrase}`;\n\n    return await execute_openssl(cmd, {});\n}\n\n// ── Extract everything from PFX ────────────────────────────────\n\n/**\n * Extract certificate + private key + CA certs from a PFX file\n * in a single call.\n *\n * @returns an {@link ExtractPFXResult} with all PEM-encoded parts.\n */\nexport async function extractAllFromPFX(options: ExtractPFXOptions): Promise<ExtractPFXResult> {\n    const [certificate, privateKey, caCertificates] = await Promise.all([\n        extractCertificateFromPFX(options),\n        extractPrivateKeyFromPFX(options),\n        extractCACertificatesFromPFX(options)\n    ]);\n    return { certificate, privateKey, caCertificates };\n}\n\n// ── Convert PFX to PEM (combined) ──────────────────────────────\n\n/**\n * Convert a PFX file to a single PEM file containing both the\n * certificate and the private key.\n *\n * Wraps:\n * ```\n * openssl pkcs12 -in <pfx> -out <pem> -nodes\n *   -passin pass:<passphrase>\n * ```\n */\nexport async function convertPFXtoPEM(pfxFile: Filename, pemFile: Filename, passphrase = \"\"): Promise<void> {\n    assert(fs.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);\n\n    const cmd = `pkcs12 -in ${q(n(pfxFile))} -out ${q(n(pemFile))} -nodes -passin pass:${passphrase}`;\n\n    await execute_openssl(cmd, {});\n}\n\n// ── Inspect PFX ────────────────────────────────────────────────\n\n/**\n * Dump the contents of a PFX file in human-readable form.\n *\n * Wraps:\n * ```\n * openssl pkcs12 -in <pfx> -info -noout\n *   -passin pass:<passphrase>\n * ```\n *\n * @returns the human-readable dump as a string.\n */\nexport async function dumpPFX(pfxFile: Filename, passphrase = \"\"): Promise<string> {\n    assert(fs.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);\n\n    const cmd = `pkcs12 -in ${q(n(pfxFile))} -info -nodes -passin pass:${passphrase}`;\n\n    return await execute_openssl(cmd, {});\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nimport assert from \"node:assert\";\n\n/** RSA key size in bits. */\nexport type KeySize = 1024 | 2048 | 3072 | 4096;\n/** Hex-encoded SHA-1 certificate thumbprint. */\nexport type Thumbprint = string;\n/** A filesystem path to a file. */\nexport type Filename = string;\n/** Status of a certificate in the trust store. */\nexport type CertificateStatus = \"unknown\" | \"trusted\" | \"rejected\";\n\nimport type { CertificatePurpose } from \"node-opcua-crypto\";\nimport type { SubjectOptions } from \"../misc/subject\";\n\n/**\n * @deprecated Use {@link KeySize} instead.\n */\nexport type KeyLength = 1024 | 2048 | 3072 | 4096;\n\nexport function quote(str?: string): string {\n    return `\"${str || \"\"}\"`;\n}\n\n/**\n * Subject Alternative Name (SAN) parameters for certificate\n * generation.\n */\nexport interface ProcessAltNamesParam {\n    /** DNS host names to include in the SAN extension. */\n    dns?: string[];\n    /** IP addresses to include in the SAN extension. */\n    ip?: string[];\n    /** OPC UA application URI for the SAN extension. */\n    applicationUri?: string;\n}\n\n/**\n * Options for creating a Certificate Signing Request (CSR).\n */\nexport interface CreateCertificateSigningRequestOptions extends ProcessAltNamesParam {\n    /** X.500 subject for the certificate. */\n    subject?: SubjectOptions | string;\n}\n\n/**\n * Extended CSR options that include filesystem paths and\n * certificate purpose — used internally by the OpenSSL toolbox.\n */\nexport interface CreateCertificateSigningRequestWithConfigOptions extends CreateCertificateSigningRequestOptions {\n    /** Root directory of the PKI store. */\n    rootDir: Filename;\n    /** Path to the OpenSSL configuration file. */\n    configFile: Filename;\n    /** Path to the private key file. */\n    privateKey: Filename;\n    /** Intended purpose of the certificate. */\n    purpose: CertificatePurpose;\n}\n\n/**\n * Validity period parameters for certificate generation.\n */\nexport interface StartDateEndDateParam {\n    /** Certificate \"Not Before\" date. Defaults to now. */\n    startDate?: Date;\n    /** Certificate \"Not After\" date (computed from validity). */\n    endDate?: Date;\n    /** Number of days the certificate is valid. @defaultValue 365 */\n    validity?: number;\n}\n\n/**\n * Parameters for creating a self-signed certificate.\n */\nexport interface CreateSelfSignCertificateParam extends ProcessAltNamesParam, StartDateEndDateParam {\n    /** X.500 subject for the certificate. */\n    subject?: SubjectOptions | string;\n}\n\n/**\n * Extended self-signed certificate options that include\n * filesystem paths and purpose — used internally.\n */\nexport interface CreateSelfSignCertificateWithConfigParam extends CreateSelfSignCertificateParam {\n    /** Root directory of the PKI store. */\n    rootDir: Filename;\n    /** Path to the OpenSSL configuration file. */\n    configFile: Filename;\n    /** Path to the private key file. */\n    privateKey: Filename;\n    /** Intended purpose of the certificate. */\n    purpose: CertificatePurpose;\n}\n\n/**\n * General-purpose parameters passed to CA operations such as\n * {@link CertificateAuthority.signCertificateRequest} and\n * {@link CertificateAuthority.revokeCertificate}.\n */\nexport interface Params extends ProcessAltNamesParam, StartDateEndDateParam {\n    /** X.500 subject for the certificate. */\n    subject?: SubjectOptions | string;\n\n    /** Path to the private key file. */\n    privateKey?: string;\n    /** Path to the OpenSSL configuration file. */\n    configFile?: string;\n    /** Root directory of the PKI store. */\n    rootDir?: string;\n\n    /** Output filename for the generated certificate. */\n    outputFile?: string;\n    /** CRL revocation reason (e.g. `\"keyCompromise\"`). */\n    reason?: string;\n}\n\nexport function adjustDate(params: StartDateEndDateParam) {\n    assert(params instanceof Object);\n    params.startDate = params.startDate || new Date();\n    assert(params.startDate instanceof Date);\n\n    params.validity = params.validity || 365; // one year\n\n    params.endDate = new Date(params.startDate.getTime());\n    params.endDate.setDate(params.startDate.getDate() + params.validity);\n\n    // params.endDate = x509Date(endDate);\n    // params.startDate = x509Date(startDate);\n\n    assert(params.endDate instanceof Date);\n    assert(params.startDate instanceof Date);\n\n    // // istanbul ignore next\n    // if (!g_config.silent) {\n    //     warningLog(\" start Date \", params.startDate.toUTCString(), x509Date(params.startDate));\n    //     warningLog(\" end   Date \", params.endDate.toUTCString(), x509Date(params.endDate));\n    // }\n}\n\nexport function adjustApplicationUri(params: Params) {\n    const applicationUri = params.applicationUri || \"\";\n    if (applicationUri.length > 200) {\n        throw new Error(`Openssl doesn't support urn with length greater than 200${applicationUri}`);\n    }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport chalk from \"chalk\";\n\nimport { g_config } from \"./config\";\n\nimport { debugLog, warningLog } from \"./debug\";\n\nexport function certificateFileExist(certificateFile: string): boolean {\n    // istanbul ignore next\n    if (fs.existsSync(certificateFile) && !g_config.force) {\n        warningLog(\n            chalk.yellow(\"        certificate \") + chalk.cyan(certificateFile) + chalk.yellow(\" already exists => do not overwrite\")\n        );\n        return false;\n    }\n    return true;\n}\n\nexport function mkdirRecursiveSync(folder: string): void {\n    if (!fs.existsSync(folder)) {\n        // istanbul ignore next\n        debugLog(chalk.white(\" .. constructing \"), folder);\n        fs.mkdirSync(folder, { recursive: true });\n    }\n}\n\nexport function makePath(folderName: string, filename?: string): string {\n    let s: string;\n    if (filename) {\n        s = path.join(path.normalize(folderName), filename);\n    } else {\n        assert(folderName);\n        s = folderName;\n    }\n    s = s.replace(/\\\\/g, \"/\");\n    return s;\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nexport const g_config = {\n    opensslVersion: \"unset\",\n    silent: process.env.VERBOSE ? !process.env.VERBOSE : true,\n    force: false\n};\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nexport const doDebug = process.env.NODEOPCUAPKIDEBUG || false;\nexport const displayError = true;\nexport const displayDebug = !!process.env.NODEOPCUAPKIDEBUG || false;\n// tslint:disable-next-line:no-empty\nexport function debugLog(...args: unknown[]) {\n    // istanbul ignore next\n    if (displayDebug) {\n        console.log.apply(null, args);\n    }\n}\nexport function warningLog(...args: unknown[]) {\n    console.log.apply(null, args);\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n// tslint:disable:no-console\n// tslint:disable:no-shadowed-variable\n\nimport assert from \"node:assert\";\nimport child_process from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport byline from \"byline\";\nimport chalk from \"chalk\";\nimport { quote } from \"../common\";\nimport { makePath } from \"../common2\";\nimport { g_config } from \"../config\";\nimport { debugLog, displayError, doDebug, warningLog } from \"../debug\";\nimport { setEnv } from \"./_env\";\nimport { get_openssl_exec_path } from \"./install_prerequisite\";\n\n// tslint:disable-next-line:variable-name\n\nlet opensslPath: string | undefined; // not initialized\n\nconst n = makePath;\n\nexport interface ExecuteOptions {\n    cwd?: string;\n    hideErrorMessage?: boolean;\n}\n\nexport async function execute(cmd: string, options: ExecuteOptions): Promise<string> {\n    const from = new Error();\n\n    options.cwd = options.cwd || process.cwd();\n\n    // istanbul ignore next\n    if (!g_config.silent) {\n        warningLog(chalk.cyan(\"                  CWD         \"), options.cwd);\n    }\n\n    const outputs: string[] = [];\n\n    return await new Promise((resolve, reject) => {\n        const child = child_process.exec(\n            cmd,\n            {\n                cwd: options.cwd,\n                windowsHide: true\n            },\n            (err: child_process.ExecException | null) => {\n                // istanbul ignore next\n                if (err) {\n                    if (!options.hideErrorMessage) {\n                        const fence = \"###########################################\";\n                        console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));\n                        console.error(chalk.bgWhiteBright.redBright(`CWD = ${options.cwd}`));\n                        console.error(chalk.bgWhiteBright.redBright(err.message));\n                        console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));\n\n                        console.error(from.stack);\n                    }\n                    reject(new Error(err.message));\n                    return;\n                }\n                resolve(outputs.join(\"\"));\n            }\n        );\n\n        if (child.stdout) {\n            const stream2 = byline(child.stdout);\n            stream2.on(\"data\", (line: string) => {\n                outputs.push(`${line}\\n`);\n            });\n            if (!g_config.silent) {\n                stream2.on(\"data\", (line: string) => {\n                    line = line.toString();\n                    if (doDebug) {\n                        process.stdout.write(`${chalk.white(\"        stdout \") + chalk.whiteBright(line)}\\n`);\n                    }\n                });\n            }\n        }\n\n        // istanbul ignore next\n        if (!g_config.silent) {\n            if (child.stderr) {\n                const stream1 = byline(child.stderr);\n                stream1.on(\"data\", (line: string) => {\n                    line = line.toString();\n                    if (displayError) {\n                        process.stdout.write(`${chalk.white(\"        stderr \") + chalk.red(line)}\\n`);\n                    }\n                });\n            }\n        }\n    });\n}\n\nexport async function find_openssl(): Promise<string> {\n    return await get_openssl_exec_path();\n}\n\nexport async function ensure_openssl_installed(): Promise<void> {\n    if (!opensslPath) {\n        opensslPath = await find_openssl();\n        const outputs = await execute_openssl(\"version\", { cwd: \".\" });\n        g_config.opensslVersion = outputs.trim();\n        if (doDebug) {\n            warningLog(\"OpenSSL version : \", g_config.opensslVersion);\n        }\n    }\n}\n\nexport async function executeOpensslAsync(cmd: string, options: ExecuteOpenSSLOptions): Promise<string> {\n    return await execute_openssl(cmd, options);\n}\n\nexport async function execute_openssl_no_failure(cmd: string, options: ExecuteOpenSSLOptions) {\n    options = options || {};\n    options.hideErrorMessage = true;\n    try {\n        return await execute_openssl(cmd, options);\n    } catch (err) {\n        debugLog(\" (ignored error =  ERROR : )\", (err as Error).message);\n    }\n}\n\nfunction getTempFolder(): string {\n    return os.tmpdir();\n}\n\nexport interface ExecuteOpenSSLOptions extends ExecuteOptions {\n    openssl_conf?: string;\n}\n\nexport async function execute_openssl(cmd: string, options: ExecuteOpenSSLOptions): Promise<string> {\n    debugLog(\"execute_openssl\", cmd, options);\n    const empty_config_file = n(getTempFolder(), \"empty_config.cnf\");\n    if (!fs.existsSync(empty_config_file)) {\n        await fs.promises.writeFile(empty_config_file, \"# empty config file\");\n    }\n\n    options = options || {};\n    options.openssl_conf = options.openssl_conf || empty_config_file; // \"!! OPEN SLL CONF NOT DEFINED BAD FILE !!\";\n    assert(options.openssl_conf);\n    setEnv(\"OPENSSL_CONF\", options.openssl_conf);\n\n    // istanbul ignore next\n    if (!g_config.silent) {\n        warningLog(chalk.cyan(\"                  OPENSSL_CONF\"), process.env.OPENSSL_CONF);\n        warningLog(chalk.cyan(\"                  RANDFILE    \"), process.env.RANDFILE);\n        warningLog(chalk.cyan(\"                  CMD         openssl \"), chalk.cyanBright(cmd));\n    }\n    await ensure_openssl_installed();\n    return await execute(`${quote(opensslPath)} ${cmd}`, options);\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nimport type { ProcessAltNamesParam } from \"../common\";\nimport { g_config } from \"../config\";\nimport { warningLog } from \"../debug\";\n\nexport const exportedEnvVars: Record<string, string> = {};\n\nexport function setEnv(varName: string, value: string): void {\n    // istanbul ignore next\n    if (!g_config.silent) {\n        warningLog(`          set ${varName}=${value}`);\n    }\n    exportedEnvVars[varName] = value;\n\n    if ([\"OPENSSL_CONF\"].indexOf(varName) >= 0) {\n        process.env[varName] = value;\n    }\n    if ([\"RANDFILE\"].indexOf(varName) >= 0) {\n        process.env[varName] = value;\n    }\n}\n\nexport function hasEnv(varName: string): boolean {\n    return Object.prototype.hasOwnProperty.call(exportedEnvVars, varName);\n}\nexport function getEnv(varName: string): string {\n    return exportedEnvVars[varName];\n}\n\nexport function getEnvironmentVarNames(): { key: string; pattern: string }[] {\n    return Object.keys(exportedEnvVars).map((varName: string) => {\n        return { key: varName, pattern: `\\\\$ENV\\\\:\\\\:${varName}` };\n    });\n}\n\nexport function processAltNames(params: ProcessAltNamesParam) {\n    params.dns = params.dns || [];\n    params.ip = params.ip || [];\n\n    // construct subjectAtlName\n    let subjectAltName: string[] = [];\n    subjectAltName.push(`URI:${params.applicationUri}`);\n    subjectAltName = ([] as string[]).concat(\n        subjectAltName,\n        params.dns.map((d: string) => `DNS:${d}`)\n    );\n    subjectAltName = ([] as string[]).concat(\n        subjectAltName,\n        params.ip.map((d: string) => `IP:${d}`)\n    );\n    const subjectAltNameString = subjectAltName.join(\", \");\n    setEnv(\"ALTNAME\", subjectAltNameString);\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n// tslint:disable:no-console\n// tslint:disable:no-shadowed-variable\n\nimport child_process from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport url from \"node:url\";\n\nimport byline from \"byline\";\nimport chalk from \"chalk\";\nimport ProgressBar from \"progress\";\nimport wget from \"wget-improved-2\";\nimport yauzl from \"yauzl\";\n\nimport { warningLog } from \"../debug\";\n\nconst doDebug = process.env.NODEOPCUAPKIDEBUG || false;\n\ndeclare interface ProxyOptions {\n    host: string;\n    port: number;\n    localAddress?: string;\n    proxyAuth?: string;\n    headers?: Record<string, string>;\n    protocol: string; // \"https\" | \"http\"\n}\ndeclare interface WgetOptions {\n    gunzip?: boolean;\n    proxy?: ProxyOptions;\n}\n\ndeclare interface WgetInterface {\n    download(url: string, outputFilename: string, options: WgetOptions): NodeJS.EventEmitter;\n}\n\ninterface ExecuteResult {\n    exitCode: number;\n    output: string;\n}\n\nfunction makeOptions(): WgetOptions {\n    const proxy =\n        process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || undefined;\n    if (proxy) {\n        const a = new url.URL(proxy);\n        const auth = a.username ? `${a.username}:${a.password}` : undefined;\n\n        const options: WgetOptions = {\n            proxy: {\n                port: a.port ? parseInt(a.port, 10) : 80,\n                protocol: a.protocol.replace(\":\", \"\"),\n                host: a.hostname ?? \"\",\n                proxyAuth: auth\n            }\n        };\n        warningLog(chalk.green(\"- using proxy \"), proxy);\n        warningLog(options);\n        return options;\n    }\n    return {};\n}\n\nasync function execute(cmd: string, cwd?: string): Promise<ExecuteResult> {\n    let output = \"\";\n\n    // xx cwd = cwd ? {cwd: cwd} : {};\n    const options = {\n        cwd,\n        windowsHide: true\n    };\n\n    return await new Promise<ExecuteResult>((resolve, reject) => {\n        const child = child_process.exec(\n            cmd,\n            options,\n            (err: child_process.ExecException | null /*, stdout: string, stderr: string*/) => {\n                const exitCode = err === null ? 0 : err.code || 1;\n                if (err) reject(err);\n                else {\n                    resolve({ exitCode, output });\n                }\n            }\n        );\n\n        const stream1 = byline(child.stdout as Readable);\n        stream1.on(\"data\", (line: string) => {\n            output += `${line}\\n`;\n            // istanbul ignore next\n            if (doDebug) {\n                process.stdout.write(`        stdout ${chalk.yellow(line)}\\n`);\n            }\n        });\n    });\n}\n\nfunction quote(str: string): string {\n    return `\"${str.replace(/\\\\/g, \"/\")}\"`;\n}\n\nfunction is_expected_openssl_version(strVersion: string): boolean {\n    return !!strVersion.match(/OpenSSL 1|3/);\n}\n\nasync function getopensslExecPath(): Promise<string> {\n    let result1: ExecuteResult | undefined;\n    try {\n        result1 = await execute(\"which openssl\");\n    } catch (err) {\n        warningLog(\"warning: \", (err as Error).message);\n        throw new Error(\"Cannot find openssl\");\n    }\n\n    const exitCode = result1?.exitCode;\n    const output = result1?.output;\n\n    if (exitCode !== 0) {\n        warningLog(chalk.yellow(\" it seems that \") + chalk.cyan(\"openssl\") + chalk.yellow(\" is not installed on your computer \"));\n        warningLog(chalk.yellow(\"Please install it before running this programs\"));\n        throw new Error(\"Cannot find openssl\");\n    }\n    const opensslExecPath = output.replace(/\\n\\r/g, \"\").trim();\n    return opensslExecPath;\n}\nexport async function check_system_openssl_version(): Promise<string> {\n    const opensslExecPath = await getopensslExecPath();\n\n    // tslint:disable-next-line:variable-name\n    const q_opensslExecPath = quote(opensslExecPath);\n\n    // istanbul ignore next\n    if (doDebug) {\n        warningLog(`              OpenSSL found in : ${chalk.yellow(opensslExecPath)}`);\n    }\n    // ------------------------ now verify that openssl version is the correct one\n    const result = await execute(`${q_opensslExecPath} version`);\n\n    const exitCode = result?.exitCode;\n    const output = result?.output;\n\n    const version = output.trim();\n\n    const versionOK = exitCode === 0 && is_expected_openssl_version(version);\n    if (!versionOK) {\n        let message =\n            chalk.whiteBright(\"Warning !!!!!!!!!!!! \") +\n            \"\\nyour version of openssl is \" +\n            version +\n            \". It doesn't match the expected version\";\n\n        if (process.platform === \"darwin\") {\n            message +=\n                chalk.cyan(\"\\nplease refer to :\") +\n                chalk.yellow(\" https://github.com/node-opcua/node-opcua/\" + \"wiki/installing-node-opcua-or-node-red-on-MacOS\");\n        }\n\n        console.log(message);\n    }\n    return output;\n}\n\nasync function install_and_check_win32_openssl_version(): Promise<string> {\n    const downloadFolder = path.join(os.tmpdir(), \".\");\n\n    function get_openssl_folder_win32(): string {\n        if (process.env.LOCALAPPDATA) {\n            const userProgramFolder = path.join(process.env.LOCALAPPDATA, \"Programs\");\n            if (fs.existsSync(userProgramFolder)) {\n                return path.join(userProgramFolder, \"openssl\");\n            }\n        }\n        return path.join(process.cwd(), \"openssl\");\n    }\n\n    function get_openssl_exec_path_win32(): string {\n        const opensslFolder = get_openssl_folder_win32();\n        return path.join(opensslFolder, \"openssl.exe\");\n    }\n\n    async function check_openssl_win32(): Promise<{ opensslOk?: boolean; version?: string }> {\n        const opensslExecPath = get_openssl_exec_path_win32();\n\n        const exists = fs.existsSync(opensslExecPath);\n        if (!exists) {\n            warningLog(\"checking presence of \", opensslExecPath);\n            warningLog(chalk.red(\" cannot find file \") + opensslExecPath);\n            return {\n                opensslOk: false,\n                version: `cannot find file ${opensslExecPath}`\n            };\n        } else {\n            // tslint:disable-next-line:variable-name\n            const q_openssl_exe_path = quote(opensslExecPath);\n            const cwd = \".\";\n\n            const { exitCode, output } = await execute(`${q_openssl_exe_path} version`, cwd);\n            const version = output.trim();\n            // istanbul ignore next\n\n            if (doDebug) {\n                warningLog(\" Version = \", version);\n            }\n            return {\n                opensslOk: exitCode === 0 && is_expected_openssl_version(version),\n                version\n            };\n        }\n    }\n\n    /**\n     * detect whether windows OS is a 64 bits or 32 bits\n     * http://ss64.com/nt/syntax-64bit.html\n     * http://blogs.msdn.com/b/david.wang/archive/2006/03/26/howto-detect-process-bitness.aspx\n     * @return {number}\n     */\n    function win32or64(): 32 | 64 {\n        if (process.env.PROCESSOR_ARCHITECTURE === \"x86\" && process.env.PROCESSOR_ARCHITEW6432) {\n            return 64;\n        }\n\n        if (process.env.PROCESSOR_ARCHITECTURE === \"AMD64\") {\n            return 64;\n        }\n\n        // check if we are running node  x32 on a x64 arch\n        if (process.env.CURRENT_CPU === \"x64\") {\n            return 64;\n        }\n        return 32;\n    }\n\n    async function download_openssl(): Promise<{ downloadedFile: string }> {\n        // const url = (win32or64() === 64 )\n        //         ? \"http://indy.fulgan.com/SSL/openssl-1.0.2o-x64_86-win64.zip\"\n        //         : \"http://indy.fulgan.com/SSL/openssl-1.0.2o-i386-win32.zip\"\n        //     ;\n        const url =\n            win32or64() === 64\n                ? \"https://github.com/node-opcua/node-opcua-pki/releases/download/2.14.2/openssl-1.0.2u-x64_86-win64.zip\"\n                : \"https://github.com/node-opcua/node-opcua-pki/releases/download/2.14.2/openssl-1.0.2u-i386-win32.zip\";\n        // the zip file\n        const outputFilename = path.join(downloadFolder, path.basename(url));\n\n        warningLog(`downloading ${chalk.yellow(url)} to ${outputFilename}`);\n\n        if (fs.existsSync(outputFilename)) {\n            return { downloadedFile: outputFilename };\n        }\n        const options = makeOptions();\n        const bar = new ProgressBar(chalk.cyan(\"[:bar]\") + chalk.cyan(\" :percent \") + chalk.white(\":etas\"), {\n            complete: \"=\",\n            incomplete: \" \",\n            total: 100,\n            width: 100\n        });\n\n        return await new Promise((resolve, reject) => {\n            const download = wget.download(url, outputFilename, options);\n            download.on(\"error\", (err: Error) => {\n                warningLog(err);\n                setImmediate(() => {\n                    reject(err);\n                });\n            });\n            download.on(\"end\", (output: string) => {\n                // istanbul ignore next\n                if (doDebug) {\n                    warningLog(output);\n                }\n                // warningLog(\"done ...\");\n                resolve({ downloadedFile: outputFilename });\n            });\n            download.on(\"progress\", (progress: number) => {\n                bar.update(progress);\n            });\n        });\n    }\n\n    async function unzip_openssl(zipFilename: string) {\n        const opensslFolder = get_openssl_folder_win32();\n\n        const zipFile = await new Promise<yauzl.ZipFile>((resolve, reject) => {\n            yauzl.open(zipFilename, { lazyEntries: true }, (err?: Error | null, zipfile?: yauzl.ZipFile) => {\n                if (err) {\n                    reject(err);\n                } else {\n                    if (!zipfile) {\n                        reject(new Error(\"zipfile is null\"));\n                    } else {\n                        resolve(zipfile);\n                    }\n                }\n            });\n        });\n\n        zipFile.readEntry();\n\n        await new Promise<void>((resolve, reject) => {\n            zipFile.on(\"end\", (err?: Error) => {\n                setImmediate(() => {\n                    // istanbul ignore next\n                    if (doDebug) {\n                        warningLog(\"unzip done\");\n                    }\n                    if (err) {\n                        reject(err);\n                    } else {\n                        resolve();\n                    }\n                });\n            });\n\n            zipFile.on(\"entry\", (entry: yauzl.Entry) => {\n                zipFile.openReadStream(entry, (err?: Error | null, readStream?: Readable) => {\n                    if (err) {\n                        return reject(err);\n                    }\n\n                    const file = path.join(opensslFolder, entry.fileName);\n\n                    // istanbul ignore next\n                    if (doDebug) {\n                        warningLog(\" unzipping :\", file);\n                    }\n\n                    const writeStream = fs.createWriteStream(file, \"binary\");\n                    // ensure parent directory exists\n                    readStream?.pipe(writeStream);\n\n                    writeStream.on(\"close\", () => {\n                        zipFile.readEntry();\n                    });\n                });\n            });\n        });\n    }\n\n    const opensslFolder = get_openssl_folder_win32();\n    const opensslExecPath = get_openssl_exec_path_win32();\n\n    if (!fs.existsSync(opensslFolder)) {\n        // istanbul ignore next\n        if (doDebug) {\n            warningLog(\"creating openssl_folder\", opensslFolder);\n        }\n        fs.mkdirSync(opensslFolder);\n    }\n\n    const { opensslOk, version: _version } = await check_openssl_win32();\n\n    if (!opensslOk) {\n        warningLog(chalk.yellow(\"openssl seems to be missing and need to be installed\"));\n        const { downloadedFile } = await download_openssl();\n\n        // istanbul ignore next\n        if (doDebug) {\n            warningLog(\"deflating \", chalk.yellow(downloadedFile));\n        }\n        await unzip_openssl(downloadedFile);\n\n        const opensslExists = !!fs.existsSync(opensslExecPath);\n\n        // istanbul ignore next\n        if (doDebug) {\n            warningLog(\"verifying \", opensslExists, opensslExists ? chalk.green(\"OK \") : chalk.red(\" Error\"), opensslExecPath);\n        }\n\n        const _opensslExecPath2 = await check_openssl_win32();\n        return opensslExecPath;\n    } else {\n        // istanbul ignore next\n        if (doDebug) {\n            warningLog(chalk.green(\"openssl is already installed and have the expected version.\"));\n        }\n        return opensslExecPath;\n    }\n}\n\n/**\n *\n * return path to the openssl executable\n */\nexport async function install_prerequisite(): Promise<string> {\n    // istanbul ignore else\n    if (process.platform !== \"win32\") {\n        return await check_system_openssl_version();\n    } else {\n        return await install_and_check_win32_openssl_version();\n    }\n}\n\nexport async function get_openssl_exec_path(): Promise<string> {\n    if (process.platform === \"win32\") {\n        const opensslExecPath = await install_prerequisite();\n        if (!fs.existsSync(opensslExecPath)) {\n            throw new Error(`internal error cannot find ${opensslExecPath}`);\n        }\n        return opensslExecPath;\n    } else {\n        return \"openssl\";\n    }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nimport chalk from \"chalk\";\nimport { g_config } from \"./config\";\nimport { warningLog } from \"./debug\";\n\n// istanbul ignore next\nexport function displayChapter(str: string) {\n    const l = \"                                                                                               \";\n    warningLog(`${chalk.bgWhite(l)} `);\n    str = `        ${str}${l}`.substring(0, l.length);\n    warningLog(chalk.bgWhite.cyan(str));\n    warningLog(`${chalk.bgWhite(l)} `);\n}\n\nexport function displayTitle(str: string) {\n    // istanbul ignore next\n    if (!g_config.silent) {\n        warningLog(\"\");\n        warningLog(chalk.yellowBright(str));\n        warningLog(chalk.yellow(new Array(str.length + 1).join(\"=\")), \"\\n\");\n    }\n}\n\nexport function displaySubtitle(str: string) {\n    // istanbul ignore next\n    if (!g_config.silent) {\n        warningLog(\"\");\n        warningLog(`    ${chalk.yellowBright(str)}`);\n        warningLog(`    ${chalk.white(new Array(str.length + 1).join(\"-\"))}`, \"\\n\");\n    }\n}\nexport function display(str: string) {\n    // istanbul ignore next\n    if (!g_config.silent) {\n        warningLog(`       ${str}`);\n    }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nimport exp from \"node:constants\";\n\nconst _exp = exp;\n\nexport * from \"./_env\";\nexport * from \"./create_certificate_signing_request\";\nexport * from \"./execute_openssl\";\nexport * from \"./install_prerequisite\";\nexport * from \"./toolbox\";\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n// tslint:disable:no-console\n// tslint:disable:no-shadowed-variable\n\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { Subject } from \"../../misc/subject\";\nimport { type CreateCertificateSigningRequestWithConfigOptions, quote } from \"../common\";\nimport { makePath } from \"../common2\";\nimport { displaySubtitle } from \"../display\";\nimport { processAltNames } from \"./_env\";\nimport { execute_openssl } from \"./execute_openssl\";\nimport { generateStaticConfig } from \"./toolbox\";\n\nconst q = quote;\nconst n = makePath;\n\n/**\n * create a certificate signing request\n */\nexport async function createCertificateSigningRequestWithOpenSSL(\n    certificateSigningRequestFilename: string,\n    params: CreateCertificateSigningRequestWithConfigOptions\n): Promise<void> {\n    assert(params);\n    assert(params.rootDir);\n    assert(params.configFile);\n    assert(params.privateKey);\n    assert(typeof params.privateKey === \"string\");\n    assert(fs.existsSync(params.configFile), `config file must exist ${params.configFile}`);\n    assert(fs.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);\n    assert(fs.existsSync(params.rootDir), \"RootDir key must exist\");\n    assert(typeof certificateSigningRequestFilename === \"string\");\n\n    // note : this openssl command requires a config file\n    processAltNames(params);\n    const configFile = generateStaticConfig(params.configFile, { cwd: params.rootDir });\n\n    const options = { cwd: params.rootDir, openssl_conf: path.relative(params.rootDir, configFile) };\n\n    const configOption = ` -config ${q(n(configFile))}`;\n\n    const subject = params.subject ? new Subject(params.subject).toString() : undefined;\n    // process.env.OPENSSL_CONF  =\"\";\n    const subjectOptions = subject ? ` -subj \"${subject}\"` : \"\";\n\n    displaySubtitle(\"- Creating a Certificate Signing Request with openssl\");\n    await execute_openssl(\n        \"req -new\" +\n            \"  -sha256 \" +\n            \" -batch \" +\n            \" -text \" +\n            configOption +\n            \" -key \" +\n            q(n(params.privateKey)) +\n            subjectOptions +\n            \" -out \" +\n            q(n(certificateSigningRequestFilename)),\n        options\n    );\n}\n","export type { SubjectOptions } from \"node-opcua-crypto\";\nexport { Subject } from \"node-opcua-crypto\";\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\n// tslint:disable:no-console\n// tslint:disable:no-shadowed-variable\n\nimport assert from \"node:assert\";\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { Filename } from \"../common\";\nimport { quote } from \"../common\";\nimport { makePath } from \"../common2\";\nimport { g_config } from \"../config\";\nimport { getEnv, getEnvironmentVarNames } from \"./_env\";\nimport { type ExecuteOptions, execute_openssl } from \"./execute_openssl\";\n\nfunction openssl_require2DigitYearInDate() {\n    // istanbul ignore next\n    if (!g_config.opensslVersion) {\n        throw new Error(\n            \"openssl_require2DigitYearInDate : openssl version is not known:\" + \"  please call ensure_openssl_installed()\"\n        );\n    }\n    return g_config.opensslVersion.match(/OpenSSL 0\\.9/);\n}\n\ng_config.opensslVersion = \"\";\n\nlet _counter = 0;\n\nexport function generateStaticConfig(configPath: string, options?: ExecuteOptions) {\n    const prePath = options?.cwd || \"\";\n\n    const originalFilename = !path.isAbsolute(configPath) ? path.join(prePath, configPath) : configPath;\n    let staticConfig = fs.readFileSync(originalFilename, { encoding: \"utf8\" });\n    for (const envVar of getEnvironmentVarNames()) {\n        staticConfig = staticConfig.replace(new RegExp(envVar.pattern, \"gi\"), getEnv(envVar.key));\n    }\n    const staticConfigPath = `${configPath}.${process.pid}-${_counter++}.tmp`;\n    const temporaryConfigPath = !path.isAbsolute(configPath) ? path.join(prePath, staticConfigPath) : staticConfigPath;\n    fs.writeFileSync(temporaryConfigPath, staticConfig);\n    if (options?.cwd) {\n        return path.relative(options.cwd, temporaryConfigPath);\n    } else {\n        return temporaryConfigPath;\n    }\n}\n\nconst q = quote;\nconst n = makePath;\n\n/**\n *   calculate the public key from private key\n *   openssl rsa -pubout -in private_key.pem\n *\n * @method getPublicKeyFromPrivateKey\n * @param privateKeyFilename: the existing file with the private key\n * @param publicKeyFilename: the file where to store the public key\n */\nexport async function getPublicKeyFromPrivateKey(privateKeyFilename: string, publicKeyFilename: string): Promise<void> {\n    assert(fs.existsSync(privateKeyFilename));\n    await execute_openssl(`rsa -pubout -in ${q(n(privateKeyFilename))} -out ${q(n(publicKeyFilename))}`, {});\n}\n\n/**\n * extract public key from a certificate\n *   openssl x509 -pubkey -in certificate.pem -nottext\n *\n * @method getPublicKeyFromCertificate\n * @param certificateFilename\n * @param publicKeyFilename\n */\nexport async function getPublicKeyFromCertificate(certificateFilename: string, publicKeyFilename: string) {\n    assert(fs.existsSync(certificateFilename));\n    await execute_openssl(`x509 -pubkey -in ${q(n(certificateFilename))} > ${q(n(publicKeyFilename))}`, {});\n}\nexport function x509Date(date?: Date): string {\n    date = date || new Date();\n    const Y = date.getUTCFullYear();\n    const M = date.getUTCMonth() + 1;\n    const D = date.getUTCDate();\n    const h = date.getUTCHours();\n    const m = date.getUTCMinutes();\n    const s = date.getUTCSeconds();\n\n    function w(s: string | number, l: number): string {\n        return `${s}`.padStart(l, \"0\");\n    }\n\n    if (openssl_require2DigitYearInDate()) {\n        // for example: on MacOS , where openssl 0.98 is installed by default\n        return `${w(Y, 2) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2)}Z`;\n    } else {\n        // for instance when openssl version is greater than 1.0.0\n        return `${w(Y, 4) + w(M, 2) + w(D, 2) + w(h, 2) + w(m, 2) + w(s, 2)}Z`;\n    }\n}\n\n/**\n * @param certificate - the certificate file in PEM format, file must exist\n */\nexport async function dumpCertificate(certificate: Filename): Promise<string> {\n    assert(fs.existsSync(certificate));\n    return await execute_openssl(`x509  -in ${q(n(certificate))} -text  -noout`, {});\n}\n\nexport async function toDer(certificatePem: string): Promise<string> {\n    assert(fs.existsSync(certificatePem));\n    const certificateDer = certificatePem.replace(\".pem\", \".der\");\n    return await execute_openssl(`x509   -outform der  -in ${certificatePem} -out ${certificateDer}`, {});\n}\n\nexport async function fingerprint(certificatePem: string): Promise<string> {\n    // openssl x509 -in my_certificate.pem -hash -dates -noout -fingerprint\n    assert(fs.existsSync(certificatePem));\n    return await execute_openssl(`x509   -fingerprint  -noout  -in ${certificatePem}`, {});\n}\n","const config =\n    \"##################################################################################################\\n\" +\n    \"## SIMPLE OPENSSL CONFIG FILE FOR SELF-SIGNED CERTIFICATE GENERATION\\n\" +\n    \"################################################################################################################\\n\" +\n    \"\\n\" +\n    \"distinguished_name       = req_distinguished_name\\n\" +\n    \"default_md               = sha1\\n\" +\n    \"\\n\" +\n    \"default_md                = sha256                      # The default digest algorithm\\n\" +\n    \"\\n\" +\n    \"[ v3_ca ]\\n\" +\n    \"subjectKeyIdentifier        = hash\\n\" +\n    \"authorityKeyIdentifier      = keyid:always,issuer:always\\n\" +\n    \"\\n\" +\n    \"# authorityKeyIdentifier    = keyid\\n\" +\n    \"basicConstraints            = CA:TRUE\\n\" +\n    \"keyUsage                    = critical, cRLSign, keyCertSign\\n\" +\n    'nsComment                   = \"Self-signed Certificate for CA generated by Node-OPCUA Certificate utility\"\\n' +\n    \"#nsCertType                 = sslCA, emailCA\\n\" +\n    \"#subjectAltName             = email:copy\\n\" +\n    \"#issuerAltName              = issuer:copy\\n\" +\n    \"#obj                        = DER:02:03\\n\" +\n    \"# crlDistributionPoints       = @crl_info\\n\" +\n    \"# [ crl_info ]\\n\" +\n    \"# URI.0                     = http://localhost:8900/crl.pem\\n\" +\n    \"subjectAltName              = $ENV::ALTNAME\\n\" +\n    \"\\n\" +\n    \"[ req ]\\n\" +\n    \"days                      = 390\\n\" +\n    \"req_extensions            = v3_req\\n\" +\n    \"x509_extensions           = v3_ca\\n\" +\n    \"\\n\" +\n    \"[v3_req]\\n\" +\n    \"basicConstraints       = CA:false\\n\" +\n    \"keyUsage               = critical, nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment\\n\" +\n    \"subjectAltName         = $ENV::ALTNAME\\n\" +\n    \"\\n\" +\n    \"[ v3_ca_signed]\\n\" +\n    \"subjectKeyIdentifier      = hash\\n\" +\n    \"authorityKeyIdentifier    = keyid,issuer\\n\" +\n    \"basicConstraints          = critical, CA:FALSE\\n\" +\n    \"keyUsage                  = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment\\n\" +\n    \"extendedKeyUsage          = clientAuth,serverAuth \\n\" +\n    'nsComment                 = \"certificate generated by Node-OPCUA Certificate utility and signed by a CA\"\\n' +\n    \"subjectAltName            = $ENV::ALTNAME\\n\" +\n    \"[ v3_selfsigned]\\n\" +\n    \"subjectKeyIdentifier      = hash\\n\" +\n    \"authorityKeyIdentifier    = keyid,issuer\\n\" +\n    \"basicConstraints          = critical, CA:FALSE\\n\" +\n    \"keyUsage                  = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment\\n\" +\n    \"extendedKeyUsage          = clientAuth,serverAuth \\n\" +\n    'nsComment                 = \"Self-signed certificate generated by Node-OPCUA Certificate utility\"\\n' +\n    \"subjectAltName            = $ENV::ALTNAME\\n\" +\n    \"[ req_distinguished_name ]\\n\" +\n    \"countryName             = Country Name (2 letter code)\\n\" +\n    \"countryName_default     = FR\\n\" +\n    \"countryName_min         = 2\\n\" +\n    \"countryName_max         = 2\\n\" +\n    \"# stateOrProvinceName     = State or Province Name (full name)\\n\" +\n    \"# stateOrProvinceName_default = Ile de France\\n\" +\n    \"# localityName            = Locality Name (city, district)\\n\" +\n    \"# localityName_default    = Paris\\n\" +\n    \"organizationName          = Organization Name (company)\\n\" +\n    \"organizationName_default  = NodeOPCUA\\n\" +\n    \"# organizationalUnitName  = Organizational Unit Name (department, division)\\n\" +\n    \"# organizationalUnitName_default = R&D\\n\" +\n    \"commonName                = Common Name (hostname, FQDN, IP, or your name)\\n\" +\n    \"commonName_max            = 256\\n\" +\n    \"commonName_default        = NodeOPCUA\\n\" +\n    \"# emailAddress            = Email Address\\n\" +\n    \"# emailAddress_max        = 40\\n\" +\n    \"# emailAddress_default    = node-opcua (at) node-opcua (dot) com\\n\" +\n    \"subjectAltName            = $ENV::ALTNAME\";\n\nexport default config;\n","const config =\n    \"#.........DO NOT MODIFY BY HAND .........................\\n\" +\n    \"[ ca ]\\n\" +\n    \"default_ca               = CA_default\\n\" +\n    \"[ CA_default ]\\n\" +\n    \"dir                      = %%ROOT_FOLDER%%            # the main CA folder\\n\" +\n    \"certs                    = $dir/certs                 # where to store certificates\\n\" +\n    \"new_certs_dir            = $dir/certs                 #\\n\" +\n    \"database                 = $dir/index.txt             # the certificate database\\n\" +\n    \"serial                   = $dir/serial                # the serial number counter\\n\" +\n    \"certificate              = $dir/public/cacert.pem     # The root CA certificate\\n\" +\n    \"private_key              = $dir/private/cakey.pem     # the CA private key\\n\" +\n    \"x509_extensions          = usr_cert                   #\\n\" +\n    \"default_days             = 3650                       # default validity : 10 years\\n\" +\n    \"\\n\" +\n    \"# default_md               = sha1\\n\" +\n    \"\\n\" +\n    \"default_md                = sha256                      # The default digest algorithm\\n\" +\n    \"\\n\" +\n    \"preserve                 = no\\n\" +\n    \"policy                   = policy_match\\n\" +\n    \"# randfile                 = $dir/random.rnd\\n\" +\n    \"# default_startdate        = YYMMDDHHMMSSZ\\n\" +\n    \"# default_enddate          = YYMMDDHHMMSSZ\\n\" +\n    \"crl_dir                  = $dir/crl\\n\" +\n    \"crl_extensions           = crl_ext\\n\" +\n    \"crl                      = $dir/revocation_list.crl # the Revocation list\\n\" +\n    \"crlnumber                = $dir/crlnumber           # CRL number file\\n\" +\n    \"default_crl_days         = 30\\n\" +\n    \"default_crl_hours        = 24\\n\" +\n    \"#msie_hack\\n\" +\n    \"\\n\" +\n    \"[ policy_match ]\\n\" +\n    \"countryName              = optional\\n\" +\n    \"stateOrProvinceName      = optional\\n\" +\n    \"localityName             = optional\\n\" +\n    \"organizationName         = optional\\n\" +\n    \"organizationalUnitName   = optional\\n\" +\n    \"commonName               = optional\\n\" +\n    \"emailAddress             = optional\\n\" +\n    \"\\n\" +\n    \"[ req ]\\n\" +\n    \"default_bits             = 4096                     # Size of keys\\n\" +\n    \"default_keyfile          = key.pem                  # name of generated keys\\n\" +\n    \"distinguished_name       = req_distinguished_name\\n\" +\n    \"attributes               = req_attributes\\n\" +\n    \"x509_extensions          = v3_ca\\n\" +\n    \"#input_password\\n\" +\n    \"#output_password\\n\" +\n    \"string_mask              = nombstr                  # permitted characters\\n\" +\n    \"req_extensions           = v3_req\\n\" +\n    \"\\n\" +\n    \"[ req_distinguished_name ]\\n\" +\n    \"\\n\" +\n    \"#0 countryName             = Country Name (2 letter code)\\n\" +\n    \"# countryName_default     = FR\\n\" +\n    \"# countryName_min         = 2\\n\" +\n    \"# countryName_max         = 2\\n\" +\n    \"# stateOrProvinceName     = State or Province Name (full name)\\n\" +\n    \"# stateOrProvinceName_default = Ile de France\\n\" +\n    \"# localityName            = Locality Name (city, district)\\n\" +\n    \"# localityName_default    = Paris\\n\" +\n    \"organizationName          = Organization Name (company)\\n\" +\n    \"organizationName_default  = NodeOPCUA\\n\" +\n    \"# organizationalUnitName  = Organizational Unit Name (department, division)\\n\" +\n    \"# organizationalUnitName_default = R&D\\n\" +\n    \"commonName                = Common Name (hostname, FQDN, IP, or your name)\\n\" +\n    \"commonName_max            = 256\\n\" +\n    \"commonName_default        = NodeOPCUA\\n\" +\n    \"# emailAddress            = Email Address\\n\" +\n    \"# emailAddress_max        = 40\\n\" +\n    \"# emailAddress_default    = node-opcua (at) node-opcua (dot) com\\n\" +\n    \"\\n\" +\n    \"[ req_attributes ]\\n\" +\n    \"#challengePassword        = A challenge password\\n\" +\n    \"#challengePassword_min    = 4\\n\" +\n    \"#challengePassword_max    = 20\\n\" +\n    \"#unstructuredName         = An optional company name\\n\" +\n    \"[ usr_cert ]\\n\" +\n    \"basicConstraints          = critical, CA:FALSE\\n\" +\n    \"subjectKeyIdentifier      = hash\\n\" +\n    \"authorityKeyIdentifier    = keyid,issuer:always\\n\" +\n    \"#authorityKeyIdentifier    = keyid\\n\" +\n    \"subjectAltName            = $ENV::ALTNAME\\n\" +\n    \"# issuerAltName            = issuer:copy\\n\" +\n    \"nsComment                 = ''OpenSSL Generated Certificate''\\n\" +\n    \"#nsCertType               = client, email, objsign for ''everything including object signing''\\n\" +\n    \"#nsCaRevocationUrl        = http://www.domain.dom/ca-crl.pem\\n\" +\n    \"#nsBaseUrl                =\\n\" +\n    \"#nsRenewalUrl             =\\n\" +\n    \"#nsCaPolicyUrl            =\\n\" +\n    \"#nsSslServerName          =\\n\" +\n    \"keyUsage                  = critical, digitalSignature, nonRepudiation,\" +\n    \" keyEncipherment, dataEncipherment, keyAgreement\\n\" +\n    \"extendedKeyUsage          = critical,serverAuth ,clientAuth\\n\" +\n    \"\\n\" +\n    \"[ v3_req ]\\n\" +\n    \"basicConstraints          = critical, CA:FALSE\\n\" +\n    \"keyUsage                  = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement\\n\" +\n    \"extendedKeyUsage          = critical,serverAuth ,clientAuth\\n\" +\n    \"subjectAltName            = $ENV::ALTNAME\\n\" +\n    'nsComment                 = \"CA Generated by Node-OPCUA Certificate utility using openssl\"\\n' +\n    \"[ v3_ca_req ]\\n\" +\n    // -----------------------------------------------------------------------\n    // v3_ca_req is used ONLY during the CSR step (`openssl req -new\n    // -extensions v3_ca_req`).  At that stage no issuer certificate\n    // exists yet (we are bootstrapping the root CA), so we must NOT\n    // include `authorityKeyIdentifier` here.\n    //\n    // OpenSSL 3.5.x (Alpine) rejects *any* authorityKeyIdentifier\n    // value during CSR generation — even plain `keyid` — because\n    // v3_akid.c unconditionally looks for an issuer certificate:\n    //\n    //   v3_akid.c:156: no issuer certificate\n    //\n    // The authorityKeyIdentifier is properly added during the\n    // self-signing step (`openssl x509 -req -extensions v3_ca`)\n    // where the issuer key is available.\n    //\n    // Tested with:\n    //   - OpenSSL 3.0.x  (Ubuntu)   — works\n    //   - OpenSSL 3.4.1  (Windows)  — works\n    //   - OpenSSL 3.5.5  (Alpine)   — works (only with this fix)\n    //\n    // References:\n    //   - https://github.com/openssl/openssl/issues/21519\n    //   - OpenSSL man x509v3_config(5) – authorityKeyIdentifier\n    // -----------------------------------------------------------------------\n    \"subjectKeyIdentifier      = hash\\n\" +\n    \"basicConstraints          = CA:TRUE\\n\" +\n    \"keyUsage                  = critical, cRLSign, keyCertSign\\n\" +\n    'nsComment                 = \"CA CSR generated by Node-OPCUA Certificate utility using openssl\"\\n' +\n    \"[ v3_ca ]\\n\" +\n    \"subjectKeyIdentifier      = hash\\n\" +\n    \"authorityKeyIdentifier    = keyid:always,issuer:always\\n\" +\n    \"basicConstraints          = CA:TRUE\\n\" +\n    \"keyUsage                  = critical, cRLSign, keyCertSign\\n\" +\n    'nsComment                 = \"CA Certificate generated by Node-OPCUA Certificate utility using openssl\"\\n' +\n    \"#nsCertType                 = sslCA, emailCA\\n\" +\n    \"#subjectAltName             = email:copy\\n\" +\n    \"#issuerAltName              = issuer:copy\\n\" +\n    \"#obj                        = DER:02:03\\n\" +\n    \"crlDistributionPoints     = @crl_info\\n\" +\n    \"[ crl_info ]\\n\" +\n    \"URI.0                     = http://localhost:8900/crl.pem\\n\" +\n    \"[ v3_selfsigned]\\n\" +\n    \"basicConstraints          = critical, CA:FALSE\\n\" +\n    \"keyUsage                  = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement\\n\" +\n    \"extendedKeyUsage          = critical,serverAuth ,clientAuth\\n\" +\n    'nsComment                 = \"Self-signed certificate, generated by NodeOPCUA\"\\n' +\n    \"subjectAltName            = $ENV::ALTNAME\\n\" +\n    \"\\n\" +\n    \"[ crl_ext ]\\n\" +\n    \"#issuerAltName            = issuer:copy\\n\" +\n    \"authorityKeyIdentifier    = keyid:always,issuer:always\\n\" +\n    \"#authorityInfoAccess       = @issuer_info\";\n\nexport default config;\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki — CertificateManager\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2014-2026 - Etienne Rossignon - etienne.rossignon (at) gadz.org\n// Copyright (c) 2022-2026 - Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n// This project is licensed under the terms of the MIT license.\n// ---------------------------------------------------------------------------------------------------------------------\n\nimport { EventEmitter } from \"node:events\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { drainPendingLocks, withLock } from \"@ster5/global-mutex\";\nimport chalk from \"chalk\";\nimport chokidar, { type FSWatcher as ChokidarFSWatcher } from \"chokidar\";\nimport {\n    type Certificate,\n    type CertificateInternals,\n    type CertificateRevocationList,\n    type CertificateRevocationListInfo,\n    type DER,\n    exploreCertificate,\n    exploreCertificateInfo,\n    exploreCertificateRevocationList,\n    generatePrivateKeyFile,\n    makeSHA1Thumbprint,\n    readCertificateChain,\n    readCertificateChainAsync,\n    readCertificateRevocationList,\n    split_der,\n    toPem,\n    verifyCertificateSignature\n} from \"node-opcua-crypto\";\n\nimport type { SubjectOptions } from \"../misc/subject\";\nimport type {\n    CertificateStatus,\n    CreateSelfSignCertificateParam,\n    CreateSelfSignCertificateWithConfigParam,\n    Filename,\n    KeySize,\n    Thumbprint\n} from \"../toolbox/common\";\nimport { makePath, mkdirRecursiveSync } from \"../toolbox/common2\";\nimport { debugLog, warningLog } from \"../toolbox/debug\";\nimport { createCertificateSigningRequestAsync, createSelfSignedCertificate } from \"../toolbox/without_openssl\";\n\nimport _simple_config_template from \"./templates/simple_config_template.cnf\";\n\n/**\n *\n * a minimalist config file for openssl that allows\n * self-signed certificate to be generated.\n *\n */\nconst configurationFileSimpleTemplate: string = _simple_config_template;\nconst fsWriteFile = fs.promises.writeFile;\n\ninterface Entry {\n    certificate: Certificate;\n    filename: string;\n    /** Lazily cached result of `exploreCertificate(certificate)`. */\n    info?: CertificateInternals;\n}\n\n/** Return the cached `info` or compute and cache it. */\nfunction getOrComputeInfo(entry: Entry): CertificateInternals {\n    if (!entry.info) {\n        entry.info = exploreCertificate(entry.certificate);\n    }\n    return entry.info;\n}\n\ninterface CRLEntry {\n    crlInfo: CertificateRevocationListInfo;\n    filename: string;\n}\ninterface CRLData {\n    serialNumbers: { [key: string]: Date };\n    crls: CRLEntry[];\n}\ninterface Thumbs {\n    trusted: Map<string, Entry>;\n    rejected: Map<string, Entry>;\n    issuers: {\n        certs: Map<string, Entry>;\n    };\n    /** key = subjectFingerPrint of issuer certificate */\n    crl: Map<string, CRLData>;\n    /** key = subjectFingerPrint of issuer certificate */\n    issuersCrl: Map<string, CRLData>;\n}\n\n/**\n * Identifies which PKI sub-store a certificate event originated from.\n */\nexport type CertificateStore = \"trusted\" | \"rejected\" | \"issuersCerts\";\n\n/**\n * Identifies which PKI sub-store a CRL event originated from.\n */\nexport type CrlStore = \"crl\" | \"issuersCrl\";\n\n/**\n * Events emitted by {@link CertificateManager} when the\n * file-system watchers detect certificate or CRL changes.\n */\nexport interface CertificateManagerEvents {\n    /** A certificate file was added to a store. */\n    certificateAdded: (event: { store: CertificateStore; certificate: Certificate; fingerprint: string; filename: string }) => void;\n    /** A certificate file was removed from a store. */\n    certificateRemoved: (event: { store: CertificateStore; fingerprint: string; filename: string }) => void;\n    /** A certificate file was modified in a store. */\n    certificateChange: (event: {\n        store: CertificateStore;\n        certificate: Certificate;\n        fingerprint: string;\n        filename: string;\n    }) => void;\n    /** A CRL file was added. */\n    crlAdded: (event: { store: CrlStore; filename: string }) => void;\n    /** A CRL file was removed. */\n    crlRemoved: (event: { store: CrlStore; filename: string }) => void;\n}\n\n/**\n * Options controlling certificate validation in\n * {@link CertificateManager.addTrustedCertificateFromChain}.\n *\n * By default all checks are **strict** (secure). Set individual\n * flags to `true` only in test/development environments.\n */\nexport interface AddCertificateValidationOptions {\n    /**\n     * Accept certificates whose validity period has expired\n     * or is not yet active.\n     * @defaultValue false\n     */\n    acceptExpiredCertificate?: boolean;\n\n    /**\n     * Accept certificates that have been revoked by their\n     * issuer's CRL.  When `false` (the default), a revoked\n     * certificate is rejected with `BadCertificateRevoked`.\n     * @defaultValue false\n     */\n    acceptRevokedCertificate?: boolean;\n\n    /**\n     * Do not fail when a CRL is missing for an issuer in the\n     * chain.  When `false` (the default), a missing CRL causes\n     * `BadCertificateRevocationUnknown`.\n     * @defaultValue false\n     */\n    ignoreMissingRevocationList?: boolean;\n\n    /**\n     * Maximum depth of the certificate chain (leaf + issuers).\n     * The leaf certificate counts as depth 1.\n     * @defaultValue 5\n     */\n    maxChainLength?: number;\n}\n\n/**\n * Options for creating a {@link CertificateManager}.\n */\nexport interface CertificateManagerOptions {\n    /**\n     * RSA key size for generated private keys.\n     * @defaultValue 2048\n     */\n    keySize?: KeySize;\n    /** Filesystem path where the PKI directory structure is stored. */\n    location: string;\n\n    /**\n     * Validation options applied by\n     * {@link CertificateManager.addTrustedCertificateFromChain}.\n     *\n     * Defaults are secure — all checks enabled.\n     */\n    addCertificateValidationOptions?: AddCertificateValidationOptions;\n\n    /**\n     * When `true`, the CertificateManager will **not** start\n     * chokidar file-system watchers on the PKI folders.\n     *\n     * The initial file-system scan still runs so the in-memory\n     * indexes are populated, but live change detection is\n     * disabled.  This is useful in test / CI environments where\n     * many CertificateManager instances are created in parallel\n     * and the accumulated `fs.watch` handles exhaust the libuv\n     * thread-pool, causing event-loop starvation.\n     *\n     * @defaultValue false\n     */\n    disableFileWatchers?: boolean;\n}\n\n/**\n * Parameters for {@link createSelfSignedCertificate}.\n * All fields from {@link CreateSelfSignCertificateParam} are required.\n */\nexport interface CreateSelfSignCertificateParam1 extends CreateSelfSignCertificateParam {\n    /**\n     * Output path for the certificate.\n     * @defaultValue `\"own/certs/self_signed_certificate.pem\"`\n     */\n    outputFile?: Filename;\n    /** X.500 subject for the certificate. */\n    subject: SubjectOptions | string;\n    /** OPC UA application URI for the SAN extension. */\n    applicationUri: string;\n    /** DNS host names to include in the SAN extension. */\n    dns: string[];\n    /** Certificate \"Not Before\" date. */\n    startDate: Date;\n    /** Number of days the certificate is valid. */\n    validity: number;\n}\n\n/**\n * Options to fine-tune certificate verification behaviour.\n * Passed to {@link CertificateManager.verifyCertificate}.\n *\n * Without any options, `verifyCertificate` is **strict**: only\n * certificates that are explicitly present in the trusted store\n * will return {@link VerificationStatus.Good}. Unknown or\n * rejected certificates return\n * {@link VerificationStatus.BadCertificateUntrusted} even when\n * their issuer chain is valid.\n *\n * Set {@link acceptCertificateWithValidIssuerChain} to `true`\n * to accept certificates whose issuer chain validates against\n * a trusted CA — even if the leaf certificate itself is not\n * in the trusted store.\n */\nexport interface VerifyCertificateOptions {\n    /** Accept certificates whose \"Not After\" date has passed. */\n    acceptOutdatedCertificate?: boolean;\n    /** Accept issuer certificates whose \"Not After\" date has passed. */\n    acceptOutDatedIssuerCertificate?: boolean;\n    /** Do not fail when a CRL is missing for an issuer. */\n    ignoreMissingRevocationList?: boolean;\n    /** Accept certificates whose \"Not Before\" date is in the future. */\n    acceptPendingCertificate?: boolean;\n    /**\n     * Accept a certificate that is not in the trusted store when\n     * its issuer (CA) certificate is trusted, the signature is\n     * valid, and the certificate does not appear in the CRL.\n     *\n     * When `false` (the default), only certificates explicitly\n     * placed in the trusted store are accepted — this is the\n     * same behaviour as {@link CertificateManager.isCertificateTrusted}.\n     *\n     * @defaultValue false\n     */\n    acceptCertificateWithValidIssuerChain?: boolean;\n}\n\n/**\n * OPC UA certificate verification status codes.\n *\n * These mirror the OPC UA `StatusCode` values for certificate\n * validation results.\n */\nexport enum VerificationStatus {\n    /** The certificate provided as a parameter is not valid. */\n    BadCertificateInvalid = \"BadCertificateInvalid\",\n    /** An error occurred verifying security. */\n    BadSecurityChecksFailed = \"BadSecurityChecksFailed\",\n    /** The certificate does not meet the requirements of the security policy. */\n    BadCertificatePolicyCheckFailed = \"BadCertificatePolicyCheckFailed\",\n    /** The certificate has expired or is not yet valid. */\n    BadCertificateTimeInvalid = \"BadCertificateTimeInvalid\",\n    /** An issuer certificate has expired or is not yet valid. */\n    BadCertificateIssuerTimeInvalid = \"BadCertificateIssuerTimeInvalid\",\n    /** The HostName used to connect to a server does not match a HostName in the certificate. */\n    BadCertificateHostNameInvalid = \"BadCertificateHostNameInvalid\",\n    /** The URI specified in the ApplicationDescription does not match the URI in the certificate. */\n    BadCertificateUriInvalid = \"BadCertificateUriInvalid\",\n    /** The certificate may not be used for the requested operation. */\n    BadCertificateUseNotAllowed = \"BadCertificateUseNotAllowed\",\n    /** The issuer certificate may not be used for the requested operation. */\n    BadCertificateIssuerUseNotAllowed = \"BadCertificateIssuerUseNotAllowed\",\n    /** The certificate is not trusted. */\n    BadCertificateUntrusted = \"BadCertificateUntrusted\",\n    /** It was not possible to determine if the certificate has been revoked. */\n    BadCertificateRevocationUnknown = \"BadCertificateRevocationUnknown\",\n    /** It was not possible to determine if the issuer certificate has been revoked. */\n    BadCertificateIssuerRevocationUnknown = \"BadCertificateIssuerRevocationUnknown\",\n    /** The certificate has been revoked. */\n    BadCertificateRevoked = \"BadCertificateRevoked\",\n    /** The issuer certificate has been revoked. */\n    BadCertificateIssuerRevoked = \"BadCertificateIssuerRevoked\",\n    /** The certificate chain is incomplete. */\n    BadCertificateChainIncomplete = \"BadCertificateChainIncomplete\",\n\n    /** Validation OK. */\n    Good = \"Good\"\n}\n\nexport function coerceCertificateChain(certificate: Certificate | Certificate[]): Certificate[] {\n    if (Array.isArray(certificate)) {\n        if (certificate.length === 0) return [];\n        return certificate.reduce((acc, cert) => {\n            return acc.concat(split_der(cert));\n        }, [] as Certificate[]);\n    }\n    return split_der(certificate);\n}\n\nexport function makeFingerprint(certificate: Certificate | Certificate[] | CertificateRevocationList): string {\n    // When the buffer contains a certificate chain (multiple\n    // concatenated DER structures), the thumbprint must be\n    // computed on the leaf certificate only (first element).\n    const chain = coerceCertificateChain(certificate as Certificate | Certificate[]);\n    return makeSHA1Thumbprint(chain[0]).toString(\"hex\");\n}\nfunction short(stringToShorten: string) {\n    return stringToShorten.substring(0, 10);\n}\n// biome-ignore lint/suspicious/noControlCharactersInRegex: we need to filter control characters\nconst forbiddenChars = /[\\x00-\\x1F<>:\"/\\\\|?*]/g;\n\nfunction buildIdealCertificateName(certificate: Certificate | Certificate[]): string {\n    const chain = coerceCertificateChain(certificate as Certificate | Certificate[]);\n    const fingerprint = makeFingerprint(chain);\n    try {\n        const commonName = exploreCertificate(chain[0]).tbsCertificate.subject.commonName || \"\";\n        // commonName may contain invalid characters for a filename such as / or \\ or :\n        // that we need to replace with a valid character.\n        // replace / or \\ or : with _\n        const sanitizedCommonName = commonName.replace(forbiddenChars, \"_\");\n        return `${sanitizedCommonName}[${fingerprint}]`;\n    } catch (_err) {\n        // make be certificate is incorrect !\n        return `invalid_certificate_[${fingerprint}]`;\n    }\n}\nfunction findMatchingIssuerKey(entries: Entry[], wantedIssuerKey: string): Entry[] {\n    return entries.filter((entry) => {\n        const info = getOrComputeInfo(entry);\n        return info.tbsCertificate.extensions && info.tbsCertificate.extensions.subjectKeyIdentifier === wantedIssuerKey;\n    });\n}\n\nfunction isSelfSigned2(info: CertificateInternals): boolean {\n    return (\n        info.tbsCertificate.extensions?.subjectKeyIdentifier ===\n        info.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier\n    );\n}\n\nfunction isSelfSigned3(certificate: Buffer): boolean {\n    const info = exploreCertificate(certificate);\n    return isSelfSigned2(info);\n}\n\nfunction _isIssuerInfo(info: CertificateInternals): boolean {\n    const basicConstraints = info.tbsCertificate.extensions?.basicConstraints;\n    if (basicConstraints?.cA) {\n        return true;\n    }\n    const keyUsage = info.tbsCertificate.extensions?.keyUsage;\n    if (keyUsage?.keyCertSign) {\n        return true;\n    }\n    return false;\n}\n\n/**\n * Check if the provided certificate acts as an issuer (CA)\n * @param certificate - the DER-encoded certificate\n * @returns true if the certificate has CA basicConstraints or keyCertSign keyUsage\n */\nexport function isIssuer(certificate: Certificate): boolean {\n    try {\n        const info = exploreCertificate(certificate);\n        return _isIssuerInfo(info);\n    } catch (_err) {\n        return false;\n    }\n}\n\n/**\n * Check if the provided certificate acts as an intermediate issuer.\n * An intermediate issuer is a CA certificate that is not a root CA (not self-signed).\n * @param certificate - the DER-encoded certificate\n * @returns true if the certificate is a CA and is not self-signed\n */\nexport function isIntermediateIssuer(certificate: Certificate): boolean {\n    try {\n        const info = exploreCertificate(certificate);\n        if (!_isIssuerInfo(info)) {\n            return false;\n        }\n        // A root CA is self-signed. If it's not self-signed, it's an intermediate CA.\n        return !isSelfSigned2(info);\n    } catch (_err) {\n        return false;\n    }\n}\n\n/**\n * Check if the provided certificate acts as a root issuer.\n * A root issuer is a CA certificate that is self-signed.\n * @param certificate - the DER-encoded certificate\n * @returns true if the certificate is a CA and is self-signed\n */\nexport function isRootIssuer(certificate: Certificate): boolean {\n    try {\n        const info = exploreCertificate(certificate);\n        if (!_isIssuerInfo(info)) {\n            return false;\n        }\n        // A root CA is securely self-signed\n        return isSelfSigned2(info);\n    } catch (_err) {\n        return false;\n    }\n}\n\n/**\n * Find the issuer certificate for a given certificate within\n * a provided certificate chain.\n *\n * @param certificate - the DER-encoded certificate whose issuer to find\n * @param chain - candidate issuer certificates to search\n * @returns the matching issuer certificate, or `null` if not found\n */\nexport function findIssuerCertificateInChain(certificate: Certificate | Certificate[], chain: Certificate[]): Certificate | null {\n    const coercedCertificate = coerceCertificateChain(certificate);\n    const firstCertificate = coercedCertificate[0];\n    if (!firstCertificate) {\n        return null;\n    }\n    const certInfo = exploreCertificate(firstCertificate);\n\n    // istanbul ignore next\n    if (isSelfSigned2(certInfo)) {\n        // the certificate is self signed so is it's own issuer.\n        return firstCertificate;\n    }\n    const wantedIssuerKey = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;\n\n    // istanbul ignore next\n    if (!wantedIssuerKey) {\n        // Certificate has no extension 3 ! the certificate might have been generated by an old system\n        debugLog(\"Certificate has no extension 3\");\n        return null;\n    }\n    const coercedChain = coerceCertificateChain(chain);\n    const potentialIssuers = coercedChain.filter((c) => {\n        const info = exploreCertificate(c);\n        return info.tbsCertificate.extensions && info.tbsCertificate.extensions.subjectKeyIdentifier === wantedIssuerKey;\n    });\n\n    if (potentialIssuers.length === 1) {\n        return potentialIssuers[0];\n    }\n    if (potentialIssuers.length > 1) {\n        debugLog(\"findIssuerCertificateInChain: certificate is not self-signed but has several issuers\");\n        return potentialIssuers[0];\n    }\n    return null;\n}\n\n/**\n * Lifecycle state of a {@link CertificateManager} instance.\n */\nexport enum CertificateManagerState {\n    Uninitialized = 0,\n    Initializing = 1,\n    Initialized = 2,\n    Disposing = 3,\n    Disposed = 4\n}\n/**\n * Manages a GDS-compliant PKI directory structure for an OPC UA\n * application.\n *\n * The PKI store layout follows the OPC UA specification:\n *\n * ```\n * <location>/\n *   ├── own/\n *   │   ├── certs/        Own certificate(s)\n *   │   └── private/      Own private key\n *   ├── trusted/\n *   │   ├── certs/        Trusted peer certificates\n *   │   └── crl/          CRLs for trusted certs\n *   ├── rejected/         Untrusted / rejected certificates\n *   └── issuers/\n *       ├── certs/        CA (issuer) certificates\n *       └── crl/          CRLs for issuer certificates\n * ```\n *\n * File-system watchers keep the in-memory indexes in sync with\n * on-disk changes. Call {@link dispose} when the instance is no\n * longer needed to release watchers and allow the process to\n * exit cleanly.\n *\n * ## Environment Variables\n *\n * - **`OPCUA_PKI_USE_POLLING`** — set to `\"true\"` to use\n *   polling-based file watching instead of native OS events.\n *   Useful for NFS, CIFS, Docker volumes, or other remote /\n *   virtual file systems where native events are unreliable.\n *\n * - **`OPCUA_PKI_POLLING_INTERVAL`** — polling interval in\n *   milliseconds (only effective when polling is enabled).\n *   Clamped to the range [100, 600 000]. Defaults to\n *   {@link folderPollingInterval} (5 000 ms).\n *\n * @example\n * ```ts\n * const cm = new CertificateManager({ location: \"/var/pki\" });\n * await cm.initialize();\n * const status = await cm.verifyCertificate(cert);\n * await cm.dispose();\n * ```\n */\n\n// ── Chain completion result types ─────────────────────────────\n\n/**\n * Status codes returned by {@link CertificateManager.completeCertificateChain}.\n */\nexport enum ChainCompletionStatus {\n    /** The chain already reached a self-signed root — no action was needed. */\n    AlreadyComplete = \"AlreadyComplete\",\n\n    /** One or more issuer certificates were successfully appended. */\n    ChainCompleted = \"ChainCompleted\",\n\n    /** The issuer for the last certificate in the chain could not be found\n     *  in the issuers or trusted stores. The chain is still partial. */\n    IssuerNotFound = \"IssuerNotFound\",\n\n    /** The input chain was empty. */\n    EmptyChain = \"EmptyChain\",\n\n    /** Chain completion was stopped because the maximum depth was reached. */\n    MaxDepthReached = \"MaxDepthReached\"\n}\n\n/**\n * Result of {@link CertificateManager.completeCertificateChain}.\n */\nexport interface ChainCompletionResult {\n    /** The (possibly completed) certificate chain, leaf first. */\n    chain: Certificate[];\n\n    /** Status code indicating whether completion succeeded and why/why not. */\n    status: ChainCompletionStatus;\n\n    /** Human-readable diagnostic message. */\n    message: string;\n}\n\n// ── CertificateManager ───────────────────────────────────────\n\nexport class CertificateManager extends EventEmitter {\n    // ── Global instance registry ─────────────────────────────────\n    // Tracks all initialized CertificateManager instances so their\n    // file watchers can be closed automatically on process exit,\n    // even if the consumer forgets to call dispose().\n    static #activeInstances = new Set<CertificateManager>();\n    static #cleanupInstalled = false;\n\n    static #installProcessCleanup(): void {\n        if (CertificateManager.#cleanupInstalled) return;\n        CertificateManager.#cleanupInstalled = true;\n\n        const closeDanglingWatchers = () => {\n            for (const cm of CertificateManager.#activeInstances) {\n                for (const w of cm.#watchers) {\n                    try {\n                        w.close();\n                    } catch {\n                        /* best-effort */\n                    }\n                }\n                cm.#watchers.splice(0);\n                cm.state = CertificateManagerState.Disposed;\n            }\n            CertificateManager.#activeInstances.clear();\n        };\n\n        // beforeExit fires when the event loop has no more work.\n        // If persistent:false works correctly on watchers, they\n        // won't prevent this event from firing.\n        process.on(\"beforeExit\", closeDanglingWatchers);\n\n        // Also handle external termination signals so watchers\n        // are cleaned up before the process exits.\n        for (const signal of [\"SIGINT\", \"SIGTERM\"] as const) {\n            process.once(signal, () => {\n                closeDanglingWatchers();\n                process.exit();\n            });\n        }\n    }\n\n    /**\n     * Dispose **all** active CertificateManager instances,\n     * closing their file watchers and freeing resources.\n     *\n     * This is mainly useful in test tear-down to ensure the\n     * Node.js process can exit cleanly.\n     */\n    public static async disposeAll(): Promise<void> {\n        const instances = [...CertificateManager.#activeInstances];\n        await Promise.all(instances.map((cm) => CertificateManager.prototype.dispose.call(cm)));\n    }\n\n    /**\n     * Assert that all CertificateManager instances have been\n     * properly disposed. Throws an Error listing the locations\n     * of any leaked instances.\n     *\n     * Intended for use in test `afterAll()` / `afterEach()`\n     * hooks to catch missing `dispose()` calls early.\n     *\n     * @example\n     * ```ts\n     * after(() => {\n     *     CertificateManager.checkAllDisposed();\n     * });\n     * ```\n     */\n    public static checkAllDisposed(): void {\n        if (CertificateManager.#activeInstances.size === 0) return;\n        const locations = [...CertificateManager.#activeInstances].map((cm) => cm.rootDir);\n        throw new Error(\n            `${CertificateManager.#activeInstances.size} CertificateManager instance(s) not disposed:\\n  - ${locations.join(\"\\n  - \")}`\n        );\n    }\n    // ─────────────────────────────────────────────────────────────\n\n    /**\n     * When `true` (the default), any certificate that is not\n     * already in the trusted or rejected store is automatically\n     * written to the rejected folder the first time it is seen.\n     */\n    public untrustUnknownCertificate = true;\n    /** Current lifecycle state of this instance. */\n    public state: CertificateManagerState = CertificateManagerState.Uninitialized;\n    /** @deprecated Use {@link folderPollingInterval} instead (typo fix). */\n    public folderPoolingInterval = 5000;\n\n    /** Interval in milliseconds for file-system polling (when enabled). */\n    public get folderPollingInterval(): number {\n        return this.folderPoolingInterval;\n    }\n    public set folderPollingInterval(value: number) {\n        this.folderPoolingInterval = value;\n    }\n\n    /** RSA key size used when generating the private key. */\n    public readonly keySize: KeySize;\n    readonly #location: string;\n    readonly #watchers: fs.FSWatcher[] = [];\n    readonly #pendingUnrefs: Set<() => void> = new Set();\n    #readCertificatesCalled = false;\n    readonly #filenameToHash = new Map<string, string>();\n    #initializingPromise?: Promise<void>;\n    readonly #addCertValidation: Required<AddCertificateValidationOptions>;\n    readonly #disableFileWatchers: boolean;\n\n    readonly #thumbs: Thumbs = {\n        rejected: new Map(),\n        trusted: new Map(),\n        issuers: {\n            certs: new Map()\n        },\n        crl: new Map(),\n        issuersCrl: new Map()\n    };\n\n    /**\n     * Create a new CertificateManager.\n     *\n     * The constructor creates the root directory if it does not\n     * exist but does **not** initialise the PKI store — call\n     * {@link initialize} before using any other method.\n     *\n     * @param options - configuration options\n     */\n    constructor(options: CertificateManagerOptions) {\n        super();\n        options.keySize = options.keySize || 2048;\n        if (!options.location) {\n            throw new Error(\"CertificateManager: missing 'location' option\");\n        }\n\n        this.#location = makePath(options.location, \"\");\n        this.keySize = options.keySize;\n\n        const v = options.addCertificateValidationOptions ?? {};\n        this.#addCertValidation = {\n            acceptExpiredCertificate: v.acceptExpiredCertificate ?? false,\n            acceptRevokedCertificate: v.acceptRevokedCertificate ?? false,\n            ignoreMissingRevocationList: v.ignoreMissingRevocationList ?? false,\n            maxChainLength: v.maxChainLength ?? 5\n        };\n\n        this.#disableFileWatchers = options.disableFileWatchers ?? process.env.OPCUA_PKI_DISABLE_FILE_WATCHERS === \"true\";\n\n        mkdirRecursiveSync(options.location);\n\n        if (!fs.existsSync(this.#location)) {\n            throw new Error(`CertificateManager cannot access location ${this.#location}`);\n        }\n    }\n\n    /** Path to the OpenSSL configuration file. */\n    get configFile() {\n        return path.join(this.rootDir, \"own/openssl.cnf\");\n    }\n\n    /** Root directory of the PKI store. */\n    get rootDir() {\n        return this.#location;\n    }\n\n    /** Path to the private key file (`own/private/private_key.pem`). */\n    get privateKey() {\n        return path.join(this.rootDir, \"own/private/private_key.pem\");\n    }\n\n    /** Path to the OpenSSL random seed file. */\n    get randomFile() {\n        return path.join(this.rootDir, \"./random.rnd\");\n    }\n\n    /**\n     * Move a certificate to the rejected store.\n     * If the certificate was previously trusted, it will be removed from the trusted folder.\n     * @param certificateOrChain - the DER-encoded certificate or certificate chain\n     */\n    public async rejectCertificate(certificateOrChain: Certificate | Certificate[]): Promise<void> {\n        await this.#moveCertificate(certificateOrChain, \"rejected\");\n    }\n\n    /**\n     * Move a certificate to the trusted store.\n     * If the certificate was previously rejected, it will be removed from the rejected folder.\n     * @param certificateOrChain - the DER-encoded certificate or certificate chain\n     */\n    public async trustCertificate(certificateOrChain: Certificate | Certificate[]): Promise<void> {\n        await this.#moveCertificate(certificateOrChain, \"trusted\");\n    }\n\n    /**\n     * Check whether the trusted certificate store is empty.\n     *\n     * This inspects the in-memory index, which is kept in\n     * sync with the `trusted/certs/` folder by file-system\n     * watchers after {@link initialize} has been called.\n     */\n    public isTrustListEmpty(): boolean {\n        return this.#thumbs.trusted.size === 0;\n    }\n\n    /**\n     * Return the number of certificates currently in the\n     * trusted store.\n     */\n    public getTrustedCertificateCount(): number {\n        return this.#thumbs.trusted.size;\n    }\n\n    /** Path to the rejected certificates folder. */\n    public get rejectedFolder(): string {\n        return path.join(this.rootDir, \"rejected\");\n    }\n    /** Path to the trusted certificates folder. */\n    public get trustedFolder(): string {\n        return path.join(this.rootDir, \"trusted/certs\");\n    }\n    /** Path to the trusted CRL folder. */\n    public get crlFolder(): string {\n        return path.join(this.rootDir, \"trusted/crl\");\n    }\n    /** Path to the issuer (CA) certificates folder. */\n    public get issuersCertFolder(): string {\n        return path.join(this.rootDir, \"issuers/certs\");\n    }\n    /** Path to the issuer CRL folder. */\n    public get issuersCrlFolder(): string {\n        return path.join(this.rootDir, \"issuers/crl\");\n    }\n    /** Path to the own certificate folder. */\n    public get ownCertFolder(): string {\n        return path.join(this.rootDir, \"own/certs\");\n    }\n    public get ownPrivateFolder(): string {\n        return path.join(this.rootDir, \"own/private\");\n    }\n\n    /**\n     * Check if a certificate is in the trusted store.\n     * If the certificate is unknown and `untrustUnknownCertificate` is set,\n     * it will be written to the rejected folder.\n     * @param certificate - the DER-encoded certificate\n     * @returns `\"Good\"` if trusted, `\"BadCertificateUntrusted\"` if rejected/unknown,\n     *   or `\"BadCertificateInvalid\"` if the certificate cannot be parsed.\n     */\n    public async isCertificateTrusted(\n        certificateOrCertificateChain: Certificate | Certificate[]\n    ): Promise<\"Good\" | \"BadCertificateUntrusted\" | \"BadCertificateInvalid\"> {\n        try {\n            const chain = coerceCertificateChain(certificateOrCertificateChain);\n            const leafCertificate = chain[0];\n            if (chain.length < 1) {\n                return \"BadCertificateInvalid\";\n            }\n            let fingerprint: Thumbprint;\n            try {\n                fingerprint = makeFingerprint(chain[0]) as Thumbprint;\n            } catch (_err) {\n                return \"BadCertificateInvalid\";\n            }\n\n            if (this.#thumbs.trusted.has(fingerprint)) {\n                return \"Good\";\n            }\n\n            if (!this.#thumbs.rejected.has(fingerprint)) {\n                if (!this.untrustUnknownCertificate) {\n                    return \"Good\";\n                }\n                // Verify structure before writing — don't persist invalid data\n                try {\n                    exploreCertificateInfo(chain[0]);\n                } catch (_err) {\n                    return \"BadCertificateInvalid\";\n                }\n\n                const filename = path.join(this.rejectedFolder, `${buildIdealCertificateName(leafCertificate)}.pem`);\n                debugLog(\"certificate has never been seen before and is now rejected (untrusted) \", filename);\n\n                await fsWriteFile(filename, toPem(chain, \"CERTIFICATE\"));\n                this.#thumbs.rejected.set(fingerprint, { certificate: leafCertificate, filename });\n            }\n            return \"BadCertificateUntrusted\";\n        } catch (_err) {\n            return \"BadCertificateInvalid\";\n        }\n    }\n    async #innerVerifyCertificateAsync(\n        certificateOrChain: Certificate | Certificate[],\n        _isIssuer: boolean,\n        level: number,\n        options: VerifyCertificateOptions\n    ): Promise<VerificationStatus> {\n        if (level >= 5) {\n            // maximum level of certificate in chain reached !\n            return VerificationStatus.BadSecurityChecksFailed;\n        }\n        const chain = coerceCertificateChain(certificateOrChain);\n        debugLog(\"NB CERTIFICATE IN CHAIN = \", chain.length);\n        const info = exploreCertificate(chain[0]);\n\n        let hasValidIssuer = false;\n        let hasTrustedIssuer = false;\n        // check if certificate is attached to a issuer\n        const hasIssuerKey = info.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;\n        debugLog(\"Certificate as an Issuer Key\", hasIssuerKey);\n\n        if (hasIssuerKey) {\n            const isSelfSigned = isSelfSigned2(info);\n\n            debugLog(\"Is the Certificate self-signed  ?\", isSelfSigned);\n            if (!isSelfSigned) {\n                debugLog(\n                    \"Is issuer found in the list of know issuers ?\",\n                    \"\\n subjectKeyIdentifier   = \",\n                    info.tbsCertificate.extensions?.subjectKeyIdentifier,\n                    \"\\n authorityKeyIdentifier = \",\n                    info.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier\n                );\n                let issuerCertificate = await this.findIssuerCertificate(chain[0]);\n                if (!issuerCertificate) {\n                    // the issuer has not been found in the list of trusted certificate\n                    // may be the issuer certificate is in the chain itself ?\n                    issuerCertificate = findIssuerCertificateInChain(chain[0], chain);\n                    if (!issuerCertificate) {\n                        debugLog(\n                            \" the issuer has not been found in the chain itself nor in the issuer.cert list => the chain is incomplete!\"\n                        );\n                        return VerificationStatus.BadCertificateChainIncomplete;\n                    }\n                    debugLog(\" the issuer certificate has been found in the chain itself ! the chain is complete !\");\n                } else {\n                    debugLog(\" the issuer certificate has been found in the issuer.cert folder !\");\n                }\n                const issuerStatus = await this.#innerVerifyCertificateAsync(issuerCertificate, true, level + 1, options);\n                if (issuerStatus === VerificationStatus.BadCertificateRevocationUnknown) {\n                    // the issuer must have a CRL available .... !\n                    return VerificationStatus.BadCertificateIssuerRevocationUnknown;\n                }\n                if (issuerStatus === VerificationStatus.BadCertificateIssuerRevocationUnknown) {\n                    // the issuer must have a CRL available .... !\n                    return VerificationStatus.BadCertificateIssuerRevocationUnknown;\n                }\n                if (issuerStatus === VerificationStatus.BadCertificateTimeInvalid) {\n                    if (!options?.acceptOutDatedIssuerCertificate) {\n                        // the issuer must have valid dates ....\n                        return VerificationStatus.BadCertificateIssuerTimeInvalid;\n                    }\n                }\n                if (issuerStatus === VerificationStatus.BadCertificateUntrusted) {\n                    debugLog(\"warning issuerStatus = \", issuerStatus.toString(), \"the issuer certificate is not trusted\");\n                    // return VerificationStatus.BadSecurityChecksFailed;\n                }\n\n                if (issuerStatus !== VerificationStatus.Good && issuerStatus !== VerificationStatus.BadCertificateUntrusted) {\n                    // if the issuer has other issue => let's drop!\n                    return VerificationStatus.BadSecurityChecksFailed;\n                }\n                // verify that certificate was signed by issuer\n                const isCertificateSignatureOK = verifyCertificateSignature(chain[0], issuerCertificate);\n                if (!isCertificateSignatureOK) {\n                    debugLog(\" the certificate was not signed by the issuer as it claim to be ! Danger\");\n                    return VerificationStatus.BadSecurityChecksFailed;\n                }\n                hasValidIssuer = true;\n\n                // let detected if our certificate is in the revocation list\n                let revokedStatus = await this.isCertificateRevoked(chain, issuerCertificate);\n                if (revokedStatus === VerificationStatus.BadCertificateRevocationUnknown) {\n                    if (options?.ignoreMissingRevocationList) {\n                        // continue as if the certificate was not revoked\n                        revokedStatus = VerificationStatus.Good;\n                    }\n                }\n                if (revokedStatus !== VerificationStatus.Good) {\n                    // certificate is revoked !!!\n                    debugLog(\"revokedStatus\", revokedStatus);\n                    return revokedStatus;\n                }\n\n                // let check if the issuer is explicitly trusted\n                const issuerTrustedStatus = await this.#checkRejectedOrTrusted(issuerCertificate);\n                debugLog(\"issuerTrustedStatus\", issuerTrustedStatus);\n\n                if (issuerTrustedStatus === \"unknown\") {\n                    hasTrustedIssuer = false;\n                } else if (issuerTrustedStatus === \"trusted\") {\n                    hasTrustedIssuer = true;\n                } else if (issuerTrustedStatus === \"rejected\") {\n                    // we should never get there: this should have been detected before !!!\n                    return VerificationStatus.BadSecurityChecksFailed;\n                }\n            } else {\n                // verify that certificate was signed by issuer (self in this case)\n                const isCertificateSignatureOK = verifyCertificateSignature(chain[0], chain[0]);\n                if (!isCertificateSignatureOK) {\n                    debugLog(\"Self-signed Certificate signature is not valid\");\n                    return VerificationStatus.BadSecurityChecksFailed;\n                }\n                const revokedStatus = await this.isCertificateRevoked(chain);\n                debugLog(\"revokedStatus of self signed certificate:\", revokedStatus);\n            }\n        }\n\n        const status = await this.#checkRejectedOrTrusted(chain[0]);\n        if (status === \"rejected\") {\n            if (!(options.acceptCertificateWithValidIssuerChain && hasValidIssuer && hasTrustedIssuer)) {\n                return VerificationStatus.BadCertificateUntrusted;\n            }\n        }\n        const _c2 = chain[1] ? exploreCertificateInfo(chain[1]) : \"non\";\n        debugLog(\"chain[1] info=\", _c2);\n\n        // Has SoftwareCertificate passed its issue date and has it not expired ?\n        // check dates\n        const certificateInfo = exploreCertificateInfo(chain[0]);\n        const now = new Date();\n\n        let isTimeInvalid = false;\n        // check that certificate is active\n        if (certificateInfo.notBefore.getTime() > now.getTime()) {\n            // certificate is not active yet\n            debugLog(\n                `${chalk.red(\"certificate is invalid : certificate is not active yet !\")} not before date =${certificateInfo.notBefore}`\n            );\n            if (!options.acceptPendingCertificate) {\n                isTimeInvalid = true;\n            }\n        }\n\n        //  check that certificate has not expired\n        if (certificateInfo.notAfter.getTime() <= now.getTime()) {\n            // certificate is obsolete\n            debugLog(\n                `${chalk.red(\"certificate is invalid : certificate has expired !\")} not after date =${certificateInfo.notAfter}`\n            );\n            if (!options.acceptOutdatedCertificate) {\n                isTimeInvalid = true;\n            }\n        }\n        if (status === \"trusted\") {\n            return isTimeInvalid ? VerificationStatus.BadCertificateTimeInvalid : VerificationStatus.Good;\n        }\n        // status should be \"unknown\" or \"rejected\" (bypassed) at this point\n        if (hasIssuerKey) {\n            if (!hasTrustedIssuer) {\n                return VerificationStatus.BadCertificateUntrusted;\n            }\n            if (!hasValidIssuer) {\n                return VerificationStatus.BadCertificateUntrusted;\n            }\n            if (!options.acceptCertificateWithValidIssuerChain) {\n                // strict mode: the leaf cert is not in the trusted store\n                return VerificationStatus.BadCertificateUntrusted;\n            }\n            return isTimeInvalid ? VerificationStatus.BadCertificateTimeInvalid : VerificationStatus.Good;\n        } else {\n            return VerificationStatus.BadCertificateUntrusted;\n        }\n    }\n\n    /**\n     * Internal verification hook called by {@link verifyCertificate}.\n     *\n     * Subclasses can override this to inject additional validation\n     * logic (e.g. application-level policy checks) while still\n     * delegating to the default chain/CRL/trust verification.\n     *\n     * @param certificate - the DER-encoded certificate to verify\n     * @param options - verification options forwarded from the\n     *   public API\n     * @returns the verification status code\n     */\n    protected async verifyCertificateAsync(\n        certificate: Certificate | Certificate[],\n        options: VerifyCertificateOptions\n    ): Promise<VerificationStatus> {\n        const chain = coerceCertificateChain(certificate);\n        for (const element of chain) {\n            try {\n                // exploreCertificateInfo will throw if the DER\n                // element is not a valid X.509 certificate\n                // (e.g. it is a CRL or other ASN.1 structure).\n                exploreCertificateInfo(element);\n            } catch (_err) {\n                return VerificationStatus.BadCertificateInvalid;\n            }\n        }\n        const status1 = await this.#innerVerifyCertificateAsync(chain, false, 0, options);\n        return status1;\n    }\n\n    /**\n     * Verify a certificate against the PKI trust store.\n     *\n     * This performs a full validation including trust status,\n     * issuer chain, CRL revocation checks, and time validity.\n     *\n     * @param certificate - the DER-encoded certificate to verify\n     * @param options - optional flags to relax validation rules\n     * @returns the verification status code\n     */\n    public async verifyCertificate(\n        certificate: Certificate | Certificate[],\n        options?: VerifyCertificateOptions\n    ): Promise<VerificationStatus> {\n        // Is the  signature on the SoftwareCertificate valid .?\n        if (!certificate) {\n            // missing certificate\n            return VerificationStatus.BadSecurityChecksFailed;\n        }\n        try {\n            const status = await this.verifyCertificateAsync(certificate, options || {});\n            return status;\n        } catch (error) {\n            warningLog(`verifyCertificate error: ${(error as Error).message}`);\n            return VerificationStatus.BadCertificateInvalid;\n        }\n    }\n\n    /**\n     * Initialize the PKI directory structure, generate the\n     * private key (if missing), and start file-system watchers.\n     *\n     * This method is idempotent — subsequent calls are no-ops.\n     * It must be called before any certificate operations.\n     */\n    public async initialize(): Promise<void> {\n        if (this.state !== CertificateManagerState.Uninitialized) {\n            return;\n        }\n        this.state = CertificateManagerState.Initializing;\n        this.#initializingPromise = this.#initialize();\n        await this.#initializingPromise;\n        this.#initializingPromise = undefined;\n        this.state = CertificateManagerState.Initialized;\n\n        // Register for automatic cleanup on process exit\n        CertificateManager.#activeInstances.add(this);\n        CertificateManager.#installProcessCleanup();\n    }\n\n    async #initialize(): Promise<void> {\n        this.state = CertificateManagerState.Initializing;\n        const pkiDir = this.#location;\n        mkdirRecursiveSync(pkiDir);\n        mkdirRecursiveSync(path.join(pkiDir, \"own\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"own/certs\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"own/private\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"rejected\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"trusted\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"trusted/certs\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"trusted/crl\"));\n\n        mkdirRecursiveSync(path.join(pkiDir, \"issuers\"));\n        mkdirRecursiveSync(path.join(pkiDir, \"issuers/certs\")); // contains Trusted CA certificates\n        mkdirRecursiveSync(path.join(pkiDir, \"issuers/crl\")); // contains CRL of revoked CA certificates\n\n        if (!fs.existsSync(this.configFile) || !fs.existsSync(this.privateKey)) {\n            return await this.withLock2(async () => {\n                if (this.state === CertificateManagerState.Disposing || this.state === CertificateManagerState.Disposed) {\n                    return;\n                }\n\n                if (!fs.existsSync(this.configFile)) {\n                    fs.writeFileSync(this.configFile, configurationFileSimpleTemplate);\n                }\n                // note : openssl 1.1.1 has a bug that causes a failure if\n                // random file cannot be found. (should be fixed in 1.1.1.a)\n                // if this issue become important we may have to consider checking that rndFile exists and recreate\n                // it if not . this could be achieved with the command :\n                //      \"openssl rand -writerand ${this.randomFile}\"\n                //\n                // cf: https://github.com/node-opcua/node-opcua/issues/554\n\n                if (!fs.existsSync(this.privateKey)) {\n                    debugLog(\"generating private key ...\");\n                    //   setEnv(\"RANDFILE\", this.randomFile);\n                    await generatePrivateKeyFile(this.privateKey, this.keySize);\n                    await this.#readCertificates();\n                } else {\n                    // debugLog(\"   initialize :  private key already exists ... skipping\");\n                    await this.#readCertificates();\n                }\n            });\n        } else {\n            await this.#readCertificates();\n        }\n    }\n\n    /**\n     * Dispose of the CertificateManager, releasing file watchers\n     * and other resources. The instance should not be used after\n     * calling this method.\n     */\n    public async dispose(): Promise<void> {\n        if (this.state === CertificateManagerState.Disposing) {\n            throw new Error(\"Already disposing\");\n        }\n\n        if (this.state === CertificateManagerState.Uninitialized) {\n            this.state = CertificateManagerState.Disposed;\n            return;\n        }\n\n        // Wait for initialization to complete before disposing\n        if (this.state === CertificateManagerState.Initializing) {\n            if (this.#initializingPromise) {\n                await this.#initializingPromise;\n            }\n        }\n\n        try {\n            this.state = CertificateManagerState.Disposing;\n            // Wait for any in-flight withLock operations (e.g.\n            // fire-and-forget trustCertificate calls) to complete\n            // so their setInterval timers are properly cleared.\n            await drainPendingLocks();\n            // Ensure all fs.watch handles are unref'd even if\n            // chokidar hasn't reached \"ready\" yet.\n            for (const unreff of this.#pendingUnrefs) {\n                unreff();\n            }\n            this.#pendingUnrefs.clear();\n            await Promise.all(this.#watchers.map((w) => w.close()));\n            this.#watchers.forEach((w) => {\n                w.removeAllListeners();\n            });\n            this.#watchers.splice(0);\n        } finally {\n            this.state = CertificateManagerState.Disposed;\n            CertificateManager.#activeInstances.delete(this);\n        }\n    }\n\n    /**\n     * Force a full re-scan of all PKI folders, rebuilding\n     * the in-memory `_thumbs` index from scratch.\n     *\n     * Call this after external processes have modified the\n     * PKI folders (e.g. via `writeTrustList` or CLI tools)\n     * to ensure the CertificateManager sees the latest\n     * state without waiting for file-system events.\n     */\n    public async reloadCertificates(): Promise<void> {\n        // Close existing watchers\n        await Promise.all(this.#watchers.map((w) => w.close()));\n        for (const w of this.#watchers) {\n            w.removeAllListeners();\n        }\n        this.#watchers.splice(0);\n\n        // Clear in-memory indexes\n        this.#thumbs.rejected.clear();\n        this.#thumbs.trusted.clear();\n        this.#thumbs.issuers.certs.clear();\n        this.#thumbs.crl.clear();\n        this.#thumbs.issuersCrl.clear();\n        this.#filenameToHash.clear();\n\n        // Re-scan all folders\n        this.#readCertificatesCalled = false;\n        await this.#readCertificates();\n    }\n\n    protected async withLock2<T>(action: () => Promise<T>): Promise<T> {\n        const lockFileName = path.join(this.rootDir, \"mutex\");\n        return withLock<T>({ fileToLock: lockFileName }, async () => {\n            return await action();\n        });\n    }\n    /**\n     * Create a self-signed certificate for this PKI's private key.\n     *\n     * The certificate is written to `params.outputFile` or\n     * `own/certs/self_signed_certificate.pem` by default.\n     *\n     * @param params - certificate parameters (subject, SANs,\n     *   validity, etc.)\n     */\n    public async createSelfSignedCertificate(params: CreateSelfSignCertificateParam1): Promise<void> {\n        if (typeof params.applicationUri !== \"string\") {\n            throw new Error(\"createSelfSignedCertificate: expecting applicationUri to be a string\");\n        }\n        if (!fs.existsSync(this.privateKey)) {\n            throw new Error(`Cannot find private key ${this.privateKey}`);\n        }\n        let certificateFilename = path.join(this.rootDir, \"own/certs/self_signed_certificate.pem\");\n        certificateFilename = params.outputFile || certificateFilename;\n\n        const _params = params as unknown as CreateSelfSignCertificateWithConfigParam;\n        _params.rootDir = this.rootDir;\n        _params.configFile = this.configFile;\n        _params.privateKey = this.privateKey;\n\n        _params.subject = params.subject || \"CN=FIXME\";\n        await this.withLock2(async () => {\n            await createSelfSignedCertificate(certificateFilename, _params);\n        });\n    }\n\n    /**\n     * Create a Certificate Signing Request (CSR) using this\n     * PKI's private key and configuration.\n     *\n     * The CSR file is written to `own/certs/` with a timestamped\n     * filename.\n     *\n     * @param params - CSR parameters (subject, SANs)\n     * @returns the filesystem path to the generated CSR file\n     */\n    public async createCertificateRequest(params: CreateSelfSignCertificateParam): Promise<Filename> {\n        if (!params) {\n            throw new Error(\"params is required\");\n        }\n        const _params = params as CreateSelfSignCertificateWithConfigParam;\n        if (Object.prototype.hasOwnProperty.call(_params, \"rootDir\")) {\n            throw new Error(\"rootDir should not be specified \");\n        }\n        _params.rootDir = path.resolve(this.rootDir);\n        _params.configFile = path.resolve(this.configFile);\n        _params.privateKey = path.resolve(this.privateKey);\n\n        return await this.withLock2<string>(async () => {\n            // compose a file name for the request\n            const now = new Date();\n            const today = `${now.toISOString().slice(0, 10)}_${now.getTime()}`;\n            const certificateSigningRequestFilename = path.join(this.rootDir, \"own/certs\", `certificate_${today}.csr`);\n            await createCertificateSigningRequestAsync(certificateSigningRequestFilename, _params);\n            return certificateSigningRequestFilename;\n        });\n    }\n\n    /**\n     * Add a CA (issuer) certificate to the issuers store.\n     * If the certificate is already present, this is a no-op.\n     * @param certificate - the DER-encoded CA certificate\n     * @param validate - if `true`, verify the certificate before adding\n     * @param addInTrustList - if `true`, also add to the trusted store\n     * @returns `VerificationStatus.Good` on success\n     */\n    public async addIssuer(certificate: DER, validate = false, addInTrustList = false): Promise<VerificationStatus> {\n        if (validate) {\n            const status = await this.verifyCertificate(certificate);\n            if (status !== VerificationStatus.Good && status !== VerificationStatus.BadCertificateUntrusted) {\n                return status;\n            }\n        }\n        const pemCertificate = toPem(certificate, \"CERTIFICATE\");\n        const fingerprint = makeFingerprint(certificate);\n        if (this.#thumbs.issuers.certs.has(fingerprint)) {\n            // already in .. simply ignore\n            return VerificationStatus.Good;\n        }\n        // write certificate\n        const filename = path.join(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);\n        await fs.promises.writeFile(filename, pemCertificate, \"ascii\");\n\n        // first time seen, let's save it.\n        this.#thumbs.issuers.certs.set(fingerprint, { certificate, filename });\n\n        if (addInTrustList) {\n            // add certificate in the trust list as well\n            await this.trustCertificate(certificate);\n        }\n\n        return VerificationStatus.Good;\n    }\n\n    /**\n     * Add multiple CA (issuer) certificates to the issuers store.\n     * @param certificates - the DER-encoded CA certificates\n     * @param validate - if `true`, verify each certificate before adding\n     * @param addInTrustList - if `true`, also add each certificate to the trusted store\n     * @returns `VerificationStatus.Good` on success\n     */\n    public async addIssuers(certificates: Certificate[], validate = false, addInTrustList = false): Promise<VerificationStatus> {\n        for (const certificate of certificates) {\n            // check that certificate is a issuer certificate\n            if (!isIssuer(certificate)) {\n                warningLog(`Certificate ${makeFingerprint(certificate)} is not a issuer certificate`);\n                continue;\n            }\n            await this.addIssuer(certificate, validate, addInTrustList);\n        }\n        return VerificationStatus.Good;\n    }\n\n    /**\n     * Add a CRL to the certificate manager.\n     * @param crl - the CRL to add\n     * @param target - \"issuers\" (default) writes to issuers/crl, \"trusted\" writes to trusted/crl\n     */\n    public async addRevocationList(\n        crl: CertificateRevocationList,\n        target: \"issuers\" | \"trusted\" = \"issuers\"\n    ): Promise<VerificationStatus> {\n        return await this.withLock2<VerificationStatus>(async () => {\n            try {\n                const index = target === \"trusted\" ? this.#thumbs.crl : this.#thumbs.issuersCrl;\n                const folder = target === \"trusted\" ? this.crlFolder : this.issuersCrlFolder;\n\n                const crlInfo = exploreCertificateRevocationList(crl);\n                const key = crlInfo.tbsCertList.issuerFingerprint;\n                if (!index.has(key)) {\n                    index.set(key, { crls: [], serialNumbers: {} });\n                }\n                const pemCertificate = toPem(crl, \"X509 CRL\");\n                // Use the issuer fingerprint for the filename — NOT buildIdealCertificateName()\n                // which expects a certificate, not a CRL. Passing a CRL causes\n                // exploreCertificate() to throw, producing \"invalid_certificate_\" names.\n                const sanitizedKey = key.replace(/:/g, \"\");\n                const filename = path.join(folder, `crl_[${sanitizedKey}].pem`);\n                await fs.promises.writeFile(filename, pemCertificate, \"ascii\");\n\n                await this.#onCrlFileAdded(index, filename);\n\n                await this.#waitAndCheckCRLProcessingStatus();\n\n                return VerificationStatus.Good;\n            } catch (err) {\n                debugLog(err);\n                return VerificationStatus.BadSecurityChecksFailed;\n            }\n        });\n    }\n\n    /**\n     * Remove all CRL files from the specified folder(s) and clear the\n     * corresponding in-memory index.\n     * @param target - \"issuers\" clears issuers/crl, \"trusted\" clears\n     *   trusted/crl, \"all\" clears both.\n     */\n    public async clearRevocationLists(target: \"issuers\" | \"trusted\" | \"all\"): Promise<void> {\n        const clearFolder = async (folder: string, index: Map<string, CRLData>) => {\n            try {\n                const files = await fs.promises.readdir(folder);\n                for (const file of files) {\n                    const ext = path.extname(file).toLowerCase();\n                    if (ext === \".crl\" || ext === \".pem\" || ext === \".der\") {\n                        await fs.promises.unlink(path.join(folder, file));\n                    }\n                }\n            } catch (err: unknown) {\n                if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n                    throw err;\n                }\n            }\n            index.clear();\n        };\n\n        if (target === \"issuers\" || target === \"all\") {\n            await clearFolder(this.issuersCrlFolder, this.#thumbs.issuersCrl);\n        }\n        if (target === \"trusted\" || target === \"all\") {\n            await clearFolder(this.crlFolder, this.#thumbs.crl);\n        }\n    }\n\n    /**\n     * Check whether an issuer certificate with the given thumbprint\n     * is already registered.\n     * @param thumbprint - hex-encoded SHA-1 thumbprint (lowercase)\n     */\n    public async hasIssuer(thumbprint: string): Promise<boolean> {\n        await this.#readCertificates();\n        const normalized = thumbprint.toLowerCase();\n        return this.#thumbs.issuers.certs.has(normalized);\n    }\n\n    /**\n     * Remove a trusted certificate identified by its SHA-1 thumbprint.\n     * Deletes the file on disk and removes the entry from the\n     * in-memory index.\n     * @param thumbprint - hex-encoded SHA-1 thumbprint (lowercase)\n     * @returns the removed certificate buffer, or `null` if not found\n     */\n    public async removeTrustedCertificate(thumbprint: string): Promise<Certificate | null> {\n        await this.#readCertificates();\n        const normalized = thumbprint.toLowerCase();\n        const entry = this.#thumbs.trusted.get(normalized);\n        if (!entry) {\n            return null;\n        }\n        try {\n            await fs.promises.unlink(entry.filename);\n        } catch (err: unknown) {\n            if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n                throw err;\n            }\n        }\n        this.#thumbs.trusted.delete(normalized);\n        return entry.certificate;\n    }\n\n    /**\n     * Remove an issuer certificate identified by its SHA-1 thumbprint.\n     * Deletes the file on disk and removes the entry from the\n     * in-memory index.\n     * @param thumbprint - hex-encoded SHA-1 thumbprint (lowercase)\n     * @returns the removed certificate buffer, or `null` if not found\n     */\n    public async removeIssuer(thumbprint: string): Promise<Certificate | null> {\n        await this.#readCertificates();\n        const normalized = thumbprint.toLowerCase();\n        const entry = this.#thumbs.issuers.certs.get(normalized);\n        if (!entry) {\n            return null;\n        }\n        try {\n            await fs.promises.unlink(entry.filename);\n        } catch (err: unknown) {\n            if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n                throw err;\n            }\n        }\n        this.#thumbs.issuers.certs.delete(normalized);\n        return entry.certificate;\n    }\n\n    /**\n     * Remove all CRL files that were issued by the given CA certificate\n     * from the specified folder (or both).\n     * @param issuerCertificate - the CA certificate whose CRLs to remove\n     * @param target - \"issuers\", \"trusted\", or \"all\" (default \"all\")\n     */\n    public async removeRevocationListsForIssuer(\n        issuerCertificate: Certificate,\n        target: \"issuers\" | \"trusted\" | \"all\" = \"all\"\n    ): Promise<void> {\n        const issuerInfo = exploreCertificate(issuerCertificate);\n        const issuerFingerprint = issuerInfo.tbsCertificate.subjectFingerPrint;\n\n        const processIndex = async (index: Map<string, CRLData>) => {\n            const crlData = index.get(issuerFingerprint);\n            if (!crlData) return;\n            for (const crlEntry of crlData.crls) {\n                try {\n                    await fs.promises.unlink(crlEntry.filename);\n                } catch (err: unknown) {\n                    if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n                        throw err;\n                    }\n                }\n            }\n            index.delete(issuerFingerprint);\n        };\n\n        if (target === \"issuers\" || target === \"all\") {\n            await processIndex(this.#thumbs.issuersCrl);\n        }\n        if (target === \"trusted\" || target === \"all\") {\n            await processIndex(this.#thumbs.crl);\n        }\n    }\n\n    /**\n     * Validate a certificate (optionally with its chain) and add\n     * the leaf certificate to the trusted store.\n     *\n     * Performs OPC UA Part 4, Table 100 validation:\n     *\n     * 1. **Certificate Structure** — parse the DER encoding.\n     * 2. **Build Certificate Chain** — walk from the leaf to a\n     *    self-signed root CA, using the provided chain and the\n     *    issuers store.\n     * 3. **Signature** — verify each certificate's signature\n     *    against its issuer.\n     * 4. **Issuer Presence** — every issuer in the chain must\n     *    already be registered in the issuers store (per GDS\n     *    7.8.2.6).\n     * 5. **Validity Period** — each certificate must be within\n     *    its validity window (overridable via\n     *    {@link AddCertificateValidationOptions.acceptExpiredCertificate}).\n     * 6. **Revocation Check** — each certificate is checked\n     *    against its issuer's CRL (overridable via\n     *    {@link AddCertificateValidationOptions.acceptRevokedCertificate}\n     *    and {@link AddCertificateValidationOptions.ignoreMissingRevocationList}).\n     *\n     * Only the leaf certificate is added to the trusted store.\n     *\n     * @param certificateChain - DER-encoded certificate or chain\n     * @returns `VerificationStatus.Good` on success, or an error\n     *  status indicating why the certificate was rejected.\n     */\n    public async addTrustedCertificateFromChain(certificateChain: Certificate | Certificate[]): Promise<VerificationStatus> {\n        // Top-level guard: never let an unexpected error escape.\n        // Every code path returns a VerificationStatus; unexpected\n        // throws (corrupt buffers, crypto failures, etc.) are\n        // caught here and mapped to BadCertificateInvalid.\n        try {\n            return await this.#addTrustedCertificateFromChainImpl(certificateChain);\n        } catch (_err) {\n            warningLog(\"addTrustedCertificateFromChain: unexpected error\", _err);\n            return VerificationStatus.BadCertificateInvalid;\n        }\n    }\n\n    async #addTrustedCertificateFromChainImpl(certificateChain: Certificate | Certificate[]): Promise<VerificationStatus> {\n        let certificates: Certificate[];\n        try {\n            certificates = coerceCertificateChain(certificateChain);\n        } catch (_err) {\n            return VerificationStatus.BadCertificateInvalid;\n        }\n        if (certificates.length === 0) {\n            return VerificationStatus.BadCertificateInvalid;\n        }\n        const leafCertificate = certificates[0];\n        const opts = this.#addCertValidation;\n\n        // ── Step 1: Certificate Structure ────────────────────────\n        let leafInfo: CertificateInternals;\n        try {\n            leafInfo = exploreCertificate(leafCertificate);\n        } catch (_err) {\n            return VerificationStatus.BadCertificateInvalid;\n        }\n\n        // Re-scan the issuers folder to pick up certificates\n        // added directly to disk (e.g. by GDS push or external\n        // tooling) that the file-system watcher may not have\n        // delivered yet.\n        await this.#scanCertFolder(this.issuersCertFolder, this.#thumbs.issuers.certs);\n\n        // ── Step 2–6: Walk the chain from leaf to root ───────────\n        // depth counts the number of certificates validated in the\n        // chain.  maxChainLength=1 → only self-signed certs;\n        // maxChainLength=2 → leaf + root CA; etc.\n        let currentCert = leafCertificate;\n        let currentInfo = leafInfo;\n        let depth = 0;\n\n        while (true) {\n            depth++;\n            if (depth > opts.maxChainLength) {\n                // Chain depth exceeded before reaching root\n                return VerificationStatus.BadSecurityChecksFailed;\n            }\n\n            // ── Step 5: Validity Period ──────────────────────────\n            if (!opts.acceptExpiredCertificate) {\n                let certDetails: ReturnType<typeof exploreCertificateInfo>;\n                try {\n                    certDetails = exploreCertificateInfo(currentCert);\n                } catch (_err) {\n                    return VerificationStatus.BadCertificateInvalid;\n                }\n                const now = new Date();\n                if (certDetails.notBefore.getTime() > now.getTime()) {\n                    return VerificationStatus.BadCertificateTimeInvalid;\n                }\n                if (certDetails.notAfter.getTime() <= now.getTime()) {\n                    return depth === 1\n                        ? VerificationStatus.BadCertificateTimeInvalid\n                        : VerificationStatus.BadCertificateIssuerTimeInvalid;\n                }\n            }\n\n            // ── Self-signed certificate ──────────────────────────\n            if (isSelfSigned2(currentInfo)) {\n                // Step 3: Verify self-signature\n                try {\n                    if (!verifyCertificateSignature(currentCert, currentCert)) {\n                        return VerificationStatus.BadCertificateInvalid;\n                    }\n                } catch (_err) {\n                    return VerificationStatus.BadCertificateInvalid;\n                }\n                // Self-signed certificates don't need revocation\n                // or issuer checks — we're at the root.\n                break;\n            }\n\n            // ── Step 2: Find issuer ──────────────────────────────\n            // First try findIssuerCertificate (checks issuers store\n            // and trusted store), then fall back to the chain.\n            let issuerCert = await this.findIssuerCertificate(currentCert);\n            if (!issuerCert) {\n                // The issuer is not in the issuers store — try\n                // the explicitly provided chain.\n                issuerCert = findIssuerCertificateInChain(currentCert, certificates);\n                if (!issuerCert || issuerCert === currentCert) {\n                    return VerificationStatus.BadCertificateChainIncomplete;\n                }\n            }\n\n            // ── Step 3: Signature verification ───────────────────\n            try {\n                if (!verifyCertificateSignature(currentCert, issuerCert)) {\n                    return VerificationStatus.BadCertificateInvalid;\n                }\n            } catch (_err) {\n                return VerificationStatus.BadCertificateInvalid;\n            }\n\n            // ── Step 4: Issuer must be in the issuers store ──────\n            // Per GDS 7.8.2.6: \"This Method will return a\n            // validation error if the Certificate is issued by a CA\n            // and the Certificate for the issuer is not in the\n            // TrustList\"\n            const issuerThumbprint = makeFingerprint(issuerCert);\n            if (!(await this.hasIssuer(issuerThumbprint))) {\n                return VerificationStatus.BadCertificateChainIncomplete;\n            }\n\n            // ── Step 6: Revocation check ─────────────────────────\n            const revokedStatus = await this.isCertificateRevoked(currentCert, issuerCert);\n            if (revokedStatus === VerificationStatus.BadCertificateRevoked) {\n                if (!opts.acceptRevokedCertificate) {\n                    return VerificationStatus.BadCertificateRevoked;\n                }\n            } else if (revokedStatus === VerificationStatus.BadCertificateRevocationUnknown) {\n                if (!opts.ignoreMissingRevocationList) {\n                    return VerificationStatus.BadCertificateRevocationUnknown;\n                }\n            }\n\n            // Move up the chain\n            currentCert = issuerCert;\n            try {\n                currentInfo = exploreCertificate(currentCert);\n            } catch (_err) {\n                return VerificationStatus.BadCertificateInvalid;\n            }\n        }\n\n        // All checks passed — trust the leaf certificate.\n        // Pass the full chain so the PEM on disk preserves\n        // intermediate CA certificates (chain-on-disk,\n        // leaf-only-in-memory principle).\n        await this.trustCertificate(certificates);\n        return VerificationStatus.Good;\n    }\n\n    /**\n     * Check whether an issuer certificate is still needed by any\n     * certificate in the trusted store.\n     *\n     * This is used before removing an issuer to ensure that\n     * doing so would not break the chain of any trusted\n     * certificate.\n     *\n     * @param issuerCertificate - the CA certificate to check\n     * @returns `true` if at least one trusted certificate was\n     *   signed by this issuer.\n     */\n    public async isIssuerInUseByTrustedCertificate(issuerCertificate: Certificate): Promise<boolean> {\n        await this.#readCertificates();\n        for (const entry of this.#thumbs.trusted.values()) {\n            if (!entry.certificate) continue;\n            try {\n                if (verifyCertificateSignature(entry.certificate, issuerCertificate)) {\n                    return true;\n                }\n            } catch (_err) {\n                // Skip certificates that can't be verified\n            }\n        }\n        return false;\n    }\n\n    /**\n     *  find the issuer certificate among the trusted  issuer certificates.\n     *\n     *  The findIssuerCertificate method is an asynchronous method that attempts to find\n     *  the issuer certificate for a given certificate from the list of issuer certificate declared in the PKI\n     *\n     *  - If the certificate is self-signed, it returns the certificate itself.\n     *\n     *  - If the certificate has no extension 3, it is assumed to be generated by an old system, and a null value is returned.\n     *\n     *  - the method checks both issuer and trusted certificates and returns the appropriate issuercertificate,\n     *    if found. If multiple matching certificates are found, a warning is logged to the console.\n     *\n     */\n    public async findIssuerCertificate(certificate: Certificate | Certificate[]): Promise<Certificate | null> {\n        const firstCertificate = coerceCertificateChain(certificate)[0];\n        const certInfo = exploreCertificate(firstCertificate);\n\n        if (isSelfSigned2(certInfo)) {\n            // the certificate is self signed so is it's own issuer.\n            return firstCertificate;\n        }\n\n        const wantedIssuerKey = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier;\n\n        if (!wantedIssuerKey) {\n            // Certificate has no extension 3 ! the certificate might have been generated by an old system\n            debugLog(\"Certificate has no extension 3\");\n            return null;\n        }\n\n        const issuerCertificates = [...this.#thumbs.issuers.certs.values()];\n\n        const selectedIssuerCertificates = findMatchingIssuerKey(issuerCertificates, wantedIssuerKey);\n\n        if (selectedIssuerCertificates.length > 0) {\n            if (selectedIssuerCertificates.length > 1) {\n                warningLog(\"Warning more than one issuer certificate exists with subjectKeyIdentifier \", wantedIssuerKey);\n            }\n            return selectedIssuerCertificates[0].certificate || null;\n        }\n        // check also in trusted  list\n        const trustedCertificates = [...this.#thumbs.trusted.values()];\n        const selectedTrustedCertificates = findMatchingIssuerKey(trustedCertificates, wantedIssuerKey);\n\n        if (selectedTrustedCertificates.length > 1) {\n            warningLog(\n                \"Warning more than one certificate exists with subjectKeyIdentifier in trusted certificate list \",\n                wantedIssuerKey,\n                selectedTrustedCertificates.length\n            );\n        }\n        return selectedTrustedCertificates.length > 0 ? selectedTrustedCertificates[0].certificate : null;\n    }\n\n    /**\n     * Outcome status for {@link CertificateManager.completeCertificateChain}.\n     */\n    public static readonly ChainCompletionStatus = ChainCompletionStatus;\n\n    /**\n     * Complete a certificate chain by walking the issuer store.\n     *\n     * Starting from the last certificate in the provided chain, this method\n     * repeatedly calls {@link findIssuerCertificate} to locate the parent\n     * certificate until it reaches a self-signed root or can no longer find\n     * an issuer.\n     *\n     * @param chain - the (potentially partial) certificate chain, leaf first\n     * @param maxDepth - maximum number of issuers to append (default: 10)\n     * @returns a {@link ChainCompletionResult} containing the (possibly completed)\n     *          chain, a status code, and an optional diagnostic message.\n     */\n    public async completeCertificateChain(chain: Certificate[], maxDepth = 10): Promise<ChainCompletionResult> {\n        if (chain.length === 0) {\n            return {\n                chain,\n                status: ChainCompletionStatus.EmptyChain,\n                message: \"Input chain is empty — nothing to complete.\"\n            };\n        }\n\n        // Re-scan the issuers folder to ensure we have the latest\n        await this.#scanCertFolder(this.issuersCertFolder, this.#thumbs.issuers.certs);\n\n        const result = [...chain];\n        let depth = 0;\n\n        while (depth < maxDepth) {\n            const lastCert = result[result.length - 1];\n            const lastInfo = exploreCertificate(lastCert);\n\n            // Stop if the last certificate is self-signed (root)\n            if (isSelfSigned2(lastInfo)) {\n                const wasExtended = result.length > chain.length;\n                return {\n                    chain: result,\n                    status: wasExtended ? ChainCompletionStatus.ChainCompleted : ChainCompletionStatus.AlreadyComplete,\n                    message: wasExtended\n                        ? `Chain completed: ${result.length - chain.length} issuer(s) appended, ending at self-signed root \"${lastInfo.tbsCertificate.subject.commonName}\".`\n                        : `Chain is already complete (self-signed root \"${lastInfo.tbsCertificate.subject.commonName}\").`\n                };\n            }\n\n            const issuerCert = await this.findIssuerCertificate(lastCert);\n            if (!issuerCert) {\n                // Cannot find the issuer — chain remains partial\n                const cn = lastInfo.tbsCertificate.subject.commonName ?? \"?\";\n                const akid = lastInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier ?? \"?\";\n                const msg =\n                    `Cannot find issuer for \"${cn}\" ` +\n                    `(authorityKeyIdentifier: ${akid}). ` +\n                    `Ensure the CA certificate is present in the issuers/certs folder.`;\n                warningLog(`completeCertificateChain: ${msg}`);\n                return {\n                    chain: result,\n                    status: ChainCompletionStatus.IssuerNotFound,\n                    message: msg\n                };\n            }\n\n            // Avoid loops: don't add the certificate if it's already in the chain\n            const issuerFingerprint = makeFingerprint(issuerCert);\n            const alreadyInChain = result.some((c) => makeFingerprint(c) === issuerFingerprint);\n            if (alreadyInChain) {\n                return {\n                    chain: result,\n                    status: ChainCompletionStatus.AlreadyComplete,\n                    message: `Chain ends at root \"${exploreCertificate(issuerCert).tbsCertificate.subject.commonName}\" (already present in chain).`\n                };\n            }\n\n            result.push(issuerCert);\n            depth++;\n        }\n\n        // maxDepth exceeded\n        return {\n            chain: result,\n            status: ChainCompletionStatus.MaxDepthReached,\n            message: `Chain completion stopped after ${maxDepth} iterations — possible circular chain or very deep hierarchy.`\n        };\n    }\n\n    /**\n     *\n     * check if the certificate explicitly appear in the trust list, the reject list or none.\n     * In case of being in the reject and trusted list at the same time is consider: rejected.\n     * @internal\n     * @private\n     */\n    async #checkRejectedOrTrusted(certificate: Certificate | Certificate[]): Promise<CertificateStatus> {\n        const firstCertificate = coerceCertificateChain(certificate)[0];\n        const fingerprint = makeFingerprint(firstCertificate);\n\n        debugLog(\"#checkRejectedOrTrusted fingerprint \", short(fingerprint));\n\n        await this.#readCertificates();\n\n        if (this.#thumbs.rejected.has(fingerprint)) {\n            return \"rejected\";\n        }\n        if (this.#thumbs.trusted.has(fingerprint)) {\n            return \"trusted\";\n        }\n        return \"unknown\";\n    }\n\n    async #moveCertificate(certificateOrChain: Certificate | Certificate[], newStatus: CertificateStatus) {\n        await this.withLock2(async () => {\n            const chain = coerceCertificateChain(certificateOrChain);\n            const certificate = chain[0]; // leaf — used for indexing\n            const fingerprint = makeFingerprint(certificate);\n\n            let status = await this.#checkRejectedOrTrusted(certificate);\n            if (status === \"unknown\") {\n                // # unknown means rejected — write full chain to disk\n                const pem = toPem(chain, \"CERTIFICATE\");\n                const filename = path.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);\n                await fs.promises.writeFile(filename, pem);\n                this.#thumbs.rejected.set(fingerprint, { certificate, filename });\n                status = \"rejected\";\n            }\n\n            debugLog(\"#moveCertificate\", fingerprint.substring(0, 10), \"from\", status, \"to\", newStatus);\n\n            if (status !== \"rejected\" && status !== \"trusted\") {\n                throw new Error(`#moveCertificate: unexpected status '${status}' for certificate ${fingerprint.substring(0, 10)}`);\n            }\n\n            if (status !== newStatus) {\n                const indexSrc = status === \"rejected\" ? this.#thumbs.rejected : this.#thumbs.trusted;\n                const srcEntry = indexSrc.get(fingerprint);\n\n                if (!srcEntry) {\n                    debugLog(\" cannot find certificate \", fingerprint.substring(0, 10), \" in\", status);\n                    throw new Error(`#moveCertificate: certificate ${fingerprint.substring(0, 10)} not found in ${status} index`);\n                }\n                const destFolder = newStatus === \"trusted\" ? this.trustedFolder : this.rejectedFolder;\n                const certificateDest = path.join(destFolder, path.basename(srcEntry.filename));\n\n                debugLog(\"#moveCertificate\", fingerprint.substring(0, 10), \"old name\", srcEntry.filename);\n                debugLog(\"#moveCertificate\", fingerprint.substring(0, 10), \"new name\", certificateDest);\n                await fs.promises.rename(srcEntry.filename, certificateDest);\n                indexSrc.delete(fingerprint);\n                const indexDest = newStatus === \"trusted\" ? this.#thumbs.trusted : this.#thumbs.rejected;\n                indexDest.set(fingerprint, { certificate, filename: certificateDest });\n            }\n        });\n    }\n    #findAssociatedCRLs(issuerCertificate: Certificate): CRLData | null {\n        const issuerCertificateInfo = exploreCertificate(issuerCertificate);\n        const key = issuerCertificateInfo.tbsCertificate.subjectFingerPrint;\n        return this.#thumbs.issuersCrl.get(key) ?? this.#thumbs.crl.get(key) ?? null;\n    }\n\n    /**\n     * Check whether a certificate has been revoked by its issuer's CRL.\n     *\n     * - Self-signed certificates are never considered revoked.\n     * - If no `issuerCertificate` is provided, the method attempts\n     *   to find it via {@link findIssuerCertificate}.\n     *\n     * @param certificate - the DER-encoded certificate to check\n     * @param issuerCertificate - optional issuer certificate; looked\n     *   up automatically when omitted\n     * @returns `Good` if not revoked, `BadCertificateRevoked` if the\n     *   serial number appears in a CRL,\n     *   `BadCertificateRevocationUnknown` if no CRL is available,\n     *   or `BadCertificateChainIncomplete` if the issuer cannot be\n     *   found.\n     */\n    public async isCertificateRevoked(\n        certificate: Certificate | Certificate[],\n        issuerCertificate?: Certificate | null\n    ): Promise<VerificationStatus> {\n        const chain = coerceCertificateChain(certificate);\n        const firstCertificate = chain[0];\n        if (isSelfSigned3(firstCertificate)) {\n            return VerificationStatus.Good;\n        }\n\n        if (!issuerCertificate) {\n            issuerCertificate = await this.findIssuerCertificate(firstCertificate);\n        }\n        if (!issuerCertificate) {\n            issuerCertificate = findIssuerCertificateInChain(firstCertificate, chain);\n        }\n        if (!issuerCertificate) {\n            return VerificationStatus.BadCertificateChainIncomplete;\n        }\n        const crls = this.#findAssociatedCRLs(issuerCertificate);\n\n        if (!crls) {\n            return VerificationStatus.BadCertificateRevocationUnknown;\n        }\n        const certInfo = exploreCertificate(firstCertificate);\n        const serialNumber =\n            certInfo.tbsCertificate.serialNumber || certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.serial || \"\";\n\n        const key = certInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint || \"<unknown>\";\n        const crl2 = this.#thumbs.crl.get(key) ?? null;\n\n        if (crls.serialNumbers[serialNumber] || crl2?.serialNumbers[serialNumber]) {\n            return VerificationStatus.BadCertificateRevoked;\n        }\n        return VerificationStatus.Good;\n    }\n\n    #pendingCrlToProcess = 0;\n    #onCrlProcessWaiters: (() => void)[] = [];\n    #queue: { index: Map<string, CRLData>; filename: string }[] = [];\n    #onCrlFileAdded(index: Map<string, CRLData>, filename: string) {\n        this.#queue.push({ index, filename });\n        this.#pendingCrlToProcess += 1;\n        if (this.#pendingCrlToProcess === 1) {\n            this.#processNextCrl();\n        }\n    }\n    async #processNextCrl() {\n        try {\n            const nextCRL = this.#queue.shift();\n            if (!nextCRL) return;\n            const { index, filename } = nextCRL;\n            const crl = await readCertificateRevocationList(filename);\n            const crlInfo = exploreCertificateRevocationList(crl);\n            debugLog(chalk.cyan(\"add CRL in folder \"), filename);\n            const fingerprint = crlInfo.tbsCertList.issuerFingerprint;\n            if (!index.has(fingerprint)) {\n                index.set(fingerprint, { crls: [], serialNumbers: {} });\n            }\n            const data = index.get(fingerprint) || { crls: [], serialNumbers: {} };\n            data.crls.push({ crlInfo, filename });\n\n            // now inject serial numbers\n            for (const revokedCertificate of crlInfo.tbsCertList.revokedCertificates) {\n                const serialNumber = revokedCertificate.userCertificate;\n                if (!data.serialNumbers[serialNumber]) {\n                    data.serialNumbers[serialNumber] = revokedCertificate.revocationDate;\n                }\n            }\n            debugLog(chalk.cyan(\"CRL\"), fingerprint, \"serial numbers = \", Object.keys(data.serialNumbers));\n        } catch (err) {\n            debugLog(\"CRL filename error =\");\n            debugLog(err);\n        }\n        this.#pendingCrlToProcess -= 1;\n        if (this.#pendingCrlToProcess === 0) {\n            for (const waiter of this.#onCrlProcessWaiters) {\n                waiter();\n            }\n            this.#onCrlProcessWaiters.length = 0;\n        } else {\n            this.#processNextCrl();\n        }\n    }\n    async #readCertificates(): Promise<void> {\n        if (this.#readCertificatesCalled) {\n            return;\n        }\n        this.#readCertificatesCalled = true;\n\n        // Chokidar configuration choices:\n        //\n        // usePolling: false (default)\n        //   Use native OS file-system events (inotify on Linux,\n        //   FSEvents on macOS, ReadDirectoryChangesW on Windows)\n        //   for near-real-time detection of cert/CRL additions\n        //   and removals. This is significantly faster than\n        //   polling (milliseconds vs seconds).\n        //\n        //   Set OPCUA_PKI_USE_POLLING=true to revert to polling\n        //   for environments where native events are unreliable\n        //   (NFS, CIFS, Docker volumes, or other remote/virtual\n        //   file systems).\n        //\n        // persistent: false\n        //   Watchers do NOT keep the Node.js event loop alive.\n        //   This prevents the process from hanging if the\n        //   CertificateManager is not properly disposed. The\n        //   trade-off is that watchers stop receiving events if\n        //   there are no other active handles — acceptable since\n        //   CertificateManager always runs alongside a server.\n        //\n        // awaitWriteFinish: not set\n        //   Certificate and CRL files are small (typically < 5 KB)\n        //   and written atomically (fs.writeFile). No need to\n        //   wait for write stabilization, which would add a 2s+\n        //   delay before the in-memory index is updated.\n        //\n        const usePolling = process.env.OPCUA_PKI_USE_POLLING === \"true\";\n        const envInterval = process.env.OPCUA_PKI_POLLING_INTERVAL\n            ? parseInt(process.env.OPCUA_PKI_POLLING_INTERVAL, 10)\n            : undefined;\n        const pollingInterval = Math.min(10 * 60 * 1000, Math.max(100, envInterval ?? this.folderPollingInterval));\n        const chokidarOptions = {\n            usePolling,\n            ...(usePolling ? { interval: pollingInterval } : {}),\n            persistent: false\n        };\n\n        // Workaround for two chokidar v4 bugs with persistent:false:\n        //\n        //   1. Chokidar does not propagate persistent:false to the\n        //      underlying fs.watch() handles. Without .unref(), an\n        //      undisposed CertificateManager blocks process exit.\n        //\n        //   2. Chokidar does not register an 'error' handler on\n        //      fs.watch when persistent:false (handler.js l.160-168).\n        //      On Windows + Node < 22, the native handle fires EPERM\n        //      when the watched directory is removed, which becomes\n        //      an uncaught exception that crashes the process.\n        //\n        // We install a single shared fs.watch() interception BEFORE\n        // creating all 5 watchers.  Every captured handle gets both\n        // an error handler (fix #2) and is later .unref()'d (fix #1).\n        //\n        // The interception stays active until ALL watchers have\n        // emitted \"ready\" — chokidar creates fs.watch handles\n        // asynchronously during directory scanning, so we must keep\n        // the interception alive until that completes.\n        const allCapturedHandles: fs.FSWatcher[] = [];\n        const origWatch = fs.watch;\n        let watcherReadyCount = 0;\n        const totalWatchers = 5;\n\n        fs.watch = ((...args: Parameters<typeof fs.watch>) => {\n            const handle = origWatch.apply(fs, args);\n            handle.setMaxListeners(handle.getMaxListeners() + 1);\n            handle.on(\"error\", () => {\n                /* swallow – watched directory was removed */\n            });\n            allCapturedHandles.push(handle);\n            return handle;\n        }) as typeof fs.watch;\n\n        const createUnreffedWatcher = (folder: string) => {\n            const startIdx = allCapturedHandles.length;\n            const w = chokidar.watch(folder, chokidarOptions);\n            const unreffAll = () => {\n                // Unref only handles created for THIS watcher\n                for (let i = startIdx; i < allCapturedHandles.length; i++) {\n                    allCapturedHandles[i].unref();\n                }\n                // Restore fs.watch once ALL watchers are ready\n                watcherReadyCount++;\n                if (watcherReadyCount >= totalWatchers) {\n                    fs.watch = origWatch;\n                }\n            };\n            return { w, capturedHandles: allCapturedHandles.slice(startIdx), unreffAll };\n        };\n\n        // ── Phase 1: Async scan ─────────────────────────────────\n        // Populate the in-memory indexes by reading existing\n        // files. Uses async readdir/stat to yield the event loop\n        // between files. All 5 folders are scanned in parallel.\n        await Promise.all([\n            this.#scanCertFolder(this.trustedFolder, this.#thumbs.trusted),\n            this.#scanCertFolder(this.issuersCertFolder, this.#thumbs.issuers.certs),\n            this.#scanCertFolder(this.rejectedFolder, this.#thumbs.rejected),\n            this.#scanCrlFolder(this.crlFolder, this.#thumbs.crl),\n            this.#scanCrlFolder(this.issuersCrlFolder, this.#thumbs.issuersCrl)\n        ]);\n\n        // ── Phase 2: Deferred file watchers ─────────────────────\n        // Start chokidar watchers in the background. We do NOT\n        // await \"ready\" so initialize() returns immediately after\n        // the sync scan. Chokidar will re-discover existing files\n        // (harmless Map overwrites) then watch for live changes.\n        //\n        // When disableFileWatchers is set, skip the watcher setup\n        // entirely. The in-memory indexes are already populated\n        // from the scan above. Also restore fs.watch immediately\n        // since no watchers will be created.\n        if (this.#disableFileWatchers) {\n            fs.watch = origWatch;\n        } else {\n            this.#startWatcher(this.trustedFolder, this.#thumbs.trusted, createUnreffedWatcher, \"trusted\");\n            this.#startWatcher(this.issuersCertFolder, this.#thumbs.issuers.certs, createUnreffedWatcher, \"issuersCerts\");\n            this.#startWatcher(this.rejectedFolder, this.#thumbs.rejected, createUnreffedWatcher, \"rejected\");\n            this.#startCrlWatcher(this.crlFolder, this.#thumbs.crl, createUnreffedWatcher, \"crl\");\n            this.#startCrlWatcher(this.issuersCrlFolder, this.#thumbs.issuersCrl, createUnreffedWatcher, \"issuersCrl\");\n        }\n    }\n\n    /**\n     * Scan a certificate folder and populate the in-memory index.\n     * Uses async readdir/stat to yield the event loop between\n     * file reads, preventing main-loop stalls with large folders.\n     */\n    async #scanCertFolder(folder: string, index: Map<string, Entry>): Promise<void> {\n        if (!fs.existsSync(folder)) return;\n        const files = await fs.promises.readdir(folder);\n        for (const file of files) {\n            const filename = path.join(folder, file);\n            try {\n                const stat = await fs.promises.stat(filename);\n                if (!stat.isFile()) continue;\n                const certs = await readCertificateChainAsync(filename);\n                if (certs.length === 0) continue;\n                const certificate = certs[0];\n\n                // Legacy migration: if the file contained multiple\n                // certs (e.g. from old buggy toPem that wrapped a\n                // concatenated DER in a single PEM block), re-write\n                // with proper multi-block PEM and auto-register any\n                // intermediate CA certs in the issuers store.\n                // Best-effort: if the filesystem is read-only the\n                // migration is skipped — the leaf is still indexed.\n                if (certs.length > 1) {\n                    try {\n                        await fs.promises.writeFile(filename, toPem(certs, \"CERTIFICATE\"), \"ascii\");\n                    } catch (writeErr) {\n                        debugLog(`scanCertFolder: could not rewrite legacy PEM ${filename} (read-only fs?)`, writeErr);\n                    }\n                    for (let i = 1; i < certs.length; i++) {\n                        if (isIssuer(certs[i])) {\n                            try {\n                                await this.addIssuer(certs[i]);\n                            } catch (issuerErr) {\n                                debugLog(`scanCertFolder: could not auto-register issuer from ${filename}`, issuerErr);\n                            }\n                        }\n                    }\n                }\n\n                const info = exploreCertificate(certificate);\n                const fingerprint = makeFingerprint(certificate);\n                index.set(fingerprint, { certificate, filename, info });\n                this.#filenameToHash.set(filename, fingerprint);\n            } catch (err) {\n                debugLog(`scanCertFolder: skipping ${filename}`, err);\n            }\n        }\n    }\n\n    /**\n     * Scan a CRL folder and populate the in-memory CRL index.\n     */\n    async #scanCrlFolder(folder: string, index: Map<string, CRLData>): Promise<void> {\n        if (!fs.existsSync(folder)) return;\n        const files = await fs.promises.readdir(folder);\n        for (const file of files) {\n            const filename = path.join(folder, file);\n            try {\n                const stat = await fs.promises.stat(filename);\n                if (!stat.isFile()) continue;\n                this.#onCrlFileAdded(index, filename);\n            } catch (err) {\n                debugLog(`scanCrlFolder: skipping ${filename}`, err);\n            }\n        }\n        await this.#waitAndCheckCRLProcessingStatus();\n    }\n\n    /**\n     * Start a chokidar watcher for a CRL folder.\n     * Non-blocking — does NOT await \"ready\".\n     */\n    #startCrlWatcher(\n        folder: string,\n        index: Map<string, CRLData>,\n        createUnreffedWatcher: (folder: string) => { w: ChokidarFSWatcher; unreffAll: () => void },\n        store: CrlStore\n    ): void {\n        const { w, unreffAll } = createUnreffedWatcher(folder);\n        w.on(\"error\", (err: unknown) => {\n            debugLog(`chokidar CRL watcher error on ${folder}:`, err);\n        });\n        let ready = false;\n\n        w.on(\"unlink\", (filename: string) => {\n            for (const [key, data] of index.entries()) {\n                data.crls = data.crls.filter((c) => c.filename !== filename);\n                if (data.crls.length === 0) {\n                    index.delete(key);\n                }\n            }\n            if (ready) {\n                this.emit(\"crlRemoved\", { store, filename });\n            }\n        });\n        w.on(\"add\", (filename: string) => {\n            if (ready) {\n                this.#onCrlFileAdded(index, filename);\n                this.emit(\"crlAdded\", { store, filename });\n            }\n        });\n        w.on(\"change\", (changedPath: string) => {\n            debugLog(\"change in folder \", folder, changedPath);\n        });\n        this.#watchers.push(w as unknown as fs.FSWatcher);\n        this.#pendingUnrefs.add(unreffAll);\n        w.on(\"ready\", () => {\n            ready = true;\n            this.#pendingUnrefs.delete(unreffAll);\n            unreffAll();\n        });\n    }\n\n    /**\n     * Start a chokidar watcher for a certificate folder.\n     * Non-blocking — does NOT await \"ready\".\n     */\n    #startWatcher(\n        folder: string,\n        index: Map<string, Entry>,\n        createUnreffedWatcher: (folder: string) => { w: ChokidarFSWatcher; unreffAll: () => void },\n        store: CertificateStore\n    ): void {\n        const { w, unreffAll } = createUnreffedWatcher(folder);\n        w.on(\"error\", (err: unknown) => {\n            debugLog(`chokidar cert watcher error on ${folder}:`, err);\n        });\n        let ready = false;\n        w.on(\"unlink\", (filename: string) => {\n            debugLog(chalk.cyan(`unlink in folder ${folder}`), filename);\n            const h = this.#filenameToHash.get(filename);\n            if (h && index.has(h)) {\n                index.delete(h);\n                this.emit(\"certificateRemoved\", { store, fingerprint: h, filename });\n            }\n        });\n        w.on(\"add\", (filename: string) => {\n            debugLog(chalk.cyan(`add in folder ${folder}`), filename);\n            try {\n                const certificate = readCertificateChain(filename)[0];\n                const info = exploreCertificate(certificate);\n                const fingerprint = makeFingerprint(certificate);\n\n                const isNew = !index.has(fingerprint);\n                index.set(fingerprint, { certificate, filename, info });\n                this.#filenameToHash.set(filename, fingerprint);\n\n                debugLog(\n                    chalk.magenta(\"CERT\"),\n                    info.tbsCertificate.subjectFingerPrint,\n                    info.tbsCertificate.serialNumber,\n                    info.tbsCertificate.extensions?.authorityKeyIdentifier?.authorityCertIssuerFingerPrint\n                );\n                if (ready || isNew) {\n                    this.emit(\"certificateAdded\", { store, certificate, fingerprint, filename });\n                }\n            } catch (err) {\n                debugLog(`Walk files in folder ${folder} with file ${filename}`);\n                debugLog(err);\n            }\n        });\n        w.on(\"change\", (changedPath: string) => {\n            debugLog(chalk.cyan(`change in folder ${folder}`), changedPath);\n            try {\n                const certificate = readCertificateChain(changedPath)[0];\n                const newFingerprint = makeFingerprint(certificate);\n                const oldHash = this.#filenameToHash.get(changedPath);\n                if (oldHash && oldHash !== newFingerprint) {\n                    index.delete(oldHash);\n                }\n                index.set(newFingerprint, { certificate, filename: changedPath, info: exploreCertificate(certificate) });\n                this.#filenameToHash.set(changedPath, newFingerprint);\n                this.emit(\"certificateChange\", { store, certificate, fingerprint: newFingerprint, filename: changedPath });\n            } catch (err) {\n                debugLog(`change event: failed to re-read ${changedPath}`, err);\n            }\n        });\n        this.#watchers.push(w as unknown as fs.FSWatcher);\n        this.#pendingUnrefs.add(unreffAll);\n        w.on(\"ready\", () => {\n            ready = true;\n            this.#pendingUnrefs.delete(unreffAll);\n            unreffAll();\n            debugLog(\"ready\");\n            debugLog([...index.keys()].map((k) => k.substring(0, 10)));\n        });\n    }\n\n    // make sure that all crls have been processed.\n    async #waitAndCheckCRLProcessingStatus(): Promise<void> {\n        return new Promise((resolve, _reject) => {\n            if (this.#pendingCrlToProcess === 0) {\n                setImmediate(resolve);\n                return;\n            }\n            this.#onCrlProcessWaiters.push(resolve);\n        });\n    }\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2022-2026 Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\nimport { createCertificateSigningRequest, pemToPrivateKey, Subject } from \"node-opcua-crypto\";\nimport type { CreateCertificateSigningRequestWithConfigOptions } from \"../common\";\nimport { display, displaySubtitle } from \"../display\";\n\n/**\n * create a certificate signing request\n */\nexport async function createCertificateSigningRequestAsync(\n    certificateSigningRequestFilename: string,\n    params: CreateCertificateSigningRequestWithConfigOptions\n): Promise<void> {\n    assert(params);\n    assert(params.rootDir);\n    assert(params.configFile);\n    assert(params.privateKey);\n    assert(typeof params.privateKey === \"string\");\n    assert(fs.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);\n\n    //  assert(fs.existsSync(params.configFile), \"config file must exist \" + params.configFile);\n    assert(fs.existsSync(params.rootDir), \"RootDir key must exist\");\n    assert(typeof certificateSigningRequestFilename === \"string\");\n\n    const subject = params.subject ? new Subject(params.subject).toString() : undefined;\n    displaySubtitle(\"- Creating a Certificate Signing Request with subtile\");\n\n    const privateKeyPem = await fs.promises.readFile(params.privateKey, \"utf-8\");\n    const privateKey = await pemToPrivateKey(privateKeyPem);\n\n    const { csr } = await createCertificateSigningRequest({\n        privateKey,\n        dns: params.dns,\n        ip: params.ip,\n        subject,\n        applicationUri: params.applicationUri,\n        purpose: params.purpose\n    });\n    await fs.promises.writeFile(certificateSigningRequestFilename, csr, \"utf-8\");\n\n    display(`- privateKey ${params.privateKey}`);\n    display(`- certificateSigningRequestFilename ${certificateSigningRequestFilename}`);\n\n    // to verify that the CSR is correct:\n    // openssl  req -in ./tmp/without_openssl.csr -noout -verify\n}\n","// ---------------------------------------------------------------------------------------------------------------------\n// node-opcua-pki\n// ---------------------------------------------------------------------------------------------------------------------\n// Copyright (c) 2022-2026 Sterfive.com\n// ---------------------------------------------------------------------------------------------------------------------\n//\n// This  project is licensed under the terms of the MIT license.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n// documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so,  subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n// Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n// ---------------------------------------------------------------------------------------------------------------------\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\n\nimport {\n    CertificatePurpose,\n    createSelfSignedCertificate as createSelfSignedCertificate1,\n    pemToPrivateKey,\n    Subject\n} from \"node-opcua-crypto\";\nimport { adjustDate, type CreateSelfSignCertificateWithConfigParam } from \"../common\";\nimport { displayTitle } from \"../display\";\n\nexport async function createSelfSignedCertificateAsync(\n    certificate: string,\n    params: CreateSelfSignCertificateWithConfigParam\n): Promise<void> {\n    params.purpose = params.purpose || CertificatePurpose.ForApplication;\n    assert(params.purpose, \"Please provide a Certificate Purpose\");\n    /**\n     * note: due to a limitation of openssl ,\n     *       it is not possible to control the startDate of the certificate validity\n     *       to achieve this the certificateAuthority tool shall be used.\n     */\n    assert(fs.existsSync(params.configFile));\n    assert(fs.existsSync(params.rootDir));\n    assert(fs.existsSync(params.privateKey));\n    if (!params.subject) {\n        throw Error(\"Missing subject\");\n    }\n\n    assert(typeof params.applicationUri === \"string\");\n    assert(Array.isArray(params.dns));\n\n    // xx no key size in self-signed assert(params.keySize == 2048 || params.keySize == 4096);\n\n    //            processAltNames(params);\n    adjustDate(params);\n    assert(Object.prototype.hasOwnProperty.call(params, \"validity\"));\n\n    let subject: Subject | string = new Subject(params.subject);\n    subject = subject.toString();\n\n    // xx const certificateRequestFilename = certificate + \".csr\";\n    const purpose = params.purpose;\n\n    displayTitle(\"Generate a certificate request\");\n\n    const privateKeyPem = await fs.promises.readFile(params.privateKey, \"utf-8\");\n    const privateKey = await pemToPrivateKey(privateKeyPem);\n\n    const { cert } = await createSelfSignedCertificate1({\n        privateKey,\n        notBefore: params.startDate,\n        notAfter: params.endDate,\n        validity: params.validity,\n        dns: params.dns,\n        ip: params.ip,\n        subject,\n        applicationUri: params.applicationUri,\n        purpose\n    });\n    await fs.promises.writeFile(certificate, cert, \"utf-8\");\n}\n\nexport async function createSelfSignedCertificate(\n    certificate: string,\n    params: CreateSelfSignCertificateWithConfigParam\n): Promise<void> {\n    await createSelfSignedCertificateAsync(certificate, params);\n}\n"],"mappings":"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","names":["assert","fs","os","path","chalk","Subject","assert","fs","assert","assert","assert","child_process","fs","os","byline","chalk","fs","path","chalk","doDebug","chalk","quote","path","fs","opensslFolder","opensslExecPath","url","execute","chalk","child_process","byline","os","fs","assert","chalk","execute","n","assert","fs","chalk","chalk","assert","fs","path","assert","fs","path","path","fs","s","q","n","assert","fs","path","config","config","n","q","assert","path","fs","Subject","os","chalk","fs","path","chalk","exploreCertificate","generatePrivateKeyFile","toPem","assert","fs","Subject","assert","fs","Subject","assert","fs","CertificatePurpose","pemToPrivateKey","Subject","CertificatePurpose","assert","fs","Subject","pemToPrivateKey","configurationFileSimpleTemplate","fs","exploreCertificate","VerificationStatus","CertificateManagerState","ChainCompletionStatus","path","toPem","chalk","generatePrivateKeyFile"]}