node-opcua-pki 6.12.2 → 6.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/index.ts","../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-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\nexport {\n CertificateAuthority,\n type CertificateAuthorityOptions,\n type GenerateKeyPairAndSignOptions,\n type GenerateKeyPairAndSignPFXOptions,\n type InitializeCSRResult,\n type InstallCACertificateResult,\n type SignCertificateOptions\n} from \"./ca/certificate_authority\";\nexport * from \"./misc/subject\";\nexport * from \"./pki/certificate_manager\";\nexport * from \"./pki/toolbox_pfx\";\nexport * from \"./toolbox/common\";\nexport { install_prerequisite } from \"./toolbox/with_openssl/install_prerequisite\";\n","// ---------------------------------------------------------------------------------------------------------------------\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/**\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 */\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\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 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 *\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 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 * 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":["import_node_assert","import_node_fs","import_node_os","import_node_path","import_chalk","import_node_opcua_crypto","import_node_assert","import_node_fs","assert","import_node_assert","fs","chalk","path","assert","import_node_assert","import_node_child_process","import_node_fs","import_node_os","import_byline","import_chalk","import_node_fs","import_node_path","import_chalk","doDebug","url","chalk","child_process","byline","quote","path","os","fs","opensslFolder","opensslExecPath","ProgressBar","wget","yauzl","execute","chalk","child_process","byline","os","fs","assert","chalk","execute","n","assert","fs","import_chalk","chalk","import_node_assert","import_node_fs","import_node_path","import_node_assert","import_node_fs","import_node_path","path","fs","s","q","n","assert","fs","path","config","config","n","q","assert","path","fs","os","chalk","import_node_fs","import_node_path","import_chalk","import_node_opcua_crypto","import_node_assert","import_node_fs","import_node_opcua_crypto","assert","fs","import_node_assert","import_node_fs","import_node_opcua_crypto","assert","fs","createSelfSignedCertificate1","configurationFileSimpleTemplate","fs","VerificationStatus","CertificateManagerState","path","chalk","chokidar"]}
1
+ {"version":3,"sources":["../lib/index.ts","../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-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\nexport {\n CertificateAuthority,\n type CertificateAuthorityOptions,\n type GenerateKeyPairAndSignOptions,\n type GenerateKeyPairAndSignPFXOptions,\n type InitializeCSRResult,\n type InstallCACertificateResult,\n type SignCertificateOptions\n} from \"./ca/certificate_authority\";\nexport * from \"./misc/subject\";\nexport * from \"./pki/certificate_manager\";\nexport * from \"./pki/toolbox_pfx\";\nexport * from \"./toolbox/common\";\nexport { install_prerequisite } from \"./toolbox/with_openssl/install_prerequisite\";\n","// ---------------------------------------------------------------------------------------------------------------------\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\n // OPC UA validators (UaExpert, compliance tools) require every\n // certificate — including the root/intermediate CA — to carry a\n // SubjectAltName extension. RFC 5280 does not strictly mandate it\n // on CA certificates but the OPC UA Profile does. Build a stable\n // URI SAN derived from the CA CommonName so the resulting CA cert\n // passes the spec check.\n const caCommonName = subject.commonName || \"NodeOPCUA-CA\";\n setEnv(\"ALTNAME\", `URI:urn:${caCommonName}`);\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 \"subjectAltName = $ENV::ALTNAME\\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 // OPC UA validators (UaExpert, ANSI compliance tools) flag a missing\n // SubjectAltName on the CA cert. Pull it from the ALTNAME env var\n // populated by construct_CertificateAuthority.\n \"subjectAltName = $ENV::ALTNAME\\n\" +\n 'nsComment = \"CA Certificate generated by Node-OPCUA Certificate utility using openssl\"\\n' +\n \"#nsCertType = sslCA, emailCA\\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":["import_node_assert","import_node_fs","import_node_os","import_node_path","import_chalk","import_node_opcua_crypto","import_node_assert","import_node_fs","assert","import_node_assert","fs","chalk","path","assert","import_node_assert","import_node_child_process","import_node_fs","import_node_os","import_byline","import_chalk","import_node_fs","import_node_path","import_chalk","doDebug","url","chalk","child_process","byline","quote","path","os","fs","opensslFolder","opensslExecPath","ProgressBar","wget","yauzl","execute","chalk","child_process","byline","os","fs","assert","chalk","execute","n","assert","fs","import_chalk","chalk","import_node_assert","import_node_fs","import_node_path","import_node_assert","import_node_fs","import_node_path","path","fs","s","q","n","assert","fs","path","config","config","n","q","assert","path","fs","os","chalk","import_node_fs","import_node_path","import_chalk","import_node_opcua_crypto","import_node_assert","import_node_fs","import_node_opcua_crypto","assert","fs","import_node_assert","import_node_fs","import_node_opcua_crypto","assert","fs","createSelfSignedCertificate1","configurationFileSimpleTemplate","fs","VerificationStatus","CertificateManagerState","ChainCompletionStatus","path","chalk","chokidar"]}