node-opcua-pki 6.22.1 → 7.0.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/bin/install_prerequisite.ts +1 -1
- package/bin/pki.ts +1 -1
- package/dist/bin/install_prerequisite.d.ts +2 -0
- package/dist/bin/install_prerequisite.js +6 -0
- package/dist/bin/install_prerequisite.js.map +1 -0
- package/dist/bin/pki.d.ts +2 -0
- package/dist/bin/pki.js +4 -0
- package/dist/bin/pki.js.map +1 -0
- package/dist/lib/ca/backends/native_ca_backend.d.ts +49 -0
- package/dist/lib/ca/backends/native_ca_backend.js +365 -0
- package/dist/lib/ca/backends/native_ca_backend.js.map +1 -0
- package/dist/lib/ca/backends/openssl_ca_backend.d.ts +47 -0
- package/dist/lib/ca/backends/openssl_ca_backend.js +398 -0
- package/dist/lib/ca/backends/openssl_ca_backend.js.map +1 -0
- package/dist/lib/ca/certificate_authority.d.ts +62 -0
- package/dist/lib/ca/certificate_authority.js +91 -0
- package/dist/lib/ca/certificate_authority.js.map +1 -0
- package/dist/lib/ca/core/ca_backend.d.ts +89 -0
- package/dist/lib/ca/core/ca_backend.js +24 -0
- package/dist/lib/ca/core/ca_backend.js.map +1 -0
- package/dist/lib/ca/core/ca_database.d.ts +91 -0
- package/dist/lib/ca/core/ca_database.js +267 -0
- package/dist/lib/ca/core/ca_database.js.map +1 -0
- package/dist/{core-BwqSJPHc.d.mts → lib/ca/core/certificate_authority_core.d.ts} +45 -394
- package/dist/lib/ca/core/certificate_authority_core.js +1300 -0
- package/dist/lib/ca/core/certificate_authority_core.js.map +1 -0
- package/dist/lib/ca/core/index.d.ts +17 -0
- package/dist/lib/ca/core/index.js +25 -0
- package/dist/lib/ca/core/index.js.map +1 -0
- package/dist/lib/ca/crypto_create_CA.d.ts +1 -0
- package/dist/lib/ca/crypto_create_CA.js +821 -0
- package/dist/lib/ca/crypto_create_CA.js.map +1 -0
- package/dist/lib/ca/index.d.ts +1 -0
- package/dist/lib/ca/index.js +2 -0
- package/dist/lib/ca/index.js.map +1 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.d.ts +2 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.js +170 -0
- package/dist/lib/ca/templates/ca_config_template.cnf.js.map +1 -0
- package/dist/lib/core.d.ts +33 -0
- package/dist/lib/core.js +56 -0
- package/dist/lib/core.js.map +1 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.js +37 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/misc/applicationurn.d.ts +1 -0
- package/dist/lib/misc/applicationurn.js +40 -0
- package/dist/lib/misc/applicationurn.js.map +1 -0
- package/dist/lib/misc/hostname.d.ts +8 -0
- package/dist/lib/misc/hostname.js +82 -0
- package/dist/lib/misc/hostname.js.map +1 -0
- package/dist/lib/misc/subject.d.ts +2 -0
- package/dist/lib/misc/subject.js +2 -0
- package/dist/lib/misc/subject.js.map +1 -0
- package/dist/{index.d.ts → lib/pki/certificate_manager.d.ts} +24 -248
- package/dist/lib/pki/certificate_manager.js +2302 -0
- package/dist/lib/pki/certificate_manager.js.map +1 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.d.ts +2 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.js +74 -0
- package/dist/lib/pki/templates/simple_config_template.cnf.js.map +1 -0
- package/dist/lib/pki/toolbox_pfx.d.ts +127 -0
- package/dist/lib/pki/toolbox_pfx.js +248 -0
- package/dist/lib/pki/toolbox_pfx.js.map +1 -0
- package/dist/lib/toolbox/common.d.ts +150 -0
- package/dist/lib/toolbox/common.js +82 -0
- package/dist/lib/toolbox/common.js.map +1 -0
- package/dist/lib/toolbox/common2.d.ts +25 -0
- package/dist/lib/toolbox/common2.js +97 -0
- package/dist/lib/toolbox/common2.js.map +1 -0
- package/dist/lib/toolbox/config.d.ts +5 -0
- package/dist/lib/toolbox/config.js +28 -0
- package/dist/lib/toolbox/config.js.map +1 -0
- package/dist/lib/toolbox/debug.d.ts +5 -0
- package/dist/lib/toolbox/debug.js +35 -0
- package/dist/lib/toolbox/debug.js.map +1 -0
- package/dist/lib/toolbox/display.d.ts +4 -0
- package/dist/lib/toolbox/display.js +56 -0
- package/dist/lib/toolbox/display.js.map +1 -0
- package/dist/lib/toolbox/index.d.ts +5 -0
- package/dist/lib/toolbox/index.js +28 -0
- package/dist/lib/toolbox/index.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.d.ts +4 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.js +50 -0
- package/dist/lib/toolbox/with_openssl/_create_random_file.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/_env.d.ts +58 -0
- package/dist/lib/toolbox/with_openssl/_env.js +126 -0
- package/dist/lib/toolbox/with_openssl/_env.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.js +72 -0
- package/dist/lib/toolbox/with_openssl/create_certificate_signing_request.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.js +92 -0
- package/dist/lib/toolbox/with_openssl/create_private_key.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.js +135 -0
- package/dist/lib/toolbox/with_openssl/create_self_signed_certificate.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.d.ts +67 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.js +246 -0
- package/dist/lib/toolbox/with_openssl/execute_openssl.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/index.d.ts +5 -0
- package/dist/lib/toolbox/with_openssl/index.js +30 -0
- package/dist/lib/toolbox/with_openssl/index.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.d.ts +7 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.js +358 -0
- package/dist/lib/toolbox/with_openssl/install_prerequisite.js.map +1 -0
- package/dist/lib/toolbox/with_openssl/toolbox.d.ts +53 -0
- package/dist/lib/toolbox/with_openssl/toolbox.js +190 -0
- package/dist/lib/toolbox/with_openssl/toolbox.js.map +1 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.d.ts +5 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.js +69 -0
- package/dist/lib/toolbox/without_openssl/create_certificate_signing_request.js.map +1 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.d.ts +3 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.js +78 -0
- package/dist/lib/toolbox/without_openssl/create_self_signed_certificate.js.map +1 -0
- package/dist/lib/toolbox/without_openssl/index.d.ts +2 -0
- package/dist/lib/toolbox/without_openssl/index.js +25 -0
- package/dist/lib/toolbox/without_openssl/index.js.map +1 -0
- package/package.json +11 -30
- package/dist/bin/install_prerequisite.mjs +0 -18
- package/dist/bin/install_prerequisite.mjs.map +0 -1
- package/dist/bin/pki.mjs +0 -5932
- package/dist/bin/pki.mjs.map +0 -1
- package/dist/chunk-EQ4DGI4M.mjs +0 -1917
- package/dist/chunk-EQ4DGI4M.mjs.map +0 -1
- package/dist/chunk-GCHH54PS.mjs +0 -30
- package/dist/chunk-GCHH54PS.mjs.map +0 -1
- package/dist/core-BwqSJPHc.d.ts +0 -1041
- package/dist/core.d.mts +0 -2
- package/dist/core.d.ts +0 -2
- package/dist/core.js +0 -1902
- package/dist/core.js.map +0 -1
- package/dist/core.mjs +0 -21
- package/dist/core.mjs.map +0 -1
- package/dist/index.d.mts +0 -1003
- package/dist/index.js +0 -5009
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -3145
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/index.ts","../lib/ca/backends/openssl_ca_backend.ts","../lib/toolbox/with_openssl/_env.ts","../lib/toolbox/with_openssl/execute_openssl.ts","../lib/toolbox/with_openssl/install_prerequisite.ts","../lib/toolbox/with_openssl/toolbox.ts","../lib/ca/certificate_authority.ts","../lib/pki/certificate_manager.ts","../lib/toolbox/without_openssl/create_certificate_signing_request.ts","../lib/toolbox/without_openssl/create_self_signed_certificate.ts","../lib/pki/templates/simple_config_template.cnf.ts","../lib/pki/toolbox_pfx.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\n// re-exported so consumers can `instanceof`-check the fail-closed errors thrown\n// by CertificateManager.initialize()/getPrivateKey(), and implement an opaque\n// key-operations provider (CertificateManagerOptions.keyOperations), without\n// depending on node-opcua-crypto directly\nexport {\n type AsymmetricDecryptParams,\n type AsymmetricSignParams,\n type IKeyOperations,\n type KeyMetadata,\n PrivateKeyPassphraseRequiredError,\n PrivateKeyUnavailableError\n} from \"node-opcua-crypto\";\nexport { NativeCaBackend } from \"./ca/backends/native_ca_backend\";\nexport { OpenSslCaBackend } from \"./ca/backends/openssl_ca_backend\";\nexport {\n CertificateAuthority,\n type CertificateAuthorityOptions,\n type GenerateKeyPairAndSignOptions,\n type GenerateKeyPairAndSignPFXOptions,\n type InitializeCSRResult,\n type InstallCACertificateResult,\n type PkiBackendCapabilities,\n type SignCertificateOptions\n} from \"./ca/certificate_authority\";\nexport type { CaBackend } from \"./ca/core/ca_backend\";\nexport {\n CertificateAuthorityCore,\n type CertificateAuthorityCoreOptions\n} from \"./ca/core/certificate_authority_core\";\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-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 fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Subject } from \"node-opcua-crypto\";\nimport { displaySubtitle, displayTitle, makePath, type Params, type ProcessAltNamesParam } from \"../../toolbox\";\nimport {\n buildSubjectAltNameString,\n cleanupStaticConfig,\n ensure_openssl_installed,\n execute_openssl,\n execute_openssl_no_failure,\n generateStaticConfig,\n type OpensslPassArg,\n passinArg,\n setEnv,\n x509Date\n} from \"../../toolbox/with_openssl\";\nimport type { CaBackend } from \"../core/ca_backend\";\nimport type { CertificateAuthorityCore } from \"../core/certificate_authority_core\";\n\nconst n = makePath;\n\n/** The ALTNAME openssl embeds as `subjectAltName` on the CA's own certificate/CSR — see `[v3_ca]` / `[v3_ca_req]`. */\nfunction caAltName(ca: CertificateAuthorityCore): string {\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 return `URI:urn:${ca.subject.commonName || \"NodeOPCUA-CA\"}`;\n}\n\n/**\n * Every render of `conf/caconfig.cnf` needs `ALTNAME` / `CDP_URL` /\n * `AIA_VALUE` resolved, **even for an `openssl` subcommand that only reads\n * a different section** (`ca -gencrl` only needs `[crl_ext]`, `ca -revoke`\n * only needs `[ca]`): openssl's config loader (`NCONF_load`) expands every\n * `$ENV::NAME` reference in the file line by line as it parses it, not\n * lazily per queried section, and every section in this template — not\n * just the two gated behind `{{#CDP_URL}}` / `{{#AIA_VALUE}}` — references\n * `$ENV::ALTNAME` unconditionally. Leaving any of the three unresolved\n * makes openssl fall through to its own native `$ENV::` resolution (a real\n * OS environment variable, which is never set) and fail with \"variable has\n * no value\".\n *\n * Passed explicitly rather than relying on whatever `setEnv()` last wrote\n * to the shared registry (see {@link generateStaticConfig}'s\n * `envOverrides`) — empty string means \"omit the extension\" for the two\n * gated keys (the template's `{{#KEY}}` block strips itself on an empty\n * value), never \"fall back to the registry\".\n *\n * Exported as the single implementation of the CDP/AIA value format:\n * `CertificateAuthority._wireRevocationEnvVars` (the legacy registry shim)\n * delegates here so the format cannot drift between the two mechanisms.\n */\nexport function caConfigEnvOverrides(ca: CertificateAuthorityCore, altName: string = caAltName(ca)): Record<string, string> {\n const aiaLegs: string[] = [];\n if (ca.ocspResponderUrl) {\n aiaLegs.push(`OCSP;URI:${ca.ocspResponderUrl}`);\n }\n if (ca.caIssuersUrl) {\n aiaLegs.push(`caIssuers;URI:${ca.caIssuersUrl}`);\n }\n return {\n ALTNAME: altName,\n CDP_URL: ca.crlDistributionUrl ?? \"\",\n AIA_VALUE: aiaLegs.join(\",\")\n };\n}\n\n/**\n * The default {@link CaBackend}: shells out to the `openssl` CLI, exactly as\n * `CertificateAuthority` always has — this is a pure code move (plus a lock\n * around every database-mutating operation, temp-config cleanup, and\n * explicit `envOverrides` on every config render) with no change in the\n * openssl commands issued or the files produced.\n */\nexport class OpenSslCaBackend implements CaBackend {\n /** This backend is the `openssl` executable, so it has to be there. */\n /**\n * `-passin env:` argv and env for an openssl call that has to load this\n * CA's key. Built here rather than on the CA: the passphrase is the\n * CA's business, but expressing it as an openssl argument is this\n * backend's, and the core has no reason to know the flag exists.\n */\n async #passin(ca: CertificateAuthorityCore): Promise<OpensslPassArg> {\n return passinArg(await ca._privateKeyPassphrase());\n }\n\n /** The openssl CLI loads its key from a file, so it cannot call out to an HSM. */\n public readonly supportsExternalSigner = false;\n\n public async preflight(): Promise<void> {\n await ensure_openssl_installed();\n }\n\n /**\n * Render `conf/caconfig.cnf` once with explicit overrides, run `fn`\n * with the rendered path, and always remove the temp file. Structural\n * guarantee that (a) every render carries the three required env\n * values and (b) no rendered `<name>.<pid>-<n>.tmp` file is ever left\n * behind — each was previously a per-method discipline.\n */\n async #withConfig<T>(\n ca: CertificateAuthorityCore,\n altName: string,\n fn: (configFile: string, options: { cwd: string }) => Promise<T>\n ): Promise<T> {\n const caRootDir = path.resolve(ca.rootDir);\n const options = { cwd: caRootDir };\n const configFile = generateStaticConfig(\"conf/caconfig.cnf\", options, caConfigEnvOverrides(ca, altName));\n try {\n return await fn(configFile, options);\n } finally {\n await cleanupStaticConfig(configFile, options);\n }\n }\n\n async #generateCaCsrWith(\n ca: CertificateAuthorityCore,\n configFile: string,\n options: { cwd: string },\n privateKeyFile: string,\n csrFile: string\n ): Promise<void> {\n const passin = await this.#passin(ca);\n displayTitle(\"Generate a certificate request for the CA key\");\n await execute_openssl(\n [\n \"req\",\n \"-new\",\n \"-sha256\",\n \"-extensions\",\n \"v3_ca_req\",\n \"-config\",\n n(configFile),\n \"-key\",\n n(privateKeyFile),\n \"-out\",\n n(csrFile),\n \"-subj\",\n ca.subject.toString(),\n ...passin.args\n ],\n { ...options, env: passin.env }\n );\n }\n\n /**\n * `openssl ca -gencrl` signs the CRL with the CA key it finds through the\n * config file (`private_key = $dir/private/cakey.pem`), so it needs the\n * CA passphrase like every other `openssl ca` invocation.\n */\n async #regenerateCrlWith(ca: CertificateAuthorityCore, configFile: string, options: { cwd: string }): Promise<void> {\n const passin = await this.#passin(ca);\n // produce a CRL in PEM format\n displaySubtitle(\"regenerate CRL (Certificate Revocation List)\");\n await execute_openssl([\"ca\", \"-gencrl\", \"-config\", n(configFile), \"-out\", \"crl/revocation_list.crl\", ...passin.args], {\n ...options,\n env: passin.env\n });\n await execute_openssl(\n [\"crl\", \"-in\", \"crl/revocation_list.crl\", \"-out\", \"crl/revocation_list.der\", \"-outform\", \"der\"],\n options\n );\n\n displaySubtitle(\"Display (Certificate Revocation List)\");\n await execute_openssl([\"crl\", \"-in\", n(ca.revocationList), \"-text\", \"-noout\"], options);\n }\n\n public async generateCaCsr(\n ca: CertificateAuthorityCore,\n _caRootDir: string,\n privateKeyFile: string,\n csrFile: string\n ): Promise<void> {\n await this.#withConfig(ca, caAltName(ca), (configFile, options) =>\n this.#generateCaCsrWith(ca, configFile, options, privateKeyFile, csrFile)\n );\n }\n\n public async bootstrap(ca: CertificateAuthorityCore): Promise<void> {\n const caRootDir = path.resolve(ca.rootDir);\n const privateKeyFilename = path.join(caRootDir, \"private/cakey.pem\");\n const csrFilename = path.join(caRootDir, \"private/cakey.csr\");\n\n // one render serves the CSR, the CA-certificate signature, and the\n // initial CRL (the overrides are identical for all three)\n await this.#withConfig(ca, caAltName(ca), async (configFile, options) => {\n await this.#generateCaCsrWith(ca, configFile, options, privateKeyFilename, csrFilename);\n\n const issuerCA = ca._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 // the -CAkey is the *issuer's* key: its passphrase, not this CA's\n const issuerPassin = await this.#passin(issuerCA);\n const signWithIssuer = async () => {\n await execute_openssl(\n [\n \"x509\",\n \"-sha256\",\n \"-req\",\n \"-days\",\n \"3650\",\n \"-extensions\",\n \"v3_ca\",\n \"-extfile\",\n n(configFile),\n \"-in\",\n \"private/cakey.csr\",\n \"-CA\",\n n(issuerCert),\n \"-CAkey\",\n n(issuerKey),\n \"-CAserial\",\n n(issuerSerial),\n \"-out\",\n \"public/cacert.pem\",\n ...issuerPassin.args\n ],\n { ...options, env: issuerPassin.env }\n );\n };\n // `-CAserial` read-increment-writes the ISSUER's serial file,\n // so this must hold the issuer's directory lock, not just the\n // subordinate's (which the caller already holds). The only\n // cross-CA lock ordering in the codebase is subordinate ->\n // issuer, so this cannot deadlock — unless both CAs share one\n // rootDir (a degenerate configuration), where the lock is\n // already held and re-acquiring the non-reentrant file lock\n // would self-deadlock: skip it there.\n if (path.resolve(issuerCA.rootDir) === caRootDir) {\n await signWithIssuer();\n } else {\n await issuerCA._withCaDirectoryLock(signWithIssuer);\n }\n } else {\n // Root CA — self-signed\n displayTitle(\"Generate CA Certificate (self-signed)\");\n const passin = await this.#passin(ca);\n await execute_openssl(\n [\n \"x509\",\n \"-sha256\",\n \"-req\",\n \"-days\",\n \"3650\",\n \"-extensions\",\n \"v3_ca\",\n \"-extfile\",\n n(configFile),\n \"-in\",\n \"private/cakey.csr\",\n \"-signkey\",\n n(privateKeyFilename),\n \"-out\",\n \"public/cacert.pem\",\n ...passin.args\n ],\n { ...options, env: passin.env }\n );\n }\n\n displaySubtitle(\"generate initial CRL (Certificate Revocation List)\");\n await this.#regenerateCrlWith(ca, configFile, options);\n });\n displayTitle(\"Create Certificate Authority (CA) ---> DONE\");\n }\n\n public async regenerateCrl(ca: CertificateAuthorityCore): Promise<void> {\n await this.#withConfig(ca, caAltName(ca), (configFile, options) => this.#regenerateCrlWith(ca, configFile, options));\n }\n\n public async signSubordinateCsr(\n ca: CertificateAuthorityCore,\n csrFile: string,\n certFile: string,\n validityDays: number\n ): Promise<void> {\n // `[v3_ca]`'s subjectAltName should describe the *subordinate* being\n // certified, not `ca` (the issuer doing the signing) — but nothing\n // here parses `csrFile`'s own subject, so this reuses the issuer's\n // identity as a well-formed placeholder. This is a pre-existing gap,\n // not a regression: the original code left this value to whatever a\n // prior, unrelated operation had last set in the shared env\n // registry (an accidental \"URI:undefined\" or a stale value from a\n // different CA entirely), so this is deterministic where the\n // original was not, even though neither describes the subordinate.\n await this.#withConfig(ca, caAltName(ca), async (configFile, options) => {\n const caRootDir = options.cwd;\n const passin = await this.#passin(ca);\n await execute_openssl(\n [\n \"x509\",\n \"-sha256\",\n \"-req\",\n \"-days\",\n String(validityDays),\n \"-extensions\",\n \"v3_ca\",\n \"-extfile\",\n n(configFile),\n \"-in\",\n n(csrFile),\n \"-CA\",\n n(ca.caCertificate),\n \"-CAkey\",\n n(path.join(caRootDir, \"private/cakey.pem\")),\n \"-CAserial\",\n n(path.join(caRootDir, \"serial\")),\n \"-out\",\n n(certFile),\n ...passin.args\n ],\n { ...options, env: passin.env }\n );\n });\n }\n\n public async signEndEntityCsr(\n ca: CertificateAuthorityCore,\n certificate: string,\n csr: string,\n params1: Params,\n sanOverride: Required<ProcessAltNamesParam>\n ): Promise<void> {\n await this.#withConfig(ca, buildSubjectAltNameString(sanOverride), async (configFile, options) => {\n displaySubtitle(\"- then we ask the authority to sign the certificate signing request\");\n const passin = await this.#passin(ca);\n await execute_openssl(\n [\n \"ca\",\n \"-config\",\n configFile,\n \"-startdate\",\n x509Date(params1.startDate),\n \"-enddate\",\n x509Date(params1.endDate),\n \"-batch\",\n \"-out\",\n n(certificate),\n \"-in\",\n n(csr),\n ...passin.args\n ],\n { ...options, env: passin.env }\n );\n\n displaySubtitle(\"- dump the certificate for a check\");\n await execute_openssl([\"x509\", \"-in\", n(certificate), \"-dates\", \"-fingerprint\", \"-purpose\", \"-noout\"], options);\n });\n }\n\n public async revoke(ca: CertificateAuthorityCore, certificate: string, reason: string): Promise<void> {\n // one render serves the revocation and the CRL regeneration\n // openssl writes its entropy file here; the native backend has no such notion\n setEnv(\"RANDFILE\", path.join(ca.rootDir, \"random.rnd\"));\n await this.#withConfig(ca, caAltName(ca), async (configFile, options) => {\n displaySubtitle(\"Revoke certificate\");\n const passin = await this.#passin(ca);\n await execute_openssl_no_failure(\n [\"ca\", \"-verbose\", \"-config\", n(configFile), \"-revoke\", certificate, \"-crl_reason\", reason, ...passin.args],\n { ...options, env: passin.env }\n );\n\n // regenerate CRL (Certificate Revocation List)\n await this.#regenerateCrlWith(ca, configFile, options);\n\n displaySubtitle(\"Verify that certificate is revoked\");\n await execute_openssl_no_failure(\n [\n \"verify\",\n \"-verbose\",\n \"-CRLfile\",\n n(ca.revocationList),\n \"-CAfile\",\n n(ca.caCertificate),\n \"-crl_check\",\n n(certificate)\n ],\n options\n );\n\n // produce CRL in DER format\n displaySubtitle(\"Produce CRL in DER form \");\n await execute_openssl(\n [\"crl\", \"-in\", n(ca.revocationList), \"-out\", \"crl/revocation_list.der\", \"-outform\", \"der\"],\n options\n );\n // produce CRL in PEM format with text\n displaySubtitle(\"Produce CRL in PEM form \");\n await execute_openssl(\n [\"crl\", \"-in\", n(ca.revocationList), \"-out\", \"crl/revocation_list.pem\", \"-outform\", \"pem\", \"-text\"],\n options\n );\n });\n }\n\n public async createSelfSignedCertificate(\n ca: CertificateAuthorityCore,\n certificateFile: string,\n privateKeyFile: string,\n params: Params\n ): Promise<void> {\n // Explicit overrides like every other method: `$ENV::ALTNAME` in the\n // rendered config comes from THIS call's params, never from whatever\n // the shared env registry happened to hold — the registry-based\n // render made a concurrent operation's SANs (or an empty ALTNAME\n // from a concurrent revoke) leak into this certificate. CDP/AIA now\n // deterministically follow this CA's configured URLs as well.\n const envOverrides = caConfigEnvOverrides(ca, buildSubjectAltNameString(params));\n const configFile = generateStaticConfig(ca.configFile, { cwd: ca.rootDir }, envOverrides);\n const options = {\n cwd: ca.rootDir,\n openssl_conf: makePath(configFile)\n };\n\n try {\n const subject = params.subject ? new Subject(params.subject).toString() : \"\";\n const subjectOptions = subject && subject.length > 1 ? [\"-subj\", subject] : [];\n const csrFile = `${certificateFile}_csr`;\n\n // `req -new -key` reads (and, for an encrypted key, must decrypt)\n // privateKeyFile too: without -passin it hangs indefinitely\n // waiting on a passphrase prompt that never comes, even with\n // stdin redirected to /dev/null — unlike e.g. `pkcs12`/`rsa\n // -pubout`, which fail fast instead. Reproduced directly against\n // OpenSSL 3.5.4. Empty passin is a no-op against a plaintext key.\n const passin = await this.#passin(ca);\n\n displaySubtitle(\"- the certificate signing request\");\n await execute_openssl(\n [\n \"req\",\n \"-new\",\n \"-sha256\",\n ...subjectOptions,\n \"-batch\",\n \"-key\",\n n(privateKeyFile),\n \"-out\",\n n(csrFile),\n ...passin.args\n ],\n { ...options, env: passin.env }\n );\n\n displaySubtitle(\"- creating the self-signed certificate\");\n await execute_openssl(\n [\n \"ca\",\n \"-selfsign\",\n \"-keyfile\",\n n(privateKeyFile),\n \"-startdate\",\n x509Date(params.startDate),\n \"-enddate\",\n x509Date(params.endDate),\n \"-batch\",\n \"-out\",\n n(certificateFile),\n \"-in\",\n n(csrFile),\n ...passin.args\n ],\n { ...options, env: passin.env }\n );\n\n displaySubtitle(\"- dump the certificate for a check\");\n await execute_openssl([\"x509\", \"-in\", n(certificateFile), \"-dates\", \"-fingerprint\", \"-purpose\", \"-noout\"], {});\n\n displaySubtitle(\"- verify self-signed certificate\");\n await execute_openssl_no_failure([\"verify\", \"-verbose\", \"-CAfile\", n(certificateFile), n(certificateFile)], options);\n\n await fs.promises.unlink(csrFile);\n } finally {\n await cleanupStaticConfig(configFile, { cwd: ca.rootDir });\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// ---------------------------------------------------------------------------------------------------------------------\nimport type { ProcessAltNamesParam } from \"../common\";\nimport { g_config } from \"../config\";\nimport { warningLog } from \"../debug\";\n\n/**\n * Environment variables passed through to every openssl child process,\n * beyond whatever a specific call explicitly needs (e.g. a passphrase via\n * `ExecuteOptions.env`).\n *\n * This app's own config-templating (ALTNAME, CDP_URL, AIA_VALUE — see\n * `generateStaticConfig`) substitutes values directly into the config file\n * content; it never relies on the openssl child actually seeing those as\n * real OS environment variables. Only `OPENSSL_CONF` and `RANDFILE` are\n * genuinely required at the OS level, alongside whatever the shell, the\n * dynamic loader, and the openssl binary themselves need to start up on\n * each platform.\n *\n * Rather than handing the child the full parent process environment —\n * which, in a hosting application, may hold unrelated secrets (API keys,\n * database credentials, tokens) that have no business being visible to a\n * spawned openssl process — only this curated, minimal set is passed\n * through. The same set is used to *discover* the openssl binary\n * (`install_prerequisite`) and to *run* it, so a binary that is found is\n * also one that can start.\n */\nexport const SAFE_ENV_PASSTHROUGH: ReadonlySet<string> = new Set([\n // POSIX/Windows shell and process essentials\n \"path\",\n \"home\",\n \"userprofile\",\n \"temp\",\n \"tmp\",\n \"tmpdir\",\n \"systemroot\",\n \"windir\",\n \"comspec\",\n \"pathext\",\n \"appdata\",\n \"localappdata\",\n // dynamic loader: a custom-built or relocated openssl (e.g. under /opt,\n // or Homebrew on macOS) may need these to find its own libcrypto/libssl\n \"ld_library_path\",\n \"dyld_library_path\",\n \"dyld_fallback_library_path\",\n // locale, so openssl's textual output stays parseable\n \"lang\",\n \"lc_all\",\n \"lc_ctype\",\n // openssl configuration this app, or the host OS/user, may rely on\n \"openssl_conf\",\n \"randfile\",\n \"openssl_modules\",\n \"openssl_engines\",\n \"ssl_cert_file\",\n \"ssl_cert_dir\"\n]);\n\n/**\n * Build the environment for an openssl child process: the allowlisted\n * subset of `process.env` (see {@link SAFE_ENV_PASSTHROUGH}), overlaid with\n * `extra` (per-invocation values such as a passphrase for `-passin env:`).\n * Never writes to `process.env`.\n */\nexport function buildChildEnv(extra?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const key of Object.keys(process.env)) {\n if (SAFE_ENV_PASSTHROUGH.has(key.toLowerCase())) {\n env[key] = process.env[key];\n }\n }\n return { ...env, ...extra };\n}\n\n/**\n * A copy of `options` safe to hand to a logger: the values of any\n * per-invocation `env` are replaced by their variable names, so a passphrase\n * passed for `-passin env:NAME` can never end up in debug output.\n */\nexport function redactEnvForLog<T extends { env?: NodeJS.ProcessEnv }>(options: T): Omit<T, \"env\"> & { env?: string[] } {\n const { env, ...rest } = options;\n return env ? { ...rest, env: Object.keys(env) } : rest;\n}\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 unsetEnv(varName: string): void {\n delete 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\n/**\n * Build the openssl `subjectAltName` value string (`URI:..., DNS:..., IP:...`)\n * from SAN parameters. The single implementation of this format — used both\n * by {@link processAltNames} (which publishes it to the shared env registry)\n * and by callers that pass it as an explicit per-render override — so a\n * format fix cannot land in one copy only.\n */\nexport function buildSubjectAltNameString(params: ProcessAltNamesParam): string {\n return [\n `URI:${params.applicationUri}`,\n ...(params.dns ?? []).map((d: string) => `DNS:${d}`),\n ...(params.ip ?? []).map((d: string) => `IP:${d}`)\n ].join(\", \");\n}\n\nexport function processAltNames(params: ProcessAltNamesParam) {\n params.dns = params.dns || [];\n params.ip = params.ip || [];\n setEnv(\"ALTNAME\", buildSubjectAltNameString(params));\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 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 { makePath } from \"../common2\";\nimport { g_config } from \"../config\";\nimport { debugLog, displayError, doDebug, warningLog } from \"../debug\";\nimport { buildChildEnv, redactEnvForLog, setEnv } from \"./_env\";\nimport { get_openssl_exec_path } from \"./install_prerequisite\";\n\nlet opensslPath: string | undefined; // not initialized\n\nconst n = makePath;\n\n/**\n * Arguments for one openssl invocation, one array element per argv entry,\n * e.g. `[\"x509\", \"-in\", certificateFile, \"-noout\"]`. Values are handed to\n * the openssl process as-is: no shell is involved, so no quoting or escaping\n * is needed and no value can be interpreted as a shell metacharacter.\n */\nexport type OpensslArgs = readonly string[];\n\nexport interface ExecuteOptions {\n cwd?: string;\n hideErrorMessage?: boolean;\n /**\n * Extra environment variables for this invocation only. Merged with a\n * curated, minimal passthrough of the current process environment (see\n * `SAFE_ENV_PASSTHROUGH` in `_env.ts`) for the spawned child — never the\n * full parent environment, and never written to `process.env`, so\n * concurrent invocations cannot see or clobber each other's values and\n * unrelated secrets sitting in the host application's environment are\n * never handed to the openssl child.\n *\n * Use this together with openssl's `-passin env:NAME` / `-passout\n * env:NAME` syntax to pass a secret without ever placing it in argv: an\n * argv value is visible to other processes via the process list. See\n * {@link passinArg} / {@link passoutArg}.\n */\n env?: NodeJS.ProcessEnv;\n}\n\n// Fixed names for the per-invocation environment variables used to pass\n// passphrases to openssl via `-passin env:NAME` / `-passout env:NAME`\n// instead of placing them in argv (see ExecuteOptions.env for why). Safe to\n// reuse across concurrent calls: each call passes its own `env` object to a\n// distinct child process — nothing here touches process.env, so there is no\n// shared mutable state to race on.\nconst PASSIN_ENV_VAR = \"NODE_OPCUA_PKI_OPENSSL_PASSIN\";\nconst PASSOUT_ENV_VAR = \"NODE_OPCUA_PKI_OPENSSL_PASSOUT\";\n\n/** openssl argv entries plus the env they need; merge `env` into `ExecuteOptions.env`. */\nexport interface OpensslPassArg {\n args: string[];\n env: NodeJS.ProcessEnv;\n}\n\n/**\n * `-passin env:...` for a source passphrase (an input key or PFX bundle).\n *\n * Always emit this — with `\"\"` when there is no passphrase — for any command\n * that reads a private key which *might* be encrypted: without a `-passin`,\n * openssl prompts on the controlling terminal for an encrypted key. An empty\n * `-passin` on a plaintext key is a no-op; on an encrypted key it fails fast\n * with `bad decrypt` instead.\n */\nexport function passinArg(passphrase = \"\"): OpensslPassArg {\n return { args: [\"-passin\", `env:${PASSIN_ENV_VAR}`], env: { [PASSIN_ENV_VAR]: passphrase } };\n}\n\n/** `-passout env:...` for an output passphrase (a PFX bundle being written). */\nexport function passoutArg(passphrase = \"\"): OpensslPassArg {\n return { args: [\"-passout\", `env:${PASSOUT_ENV_VAR}`], env: { [PASSOUT_ENV_VAR]: passphrase } };\n}\n\n/** For logs and error messages only: a shell-like rendering of an argv. Never re-parsed. */\nfunction renderForDisplay(file: string, args: OpensslArgs): string {\n return [file, ...args].map((a) => (a === \"\" || /[\\s\"'`$\\\\]/.test(a) ? JSON.stringify(a) : a)).join(\" \");\n}\n\n/**\n * True when a string carries a character we should not hand to the openssl\n * child. Two kinds are rejected:\n *\n * - ASCII control characters, which have no place in an option or a path; and\n * - any *non-ASCII* punctuation, symbol, separator or format character.\n *\n * The second kind matters on Windows: a spawned program rebuilds its argument\n * list from the command line through the active ANSI code page, and that\n * conversion can \"best-fit\" a look-alike character onto a plain ASCII one — a\n * full-width quotation mark onto `\"`, a non-breaking space onto a space, and so\n * on — which would change where one argument ends and the next begins. Every\n * option and path this library passes to openssl is plain ASCII, so refusing\n * these characters costs nothing in normal use.\n *\n * Non-ASCII *letters, digits and combining marks* are deliberately allowed, so\n * a PKI folder living under an accented or non-Latin user profile (for example\n * `C:\\\\Users\\\\José\\\\pki`) keeps working.\n */\nfunction hasUnsafeArgChar(value: string): boolean {\n for (const ch of value) {\n const code = ch.codePointAt(0) as number;\n if (code < 0x20 || code === 0x7f) {\n return true; // ASCII control character\n }\n if (code < 0x80) {\n continue; // ordinary ASCII: options, flags and paths use these\n }\n if (/[\\p{L}\\p{N}\\p{M}]/u.test(ch)) {\n continue; // real text (letters / digits / marks) in a configured path\n }\n return true; // non-ASCII punctuation, symbol, separator or format char\n }\n return false;\n}\n\n/**\n * Make sure the openssl binary path and every argument are safe to pass to the\n * child process (see {@link hasUnsafeArgChar}). Throws with the offending value\n * rather than letting it reach openssl.\n */\nfunction checkArgs(file: string, args: OpensslArgs): void {\n for (const value of [file, ...args]) {\n if (typeof value === \"string\" && hasUnsafeArgChar(value)) {\n throw new Error(`execute_openssl: refusing argument with an unexpected character: ${JSON.stringify(value)}`);\n }\n }\n}\n\n/**\n * Run `file` with `args` directly (no shell): each element of `args` reaches\n * the child as one argv entry, so a value can never be reinterpreted as a\n * shell metacharacter, a redirection, or a second command. stdin is\n * `/dev/null`: OpenSSL's app UI opens the console before honouring\n * `-passin` and calls tcgetattr on stdin when there is no controlling TTY;\n * a socket (Node's default child stdin) yields ENOTTY on Linux but\n * EOPNOTSUPP on macOS, which OpenSSL rejects and the key load fails even\n * with the right passphrase; /dev/null yields ENOTTY everywhere. It also\n * removes any way for openssl to block reading a password from stdin.\n */\nexport async function execute(file: string, args: OpensslArgs, options: ExecuteOptions): Promise<string> {\n const from = new Error();\n\n checkArgs(file, args);\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 const errorOutputs: string[] = [];\n const display = renderForDisplay(file, args);\n\n return await new Promise((resolve, reject) => {\n const child = child_process.spawn(file, [...args], {\n cwd: options.cwd,\n windowsHide: true,\n env: buildChildEnv(options.env),\n stdio: [\"ignore\", \"pipe\", \"pipe\"]\n });\n\n const fail = (message: string) => {\n // istanbul ignore next\n // The banner duplicates what the rejection already carries, and\n // this is the one place in this module that ignored the library's\n // own (default-on) silence. Callers that let the error propagate\n // still get the full command and stderr from the Error itself;\n // set VERBOSE to see it as it happens.\n if (!options.hideErrorMessage && !g_config.silent) {\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(message));\n console.error(chalk.bgWhiteBright.redBright(`${fence} OPENSSL ERROR ${fence}`));\n\n console.error(from.stack);\n }\n reject(new Error(message));\n };\n\n child.on(\"error\", (err: Error) => fail(`Command failed: ${display}\\n${err.message}`));\n child.on(\"close\", (code: number | null, signal: NodeJS.Signals | null) => {\n if (code === 0) {\n resolve(outputs.join(\"\"));\n return;\n }\n const why = signal ? `signal ${signal}` : `exit code ${code}`;\n fail(`Command failed: ${display}\\n${errorOutputs.join(\"\")}(${why})`);\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 if (child.stderr) {\n const stream1 = byline(child.stderr);\n stream1.on(\"data\", (line: string) => {\n line = line.toString();\n errorOutputs.push(`${line}\\n`);\n // istanbul ignore next\n if (!g_config.silent && displayError) {\n process.stdout.write(`${chalk.white(\" stderr \") + chalk.red(line)}\\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(args: OpensslArgs, options: ExecuteOpenSSLOptions): Promise<string> {\n return await execute_openssl(args, options);\n}\n\nexport async function execute_openssl_no_failure(args: OpensslArgs, options: ExecuteOpenSSLOptions) {\n options = options || {};\n options.hideErrorMessage = true;\n try {\n return await execute_openssl(args, 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\n/**\n * Run `openssl` with `args` (see {@link OpensslArgs}): no shell, one argv\n * entry per element, curated environment, /dev/null stdin.\n */\nexport async function execute_openssl(args: OpensslArgs, options: ExecuteOpenSSLOptions): Promise<string> {\n // never log `options` as-is: `options.env` may carry a passphrase\n debugLog(\"execute_openssl\", args, redactEnvForLog(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 \"), chalk.cyanBright(renderForDisplay(\"openssl\", args)));\n }\n await ensure_openssl_installed();\n return await execute(opensslPath as string, args, 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// ---------------------------------------------------------------------------------------------------------------------\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 { pipeline } from \"node:stream/promises\";\n\nimport byline from \"byline\";\nimport chalk from \"chalk\";\nimport yauzl from \"yauzl\";\n\nimport { warningLog } from \"../debug\";\nimport { buildChildEnv } from \"./_env\";\n\nconst doDebug = process.env.NODEOPCUAPKIDEBUG || false;\n\ninterface ExecuteResult {\n exitCode: number;\n output: string;\n}\n\n/**\n * Run `file` with `args` directly (no shell), same as execute_openssl's\n * `execute()`: the same curated environment (a binary discovered with the\n * full parent env but run with a reduced one could otherwise be found here\n * and then fail to load its libraries later), and /dev/null stdin.\n * Resolves with the exit code rather than rejecting on a non-zero exit;\n * rejects only when the process cannot be spawned at all.\n */\nasync function execute(file: string, args: string[], cwd?: string): Promise<ExecuteResult> {\n let output = \"\";\n\n return await new Promise<ExecuteResult>((resolve, reject) => {\n const child = child_process.spawn(file, args, {\n cwd,\n windowsHide: true,\n env: buildChildEnv(),\n stdio: [\"ignore\", \"pipe\", \"pipe\"]\n });\n child.on(\"error\", (err: Error) => reject(err));\n child.on(\"close\", (code: number | null) => resolve({ exitCode: code ?? 1, output }));\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 is_expected_openssl_version(strVersion: string): boolean {\n return !!strVersion.match(/OpenSSL \\d/);\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 // 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(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 const cwd = \".\";\n\n const { exitCode, output } = await execute(opensslExecPath, [\"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 * Try to find a system-installed openssl on Windows via `where`.\n * Returns the path if found and version is acceptable, otherwise undefined.\n */\n async function find_system_openssl_win32(): Promise<string | undefined> {\n try {\n const result = await execute(\"where\", [\"openssl\"]);\n if (result.exitCode !== 0) {\n return undefined;\n }\n // `where` may return multiple lines; take the first one\n const opensslPath = result.output.split(/\\r?\\n/)[0].trim();\n if (!opensslPath || !fs.existsSync(opensslPath)) {\n return undefined;\n }\n // verify version\n const versionResult = await execute(opensslPath, [\"version\"]);\n const version = versionResult.output.trim();\n if (versionResult.exitCode === 0 && is_expected_openssl_version(version)) {\n warningLog(\n chalk.green(\"Using system OpenSSL: \") + chalk.cyan(version) + chalk.green(\" at \") + chalk.cyan(opensslPath)\n );\n return opensslPath;\n }\n warningLog(chalk.yellow(\"System OpenSSL found but version not accepted: \") + version);\n return undefined;\n } catch (_err) {\n return undefined;\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 =\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\n const response = await fetch(url, { redirect: \"follow\" });\n if (!response.ok || !response.body) {\n throw new Error(`Failed to download OpenSSL from ${url}: ${response.status} ${response.statusText}`);\n }\n\n const contentLength = parseInt(response.headers.get(\"content-length\") || \"0\", 10);\n let downloaded = 0;\n let lastPercent = -1;\n\n const fileStream = fs.createWriteStream(outputFilename);\n\n // Use pipeline for proper backpressure and cleanup\n const body = response.body as unknown as Readable;\n body.on(\"data\", (chunk: Buffer) => {\n downloaded += chunk.length;\n if (contentLength > 0) {\n const percent = Math.floor((downloaded / contentLength) * 100);\n if (percent !== lastPercent && percent % 10 === 0) {\n lastPercent = percent;\n warningLog(` download progress: ${percent}%`);\n }\n }\n });\n\n await pipeline(body, fileStream);\n\n // Verify the downloaded file exists and has content\n const stat = fs.statSync(outputFilename);\n if (stat.size === 0) {\n fs.unlinkSync(outputFilename);\n throw new Error(`Downloaded file is empty: ${outputFilename}`);\n }\n\n warningLog(chalk.green(\"Download complete: \") + `${stat.size} bytes`);\n return { downloadedFile: outputFilename };\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 // 1. Try system-installed OpenSSL first (e.g. on CI runners)\n const systemOpenssl = await find_system_openssl_win32();\n if (systemOpenssl) {\n return systemOpenssl;\n }\n\n // 2. Check bundled OpenSSL at the expected local path\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 // 3. Download as last resort\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// ---------------------------------------------------------------------------------------------------------------------\n\nimport assert from \"node:assert\";\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport type { Filename } from \"../common\";\nimport { makePath } from \"../common2\";\nimport { g_config } from \"../config\";\nimport { getEnv, getEnvironmentVarNames, hasEnv } from \"./_env\";\nimport { type ExecuteOptions, execute_openssl, passinArg } 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\n/**\n * Strip Mustache-style conditional blocks from an OpenSSL config template.\n *\n * {{#KEY}}...{{/KEY}}\n *\n * The block content is **kept** (markers removed) iff `KEY` resolves to a\n * non-empty value — from `envOverrides` if it names the key, otherwise from\n * the global registry (see {@link exportedEnvVars}) — and stripped\n * (including the markers and the trailing newline) otherwise.\n *\n * Used to drive opt-in extensions like CRL Distribution Points and\n * Authority Information Access: the same template serves the\n * extension-set case (URL configured) and the extension-omitted case\n * (no URL configured).\n */\nfunction stripConditionalBlocks(template: string, envOverrides?: Readonly<Record<string, string>>): string {\n return template.replace(/\\{\\{#([A-Z_][A-Z0-9_]*)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}\\r?\\n?/g, (_match, key: string, content: string) => {\n const keep =\n envOverrides && Object.prototype.hasOwnProperty.call(envOverrides, key)\n ? envOverrides[key] !== \"\"\n : hasEnv(key) && getEnv(key) !== \"\";\n return keep ? content : \"\";\n });\n}\n\n/**\n * Render an OpenSSL config template (`$ENV::NAME` substitution, `{{#NAME}}`\n * conditional blocks) to a temporary file and return its path.\n *\n * `envOverrides`, when given, is consulted before the global env registry\n * for every key it names — the substituted value never depends on whatever\n * `setEnv()` calls other, possibly-concurrent code made before or after this\n * call. Pass it for any value the caller has already computed locally (as\n * every `CertificateAuthority` operation now does for `ALTNAME` /\n * `CDP_URL` / `AIA_VALUE`); omit it, as every other caller does, to keep\n * reading the shared registry exactly as before.\n */\nexport function generateStaticConfig(\n configPath: string,\n options?: ExecuteOptions,\n envOverrides?: Readonly<Record<string, string>>\n) {\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 // Strip conditional blocks first so unset placeholders never reach\n // the env-var substitution pass below.\n staticConfig = stripConditionalBlocks(staticConfig, envOverrides);\n // Substituted values go through the function form of String.replace: the\n // string form interprets `$`-sequences (`$&`, `$$`, \"$`\", `$'`) in the\n // VALUE as replacement patterns, so a subject CN or CDP/OCSP URL\n // containing `$&` would re-insert the `$ENV::NAME` literal into the\n // rendered config and every openssl call on it would fail with\n // \"variable has no value\".\n for (const envVar of getEnvironmentVarNames()) {\n if (envOverrides && Object.prototype.hasOwnProperty.call(envOverrides, envVar.key)) {\n continue; // overridden below\n }\n staticConfig = staticConfig.replace(new RegExp(envVar.pattern, \"gi\"), () => getEnv(envVar.key));\n }\n if (envOverrides) {\n for (const [key, value] of Object.entries(envOverrides)) {\n staticConfig = staticConfig.replace(new RegExp(`\\\\$ENV\\\\:\\\\:${key}`, \"gi\"), () => value);\n }\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\n/**\n * Remove a rendered temp config produced by {@link generateStaticConfig}.\n * `configFile` is whatever that function returned (relative to\n * `options.cwd` when a cwd was given, absolute otherwise). Every caller of\n * `generateStaticConfig` is expected to pair it with this in a `finally`,\n * or the `<name>.<pid>-<n>.tmp` files accumulate next to the real config\n * forever.\n */\nexport async function cleanupStaticConfig(configFile: string, options?: ExecuteOptions): Promise<void> {\n await fs.promises.rm(path.resolve(options?.cwd ?? \"\", configFile), { force: true });\n}\n\nconst n = makePath;\n\n/**\n * calculate the public key from private key\n * openssl rsa -pubout -in private_key.pem -passin env:...\n *\n * `-passin` is always emitted (empty for a plaintext key) so an encrypted\n * key without a passphrase fails fast instead of hanging on a TTY prompt.\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 * @param passphrase: decrypts `privateKeyFilename` if it is an encrypted PKCS#8 key\n */\nexport async function getPublicKeyFromPrivateKey(\n privateKeyFilename: string,\n publicKeyFilename: string,\n passphrase?: string\n): Promise<void> {\n assert(fs.existsSync(privateKeyFilename));\n const passin = passinArg(passphrase);\n await execute_openssl([\"rsa\", \"-pubout\", \"-in\", n(privateKeyFilename), \"-out\", n(publicKeyFilename), ...passin.args], {\n env: passin.env\n });\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 // no shell, so no `>` redirection: capture stdout and write it ourselves\n const output = await execute_openssl([\"x509\", \"-pubkey\", \"-in\", n(certificateFilename)], {});\n await fs.promises.writeFile(publicKeyFilename, output);\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\", 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","// ---------------------------------------------------------------------------------------------------------------------\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 { type OpensslPassArg, passinArg, setEnv, unsetEnv } from \"../toolbox/with_openssl\";\nimport { NativeCaBackend } from \"./backends/native_ca_backend\";\nimport { caConfigEnvOverrides, OpenSslCaBackend } from \"./backends/openssl_ca_backend\";\nimport { CertificateAuthorityCore, type CertificateAuthorityCoreOptions } from \"./core/certificate_authority_core\";\n\n// Everything the CA exposes lives in the core module; re-exported here so\n// that `import { ... } from \"./ca/certificate_authority\"` keeps resolving\n// exactly as it did before the split.\nexport * from \"./core/certificate_authority_core\";\n\n/**\n * Options for {@link CertificateAuthority}: the core's, except that the\n * backend is named rather than supplied.\n */\nexport interface CertificateAuthorityOptions extends Omit<CertificateAuthorityCoreOptions, \"backend\"> {\n /**\n * Signing backend to use.\n *\n * - `\"openssl\"` (default): shells out to the `openssl` CLI, as this\n * class always has.\n * - `\"native\"`: signs in pure JS via `node-opcua-crypto`, writing the\n * same on-disk database format, and needs no `openssl` executable.\n *\n * Setting `signer` implies `\"native\"`, since the openssl CLI can only\n * load a key from a file.\n *\n * To supply a backend instance directly - including one of your own -\n * use {@link CertificateAuthorityCore}, which is also the class to\n * build against when you do not want the openssl backend in your\n * bundle at all.\n */\n backend?: \"openssl\" | \"native\";\n}\n\n/**\n * A Certificate Authority with a backend chosen for you.\n *\n * This is {@link CertificateAuthorityCore} plus the convenience that made\n * it the historical API: name a backend, or name nothing and get openssl.\n * Because it resolves the name, it necessarily references both backends,\n * which is why the core exists separately - a program that never imports\n * this class never pulls the openssl backend into its bundle.\n */\nexport class CertificateAuthority extends CertificateAuthorityCore {\n constructor(options: CertificateAuthorityOptions) {\n if (options.signer && options.backend === \"openssl\") {\n throw new Error(\n \"CertificateAuthority: backend 'openssl' cannot be combined with 'signer' - \" +\n \"the openssl CLI loads its key from a file and cannot call an external signer. \" +\n \"Drop the backend option (a signer implies 'native') or drop the signer.\"\n );\n }\n const backend = options.signer || options.backend === \"native\" ? new NativeCaBackend() : new OpenSslCaBackend();\n super({ ...options, backend });\n }\n\n /**\n * @internal `-passin env:` argv + env for an openssl call that loads\n * this CA's key (always emitted, empty when none).\n *\n * The openssl backend builds its own now; this remains so that external\n * code calling it keeps working, and lives here rather than on the core\n * because the flag means nothing to a backend that spawns nothing.\n */\n public async _opensslPassin(): Promise<OpensslPassArg> {\n return passinArg(await this._privateKeyPassphrase());\n }\n\n /**\n * @internal\n * Legacy shim: publish the `CDP_URL` / `AIA_VALUE` config substitution\n * values to the shared env registry, or unset them so the matching\n * `{{#KEY}}...{{/KEY}}` blocks are stripped. Nothing in this package\n * reads the registry any more - every openssl config render receives\n * these values explicitly, from the same {@link caConfigEnvOverrides}\n * builder this delegates to, so the two cannot drift - kept only for\n * external code that renders openssl config templates against the\n * registry directly.\n *\n * It lives on this class rather than the core because it is meaningful\n * only to the openssl backend.\n */\n public _wireRevocationEnvVars(): void {\n const overrides = caConfigEnvOverrides(this);\n if (overrides.CDP_URL) {\n setEnv(\"CDP_URL\", overrides.CDP_URL);\n } else {\n unsetEnv(\"CDP_URL\");\n }\n if (overrides.AIA_VALUE) {\n setEnv(\"AIA_VALUE\", overrides.AIA_VALUE);\n } else {\n unsetEnv(\"AIA_VALUE\");\n }\n }\n}\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 CaSigner,\n type Certificate,\n type CertificateInternals,\n type CertificateRevocationList,\n type CertificateRevocationListInfo,\n caSignerFromKeyOperations,\n type DER,\n exploreCertificate,\n exploreCertificateInfo,\n exploreCertificateRevocationList,\n generatePrivateKeyFile,\n type IKeyOperations,\n keyOperationsFromPrivateKey,\n type LocalKeyOperations,\n makeSHA1Thumbprint,\n type PrivateKey,\n PrivateKeyUnavailableError,\n readCertificateChain,\n readCertificateChainAsync,\n readCertificateRevocationList,\n readPrivateKey,\n split_der,\n toPem,\n verifyCertificateSignature,\n writePrivateKeyFile\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 PrivateKeyPassphrase,\n PrivateKeyProvider,\n Thumbprint\n} from \"../toolbox/common\";\nimport { resolvePrivateKeyPassphrase } from \"../toolbox/common\";\nimport {\n ensurePrivateDirectory,\n isEncryptedPrivateKeyFile,\n makePath,\n mkdirRecursiveSync,\n restrictPrivateFilePermissions\n} 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 * Encrypt the private key at rest with this passphrase (opt-in,\n * default off). When set:\n * - a freshly generated key is written as encrypted PKCS#8;\n * - an existing *plaintext* key is re-encrypted in place by\n * {@link CertificateManager.initialize} (atomic rename, same as\n * {@link CertificateManager.reencryptPrivateKey}), so enabling the\n * option on an existing install never leaves the key in cleartext;\n * - an existing encrypted key requires the same passphrase — a\n * mismatch, or an encrypted key with no passphrase configured, fails\n * `initialize()` closed with `PrivateKeyPassphraseRequiredError`.\n *\n * A function is called at most once per `CertificateManager` instance\n * (the decrypted key is cached in memory for the instance's lifetime,\n * see {@link CertificateManager.getPrivateKey}). Never logged.\n *\n * Existing consumers that read `own/private/private_key.pem` directly\n * (e.g. node-opcua's `OPCUAServer`, or `createPFX` without its\n * `privateKeyPassphrase` option) still expect a plaintext key — do not\n * enable this unless every reader of that file goes through\n * {@link CertificateManager.getPrivateKey} or is given the passphrase.\n */\n privateKeyPassphrase?: PrivateKeyPassphrase;\n\n /**\n * Source the private key from somewhere other than\n * `own/private/private_key.pem` (an HSM, a KMS, ...). When set, it\n * overrides disk entirely for every operation that needs the private\n * key — the on-disk file is not read, and `privateKeyPassphrase` is\n * ignored.\n */\n privateKeyProvider?: PrivateKeyProvider;\n\n /**\n * Use a private key this manager can never read: an opaque\n * {@link IKeyOperations} provider (HSM, KMS, TPM, OS keystore, ...).\n *\n * The distinction with `privateKeyProvider` matters: a\n * `privateKeyProvider` *sources raw key material* from elsewhere and\n * hands it back; `keyOperations` never reveals the key — only sign and\n * decrypt operations on it. Prefer `keyOperations` whenever the key\n * does not need to be exportable.\n *\n * When set:\n * - {@link CertificateManager.getPrivateKey} and\n * {@link CertificateManager.reencryptPrivateKey} throw\n * `PrivateKeyUnavailableError` — there is no key material to return\n * or rewrite, and no on-disk key is generated or expected;\n * - {@link CertificateManager.getKeyOperations} returns this object;\n * - `initialize()` fails closed if the provider cannot answer\n * `getKeyMetadata()` (unreachable HSM, misconfiguration);\n * - mutually exclusive with `privateKeyProvider` and\n * `privateKeyPassphrase` — both describe key *material*, which an\n * opaque configuration does not have.\n */\n keyOperations?: IKeyOperations;\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// Characters we keep in the human-readable part of a stored certificate\n// filename: plain ASCII letters, digits, and a few separators. Everything else\n// is dropped. The Common Name is free text supplied in the certificate, so it\n// is only ever decorative here — the fingerprint in brackets is what actually\n// identifies the file — and keeping the label to a small, known set makes the\n// name portable and predictable across file systems.\nconst disallowedInLabel = /[^A-Za-z0-9._ -]/g;\n\n// Exported for tests; not re-exported from the package index (internal helper).\nexport function toFilenameLabel(commonName: string): string {\n // Normalising to NFKC first lets common \"wide\"/decorative letter forms fold\n // to their plain ASCII equivalent, so a legitimate name stays readable\n // instead of collapsing into underscores. Anything left outside the allowed\n // set becomes \"_\", any remaining non-ASCII is dropped, and the length is\n // capped since the fingerprint already guarantees uniqueness.\n const label = (commonName || \"\")\n .normalize(\"NFKC\")\n .replace(disallowedInLabel, \"_\")\n .replace(/[^\\x20-\\x7E]/g, \"\")\n .slice(0, 64)\n .trim();\n return label || \"certificate\";\n}\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 const sanitizedCommonName = toFilenameLabel(commonName);\n return `${sanitizedCommonName}[${fingerprint}]`;\n } catch (_err) {\n // make be certificate is incorrect !\n return `invalid_certificate_[${fingerprint}]`;\n }\n}\n\n// Defense-in-depth for the store layout: a certificate filename is always\n// `<label>.pem` with no path separators — `toFilenameLabel` guarantees this. This\n// helper joins a store folder with such a filename and verifies the *resolved*\n// path stays inside that folder, so a future sanitizer regression, a new call\n// site, or an unforeseen encoding can never write a certificate outside its\n// store. It fails closed (throws) rather than silently writing elsewhere.\n// Exported for tests; not re-exported from the package index (internal helper).\nexport function safeStoreJoin(folder: string, filename: string): string {\n const full = path.resolve(folder, filename);\n const base = path.resolve(folder);\n if (full !== base && !full.startsWith(base + path.sep)) {\n warningLog(\"refusing to write certificate outside its store folder:\", filename);\n throw new Error(`certificate filename escapes its store folder: ${filename}`);\n }\n return full;\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 static #exitHandler: (() => void) | undefined;\n\n /**\n * Install a best-effort `exit` hook that closes any watcher\n * still open when the process terminates.\n *\n * **This library never terminates the host process.** No\n * SIGINT/SIGTERM handler is installed: deciding how (and\n * whether) to shut down on a signal is the application's\n * responsibility, and a listener registered here would both\n * pre-empt the application's own graceful shutdown and\n * silently suppress Node's default signal behaviour.\n *\n * Nothing here is load-bearing for process exit. The native\n * `fs.watch` handles are `unref()`'d when the watchers are\n * created (see `#readCertificates`), so an undisposed\n * CertificateManager never keeps the event loop alive. This\n * hook is only tidiness on the way out.\n *\n * `exit` rather than `beforeExit`: `beforeExit` fires when\n * the loop merely drains and the loop can subsequently be\n * resurrected, which would leave a still-in-use instance\n * marked Disposed. `exit` is terminal, synchronous-only and\n * cannot alter the exit code.\n */\n static #installExitCleanup(): 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 CertificateManager.#exitHandler = closeDanglingWatchers;\n process.on(\"exit\", closeDanglingWatchers);\n }\n\n /**\n * Remove the `exit` hook once the last instance is disposed,\n * so a library that is initialized and disposed repeatedly\n * does not accumulate process listeners. A later\n * `initialize()` re-arms it.\n */\n static #uninstallExitCleanupIfIdle(): void {\n if (CertificateManager.#activeInstances.size > 0) return;\n if (CertificateManager.#exitHandler) {\n process.removeListener(\"exit\", CertificateManager.#exitHandler);\n CertificateManager.#exitHandler = undefined;\n }\n CertificateManager.#cleanupInstalled = false;\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 readonly #privateKeyPassphrase?: PrivateKeyPassphrase;\n readonly #privateKeyProvider?: PrivateKeyProvider;\n readonly #keyOperations?: IKeyOperations;\n /** The stable lazy wrap handed out by {@link getKeyOperations} for non-opaque configurations. */\n #lazyLocalKeyOperations?: IKeyOperations;\n /**\n * The on-disk key, decrypted once and kept for the instance's lifetime,\n * so the passphrase (or its resolver function) is consulted at most\n * once. Cleared by `dispose()`. Not used when a provider is configured:\n * the provider is the authority on the current key.\n */\n #cachedPrivateKey?: PrivateKey;\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 this.#privateKeyPassphrase = options.privateKeyPassphrase;\n this.#privateKeyProvider = options.privateKeyProvider;\n this.#keyOperations = options.keyOperations;\n if (this.#keyOperations && this.#privateKeyProvider) {\n throw new Error(\n \"CertificateManager: 'keyOperations' and 'privateKeyProvider' are mutually exclusive:\" +\n \" one hides the key, the other sources its material\"\n );\n }\n if (this.#keyOperations && this.#privateKeyPassphrase) {\n throw new Error(\n \"CertificateManager: 'privateKeyPassphrase' is meaningless with 'keyOperations':\" +\n \" there is no key material for a passphrase to protect\"\n );\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 /**\n * Path to the private key file (`own/private/private_key.pem`).\n *\n * Kept for backward compatibility with code that reads the key\n * directly from disk. When a passphrase or a `privateKeyProvider` is\n * configured, prefer {@link getPrivateKey} instead — this getter still\n * returns the on-disk path even if a provider is configured (there may\n * be no meaningful file in that case).\n */\n get privateKey() {\n return path.join(this.rootDir, \"own/private/private_key.pem\");\n }\n\n /**\n * Resolve the private key: from `privateKeyProvider` if configured,\n * otherwise from disk (decrypting with `privateKeyPassphrase` if the\n * key is encrypted). Fails closed — throws\n * `PrivateKeyPassphraseRequiredError` — if the on-disk key is encrypted\n * and no passphrase is configured, or if the wrong passphrase is\n * configured.\n *\n * The on-disk key is read and decrypted once and then cached for the\n * lifetime of this instance, so a `privateKeyPassphrase` function is\n * called at most once (concurrent first calls share the same read). A\n * failed read is not cached, so a caller can fix the passphrase and\n * retry. A `privateKeyProvider` is consulted on every call: it is the\n * authority on what the current key is.\n */\n public async getPrivateKey(): Promise<PrivateKey> {\n if (this.#keyOperations) {\n throw new PrivateKeyUnavailableError(\n \"CertificateManager.getPrivateKey is not available when keyOperations is configured:\" +\n \" the private key is held by the key-operations provider (HSM/KMS) and cannot be read\" +\n \" — use getKeyOperations() instead\"\n );\n }\n if (this.#privateKeyProvider) {\n return await this.#privateKeyProvider.getPrivateKey();\n }\n if (this.#cachedPrivateKey) {\n return this.#cachedPrivateKey;\n }\n if (!this.#privateKeyPromise) {\n this.#privateKeyPromise = (async () => {\n const passphrase = await resolvePrivateKeyPassphrase(this.#privateKeyPassphrase);\n return readPrivateKey(this.privateKey, passphrase);\n })().then(\n (key) => {\n this.#cachedPrivateKey = key;\n return key;\n },\n (err) => {\n this.#privateKeyPromise = undefined; // not cached: allow a retry\n throw err;\n }\n );\n }\n return await this.#privateKeyPromise;\n }\n /** In-flight first read of the on-disk key, so concurrent callers share one passphrase resolution. */\n #privateKeyPromise?: Promise<PrivateKey>;\n\n /**\n * True when this manager's key is opaque — configured through\n * `keyOperations`, held by an HSM/KMS, never obtainable as material.\n * When true, {@link getPrivateKey} throws `PrivateKeyUnavailableError`\n * and {@link getKeyOperations} is the only way to use the key.\n */\n public isPrivateKeyOpaque(): boolean {\n return !!this.#keyOperations;\n }\n\n /**\n * The key as an opaque {@link IKeyOperations} — the recommended way to\n * *use* the private key regardless of where it lives.\n *\n * Returns the configured `keyOperations` object when the key is opaque.\n * Otherwise returns a stable lazy wrap over {@link getPrivateKey}: its\n * methods resolve the key on first use (disk read, passphrase,\n * `privateKeyProvider` — all async), so the wrap offers no synchronous\n * fast path; callers that need one resolve the key themselves and build\n * a `LocalKeyOperations` over it. The wrap follows key rotation: a\n * `privateKeyProvider` that starts returning a different key gets a\n * fresh underlying `LocalKeyOperations`.\n */\n public getKeyOperations(): IKeyOperations {\n if (this.#keyOperations) {\n return this.#keyOperations;\n }\n if (!this.#lazyLocalKeyOperations) {\n let cached: { key: PrivateKey; ops: LocalKeyOperations } | undefined;\n const resolve = async (): Promise<LocalKeyOperations> => {\n const key = await this.getPrivateKey();\n // keyed by identity: the disk path always returns the cached envelope,\n // while a provider that rotates the key yields a fresh wrap\n if (!cached || cached.key !== key) {\n cached = { key, ops: keyOperationsFromPrivateKey(key) };\n }\n return cached.ops;\n };\n this.#lazyLocalKeyOperations = {\n sign: async (data, params) => (await resolve()).sign(data, params),\n decryptBlock: async (block, params) => (await resolve()).decryptBlock(block, params),\n getKeyMetadata: async () => (await resolve()).getKeyMetadata(),\n getPublicKey: async () => (await resolve()).getPublicKey()\n };\n }\n return this.#lazyLocalKeyOperations;\n }\n\n /**\n * Fail closed at `initialize()` time, not on the first certificate\n * operation, if the key cannot actually be used: wrong/missing\n * passphrase on an encrypted key, a broken `privateKeyProvider`, or an\n * unreachable `keyOperations` provider (probed via `getKeyMetadata`).\n */\n async #probePrivateKey(): Promise<void> {\n if (this.#keyOperations) {\n await this.#keyOperations.getKeyMetadata();\n return;\n }\n await this.getPrivateKey();\n }\n\n /**\n * The key to hand to a certificate-issuance primitive: the raw\n * {@link PrivateKey} for a local configuration, or a {@link CaSigner}\n * adapted from `keyOperations` when the key is opaque — which requires\n * the provider to implement `getPublicKey` (the adapter says so if not).\n */\n async #resolveSigningKey(): Promise<PrivateKey | CaSigner> {\n if (this.#keyOperations) {\n return caSignerFromKeyOperations(this.#keyOperations);\n }\n return await this.getPrivateKey();\n }\n\n /**\n * Enable, disable, or rotate the passphrase protecting the on-disk\n * private key: decrypt with `oldPassphrase` (omit if the key is\n * currently unencrypted), then write back encrypted with\n * `newPassphrase` (omit to leave it unencrypted). The write goes to a\n * temporary file in the same directory and is atomically renamed into\n * place, so a crash mid-rotation cannot leave a partially-written key;\n * the temporary file is removed if anything fails, so a rotation *to*\n * plaintext can never leave a stray cleartext copy behind. Runs under\n * the same lock as `initialize()`.\n *\n * This only rewrites the on-disk file — it does not update this\n * instance's own `privateKeyPassphrase` (set at construction), and it\n * drops this instance's cached key so that disk stays the source of\n * truth. Construct a new `CertificateManager` with the new passphrase to\n * continue using it afterward.\n *\n * Not supported when a `privateKeyProvider` is configured (there is no\n * disk file for this method to rewrite).\n */\n public async reencryptPrivateKey(oldPassphrase?: PrivateKeyPassphrase, newPassphrase?: PrivateKeyPassphrase): Promise<void> {\n if (this.#keyOperations) {\n throw new PrivateKeyUnavailableError(\n \"reencryptPrivateKey: not supported when keyOperations is configured — there is no key material to rewrite\"\n );\n }\n if (this.#privateKeyProvider) {\n throw new Error(\"reencryptPrivateKey: not supported when a privateKeyProvider is configured\");\n }\n const oldPass = await resolvePrivateKeyPassphrase(oldPassphrase);\n const newPass = await resolvePrivateKeyPassphrase(newPassphrase);\n await this.withLock2(async () => {\n const privateKey = readPrivateKey(this.privateKey, oldPass);\n await this.#rewritePrivateKeyFile(privateKey, newPass);\n });\n this.#cachedPrivateKey = undefined;\n this.#privateKeyPromise = undefined;\n }\n\n /**\n * Atomically replace the on-disk private key with `privateKey`, written\n * as PKCS#8 (encrypted with `passphrase` if given). Temp file next to the\n * target, `0600`, renamed into place; the temp file is unlinked on any\n * failure so no partial or cleartext copy can be left behind.\n * Caller must hold the lock.\n */\n async #rewritePrivateKeyFile(privateKey: PrivateKey, passphrase: string | undefined): Promise<void> {\n const tmpFilename = `${this.privateKey}.${process.pid}-${Date.now()}.tmp`;\n try {\n await writePrivateKeyFile(tmpFilename, privateKey, { passphrase });\n await fs.promises.rename(tmpFilename, this.privateKey);\n } finally {\n await fs.promises.rm(tmpFilename, { force: true });\n }\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 = safeStoreJoin(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 // Has SoftwareCertificate passed its issue date and has it not expired ?\n // check dates\n //\n // exploreCertificate rather than exploreCertificateInfo: only the two\n // dates are wanted, and exploreCertificateInfo additionally insists on\n // an RSA-sized public key. It threw on an EC certificate, which the\n // caller then saw as BadCertificateInvalid - a verdict about the key\n // type, reported as if the certificate were malformed. An issuer\n // parsed the same way, purely to be written to the debug log, made\n // that fatal for any chain with an EC issuer in it.\n const { validity } = exploreCertificate(chain[0]).tbsCertificate;\n const now = new Date();\n\n let isTimeInvalid = false;\n // check that certificate is active\n if (validity.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 =${validity.notBefore}`\n );\n if (!options.acceptPendingCertificate) {\n isTimeInvalid = true;\n }\n }\n\n // check that certificate has not expired\n if (validity.notAfter.getTime() <= now.getTime()) {\n // certificate is obsolete\n debugLog(`${chalk.red(\"certificate is invalid : certificate has expired !\")} not after date =${validity.notAfter}`);\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 // exploreCertificate throws if the DER element is not a\n // valid X.509 certificate (e.g. it is a CRL or some other\n // ASN.1 structure), which is the only question being asked\n // here. exploreCertificateInfo used to stand in for it, but\n // it also insists on an RSA-sized public key, so every chain\n // containing an EC certificate was reported invalid on\n // structural grounds it never actually failed.\n exploreCertificate(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 try {\n await this.#initializingPromise;\n } catch (err) {\n // Fail closed but not stuck: a failed initialize() (typically a\n // wrong or missing private-key passphrase) must leave the\n // instance re-initializable, not parked in Initializing where a\n // retry would silently no-op with empty trust indexes.\n this.#initializingPromise = undefined;\n this.state = CertificateManagerState.Uninitialized;\n throw err;\n }\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.#installExitCleanup();\n }\n\n async #initialize(): Promise<void> {\n const pkiDir = this.#location;\n mkdirRecursiveSync(pkiDir);\n mkdirRecursiveSync(path.join(pkiDir, \"own\"));\n mkdirRecursiveSync(path.join(pkiDir, \"own/certs\"));\n ensurePrivateDirectory(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 // when a privateKeyProvider or keyOperations is configured it overrides disk\n // entirely, so there is no on-disk key to generate, encrypt, or check for existence\n const ownsDiskKey = !this.#privateKeyProvider && !this.#keyOperations;\n const needsKeyGeneration = ownsDiskKey && !fs.existsSync(this.privateKey);\n // Secure by default: a passphrase configured on an install whose key\n // is still plaintext means \"protect this key\", not \"ignore me\". Node's\n // createPrivateKey would silently accept the plaintext key with any\n // passphrase, so detect it here and encrypt in place.\n const needsKeyEncryption =\n ownsDiskKey &&\n !needsKeyGeneration &&\n this.#privateKeyPassphrase !== undefined &&\n !isEncryptedPrivateKeyFile(this.privateKey);\n\n if (!fs.existsSync(this.configFile) || needsKeyGeneration || needsKeyEncryption) {\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 (ownsDiskKey && !fs.existsSync(this.privateKey)) {\n debugLog(\"generating private key ...\");\n // setEnv(\"RANDFILE\", this.randomFile);\n const passphrase = await resolvePrivateKeyPassphrase(this.#privateKeyPassphrase);\n await generatePrivateKeyFile(this.privateKey, this.keySize, { passphrase });\n // seed the cache from the passphrase we already resolved,\n // so a passphrase function is not called a second time below\n this.#cachedPrivateKey = readPrivateKey(this.privateKey, passphrase);\n } else if (ownsDiskKey && this.#privateKeyPassphrase !== undefined && !isEncryptedPrivateKeyFile(this.privateKey)) {\n // (re-checked under the lock: another instance may have done it first)\n warningLog(\"initialize: private key is plaintext but a passphrase is configured; encrypting it in place\");\n const passphrase = await resolvePrivateKeyPassphrase(this.#privateKeyPassphrase);\n const plaintextKey = readPrivateKey(this.privateKey);\n await this.#rewritePrivateKeyFile(plaintextKey, passphrase);\n this.#cachedPrivateKey = plaintextKey;\n }\n if (ownsDiskKey) {\n // repair permissions on installs created before this hardening,\n // and confirm them on a freshly generated key\n restrictPrivateFilePermissions(this.privateKey, 0o600);\n }\n await this.#probePrivateKey();\n await this.#readCertificates();\n });\n } else {\n if (ownsDiskKey) {\n restrictPrivateFilePermissions(this.privateKey, 0o600);\n }\n await this.#probePrivateKey();\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 this.#cachedPrivateKey = undefined;\n this.#privateKeyPromise = undefined;\n CertificateManager.#activeInstances.delete(this);\n CertificateManager.#uninstallExitCleanupIfIdle();\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 (!this.#privateKeyProvider && !this.#keyOperations && !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 // a copy, not an in-place mutation: the caller's object must never\n // end up holding the resolved private key material\n const _params: CreateSelfSignCertificateWithConfigParam = {\n ...(params as unknown as CreateSelfSignCertificateWithConfigParam),\n rootDir: this.rootDir,\n configFile: this.configFile,\n privateKey: await this.#resolveSigningKey(),\n subject: params.subject || \"CN=FIXME\"\n };\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 if (Object.prototype.hasOwnProperty.call(params, \"rootDir\")) {\n throw new Error(\"rootDir should not be specified \");\n }\n // a copy, not an in-place mutation: the caller's object must never\n // end up holding the resolved private key material\n const _params: CreateSelfSignCertificateWithConfigParam = {\n ...(params as CreateSelfSignCertificateWithConfigParam),\n rootDir: path.resolve(this.rootDir),\n configFile: path.resolve(this.configFile),\n privateKey: await this.#resolveSigningKey()\n };\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 = safeStoreJoin(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 // currentInfo is kept in lockstep with currentCert, so the\n // dates are already parsed. Re-reading them through\n // exploreCertificateInfo cost a second parse and rejected any\n // EC certificate outright, since that helper also insists on\n // an RSA-sized public key.\n const { validity } = currentInfo.tbsCertificate;\n const now = new Date();\n if (validity.notBefore.getTime() > now.getTime()) {\n return VerificationStatus.BadCertificateTimeInvalid;\n }\n if (validity.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 = safeStoreJoin(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 = safeStoreJoin(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 // depth: 0\n // PKI store folders are flat — certificates and CRLs sit\n // directly in them. Without an explicit depth chokidar\n // recurses without bound (handler.js: `oDepth == null`)\n // and descends into any subdirectory that appears, which\n // routes through NodeFsHandler._handleRead ->\n // FSWatcher._throttle. That schedules *ref'd* 1s timers;\n // a directory churning under the watcher can spin\n // thousands of them and keep the event loop alive\n // indefinitely, so an undisposed CertificateManager stops\n // the process from exiting. Note this is a timer problem,\n // not a handle problem: the fs.watch handles themselves\n // come back correctly unref'd. Flat watching keeps\n // _handleRead off subdirectories entirely.\n //\n const chokidarOptions = {\n usePolling,\n ...(usePolling ? { interval: pollingInterval } : {}),\n depth: 0,\n persistent: false\n };\n\n // Workaround for two problems with persistent:false:\n //\n // 1. Chokidar does forward persistent:false into fs.watch\n // (handler.js, createFsWatchInstance) and on current\n // Node the resulting handles do come back unref'd —\n // but that was not enough in practice on Windows when\n // 0fbe111 was written, where undisposed instances\n // pinned the loop open. The explicit .unref() is kept\n // as the guarantee rather than trusting the platform.\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 { coercePrivateKeyPem, createCertificateSigningRequest, pemToPrivateKey, Subject } from \"node-opcua-crypto\";\nimport { type CreateCertificateSigningRequestWithConfigOptions, isOpaqueSigner } 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 if (typeof params.privateKey === \"string\") {\n assert(fs.existsSync(params.privateKey), `Private key must exist${params.privateKey}`);\n }\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 // an opaque signer is handed to the CSR primitive as-is: the\n // proof-of-possession signature and the embedded public key both come\n // from the signer, and there is no PEM to coerce\n const privateKey = isOpaqueSigner(params.privateKey)\n ? params.privateKey\n : await pemToPrivateKey(\n typeof params.privateKey === \"string\"\n ? await fs.promises.readFile(params.privateKey, \"utf-8\")\n : coercePrivateKeyPem(params.privateKey)\n );\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(\n `- privateKey ${\n typeof params.privateKey === \"string\"\n ? params.privateKey\n : isOpaqueSigner(params.privateKey)\n ? \"<opaque-signer>\"\n : \"<in-memory>\"\n }`\n );\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 coercePrivateKeyPem,\n createSelfSignedCertificate as createSelfSignedCertificate1,\n pemToPrivateKey,\n Subject\n} from \"node-opcua-crypto\";\nimport { adjustDate, type CreateSelfSignCertificateWithConfigParam, isOpaqueSigner } 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 if (typeof params.privateKey === \"string\") {\n assert(fs.existsSync(params.privateKey));\n }\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 // an opaque signer is handed to the primitive as-is: the public half\n // comes from getPublicKey() and signing routes through the signer\n // (node-opcua-crypto >= 5.10.1); there is no PEM to coerce\n const privateKey = isOpaqueSigner(params.privateKey)\n ? params.privateKey\n : await pemToPrivateKey(\n typeof params.privateKey === \"string\"\n ? await fs.promises.readFile(params.privateKey, \"utf-8\")\n : coercePrivateKeyPem(params.privateKey)\n );\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","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","// ---------------------------------------------------------------------------------------------------------------------\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\n/**\n * PFX (PKCS#12) bundling, implemented in pure JavaScript.\n *\n * These helpers used to shell out to `openssl pkcs12`. They no longer do:\n * every function here goes through `node-opcua-crypto`, so a program that\n * only bundles and unbundles PFX files needs no `openssl` executable on the\n * machine. The exported API is unchanged.\n *\n * Two consequences are worth knowing about:\n *\n * - **What is written.** Bundles are protected with PBES2 / AES-256-CBC\n * under an SHA-256 MAC, which is what openssl 3 produces by default and\n * what openssl 1.x produced only when asked. Both read them back.\n * - **What can be read.** A bundle whose certificate safe uses the 40-bit\n * RC2 encryption that openssl 1.x chose by default cannot be read here.\n * That cipher is broken, and openssl 3 itself refuses it without\n * `-legacy`. {@link readPfxFile} turns the failure into a message saying\n * how to convert the file, rather than leaving an OID to be decoded by\n * hand.\n */\n\nimport assert from \"node:assert\";\nimport fs from \"node:fs\";\n\nimport {\n type Certificate,\n certificateMatchesPrivateKey,\n certificatesToPem,\n coercePrivateKeyPem,\n createPfx,\n makeSHA1Thumbprint,\n type ParsedPfx,\n Pkcs12UnsupportedAlgorithmError,\n parsePfx,\n readCertificateChain,\n readPrivateKey,\n toPem,\n x509\n} from \"node-opcua-crypto\";\n\nimport type { Filename } from \"../toolbox/common\";\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, plaintext or encrypted PKCS#8). */\n privateKeyFile: Filename;\n\n /**\n * Passphrase that decrypts `privateKeyFile` if it is an encrypted\n * PKCS#8 key (e.g. a `CertificateManager` created with\n * `privateKeyPassphrase`). Omit for a plaintext key. This is distinct\n * from `passphrase`, which protects the *output* PFX bundle.\n */\n privateKeyPassphrase?: string;\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 * Optional display label stored on the bundle, the equivalent of\n * openssl's `-name`. The Windows certificate store and `keytool` show\n * it; {@link extractAllFromPFX} and {@link dumpPFX} report it back.\n */\n friendlyName?: string;\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 /** The display label the bundle carries, if it has one. */\n friendlyName?: string;\n}\n\n// ── Reading a bundle ───────────────────────────────────────────\n\n/**\n * Open and decrypt a PFX file, reporting failures in terms of the file the\n * caller named.\n *\n * The one failure worth translating is a bundle encrypted with an algorithm\n * this implementation will not touch. In practice that means openssl 1.x's\n * default certificate-safe cipher, 40-bit RC2: the underlying error names\n * an OID, which tells the reader nothing about what to do next.\n */\nasync function readPfxFile(options: ExtractPFXOptions): Promise<ParsedPfx> {\n const { pfxFile, passphrase = \"\" } = options;\n\n assert(fs.existsSync(pfxFile), `PFX file does not exist: ${pfxFile}`);\n\n try {\n return await parsePfx(await fs.promises.readFile(pfxFile), passphrase);\n } catch (err) {\n if (err instanceof Pkcs12UnsupportedAlgorithmError) {\n const explained = new Error(\n [\n `${pfxFile} uses an encryption algorithm that is not supported (${err.message}).`,\n \"Bundles written by openssl 1.x default to 40-bit RC2, which openssl 3 itself\",\n \"rejects unless given -legacy. Convert the file once with:\",\n \" openssl pkcs12 -legacy -in old.pfx -nodes -out tmp.pem\",\n \" openssl pkcs12 -export -in tmp.pem -out new.pfx\"\n ].join(\"\\n\")\n );\n // assigned rather than passed to the constructor: `lib` is empty\n // in this project, so the ES2022 ErrorOptions overload is not in\n // scope even though every supported Node runtime honours it.\n (explained as Error & { cause?: unknown }).cause = err;\n throw explained;\n }\n throw err;\n }\n}\n\n// ── Create PFX ─────────────────────────────────────────────────\n\n/**\n * Create a PFX (PKCS#12) file from a certificate and private key.\n *\n * The bundle is protected with PBES2 / AES-256-CBC under an SHA-256 MAC. If\n * `certificateFile` holds more than one PEM block, the first is the\n * identity and the rest join the issuer chain, ahead of any\n * `caCertificateFiles`.\n *\n * Nothing is written unless every input reads back cleanly and the private\n * key belongs to the certificate, so a wrong `privateKeyPassphrase` or a\n * mismatched pair leaves no half-made file behind.\n *\n * @param options - see {@link CreatePFXOptions}\n */\nexport async function createPFX(options: CreatePFXOptions): Promise<void> {\n const { certificateFile, privateKeyFile, privateKeyPassphrase, 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 const [certificate, ...bundledIssuers] = readCertificateChain(certificateFile);\n if (!certificate) {\n throw new Error(`Certificate file holds no certificate: ${certificateFile}`);\n }\n\n const certificateChain: Certificate[] = [...bundledIssuers];\n for (const caFile of caCertificateFiles ?? []) {\n assert(fs.existsSync(caFile), `CA certificate file does not exist: ${caFile}`);\n certificateChain.push(...readCertificateChain(caFile));\n }\n\n // throws on an encrypted key with no passphrase - where the openssl\n // version would instead block on a terminal prompt that never came\n const privateKey = readPrivateKey(privateKeyFile, privateKeyPassphrase);\n\n // `openssl pkcs12 -export` refuses a key that does not go with the\n // certificate (\"No certificate matches private key\"); keep that guard,\n // or the result is a bundle nothing can use.\n if (!certificateMatchesPrivateKey(certificate, privateKey)) {\n throw new Error(`The private key ${privateKeyFile} does not match the certificate ${certificateFile}`);\n }\n\n const pfx = await createPfx({\n certificate,\n certificateChain,\n privateKey,\n password: passphrase,\n friendlyName: options.friendlyName\n });\n\n await fs.promises.writeFile(outputFile, pfx);\n}\n\n// ── Extract certificate from PFX ───────────────────────────────\n\n/**\n * Extract the client/server certificate from a PFX file.\n *\n * @returns the certificate in PEM format.\n */\nexport async function extractCertificateFromPFX(options: ExtractPFXOptions): Promise<string> {\n const { certificate } = await readPfxFile(options);\n return toPem(certificate, \"CERTIFICATE\");\n}\n\n// ── Extract private key from PFX ───────────────────────────────\n\n/**\n * Extract the private key from a PFX file.\n *\n * @returns the private key as unencrypted PKCS#8 PEM.\n */\nexport async function extractPrivateKeyFromPFX(options: ExtractPFXOptions): Promise<string> {\n const { privateKey } = await readPfxFile(options);\n return coercePrivateKeyPem(privateKey);\n}\n\n// ── Extract CA certificates from PFX ───────────────────────────\n\n/**\n * Extract the CA / intermediate certificates from a PFX file.\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 { certificateChain } = await readPfxFile(options);\n return certificateChain.length === 0 ? \"\" : certificatesToPem(certificateChain);\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 * Prefer this over the single-part helpers when you want more than one\n * part: the file is read, decrypted and MAC-verified once here, against\n * once per part.\n *\n * @returns an {@link ExtractPFXResult} with all PEM-encoded parts.\n */\nexport async function extractAllFromPFX(options: ExtractPFXOptions): Promise<ExtractPFXResult> {\n const { certificate, certificateChain, privateKey, friendlyName } = await readPfxFile(options);\n return {\n certificate: toPem(certificate, \"CERTIFICATE\"),\n privateKey: coercePrivateKeyPem(privateKey),\n caCertificates: certificateChain.length === 0 ? \"\" : certificatesToPem(certificateChain),\n friendlyName\n };\n}\n\n// ── Convert PFX to PEM (combined) ──────────────────────────────\n\n/**\n * Convert a PFX file to a single PEM file holding the private key, the\n * certificate, and any issuer certificates the bundle carries, in that\n * order.\n */\nexport async function convertPFXtoPEM(pfxFile: Filename, pemFile: Filename, passphrase = \"\"): Promise<void> {\n const { certificate, certificateChain, privateKey } = await readPfxFile({ pfxFile, passphrase });\n\n const parts = [coercePrivateKeyPem(privateKey), toPem(certificate, \"CERTIFICATE\")];\n if (certificateChain.length > 0) {\n parts.push(certificatesToPem(certificateChain));\n }\n await fs.promises.writeFile(pemFile, `${parts.map((part) => part.trimEnd()).join(\"\\n\")}\\n`, \"utf-8\");\n}\n\n// ── Inspect PFX ────────────────────────────────────────────────\n\nfunction formatThumbprint(certificate: Certificate): string {\n return (makeSHA1Thumbprint(certificate).toString(\"hex\").toUpperCase().match(/../g) ?? []).join(\":\");\n}\n\n/**\n * The shape of a WebCrypto algorithm descriptor, spelled out locally: this\n * project compiles with an empty `lib`, so the DOM's `KeyAlgorithm` is not\n * in scope. RSA keys carry a `modulusLength` and EC keys a `namedCurve`;\n * neither is guaranteed, hence both optional.\n */\ninterface AlgorithmDescription {\n name: string;\n modulusLength?: number;\n namedCurve?: string;\n}\n\nfunction describePublicKey(certificate: x509.X509Certificate): string {\n const algorithm = certificate.publicKey.algorithm as AlgorithmDescription;\n if (algorithm.modulusLength) {\n return `${algorithm.name} ${algorithm.modulusLength} bits`;\n }\n if (algorithm.namedCurve) {\n return `${algorithm.name} ${algorithm.namedCurve}`;\n }\n return algorithm.name;\n}\n\nfunction describeCertificate(der: Certificate, indent: string): string[] {\n const certificate = new x509.X509Certificate(der);\n return [\n `${indent}subject : ${certificate.subject}`,\n `${indent}issuer : ${certificate.issuer}`,\n `${indent}serial number : ${certificate.serialNumber}`,\n `${indent}not before : ${certificate.notBefore.toISOString()}`,\n `${indent}not after : ${certificate.notAfter.toISOString()}`,\n `${indent}public key : ${describePublicKey(certificate)}`,\n `${indent}signature : ${(certificate.signatureAlgorithm as unknown as AlgorithmDescription).name}`,\n `${indent}SHA-1 thumbprint : ${formatThumbprint(der)}`\n ];\n}\n\n/**\n * Dump the contents of a PFX file in human-readable form.\n *\n * The layout is this package's own, not `openssl pkcs12 -info`'s: it\n * describes the identity the bundle carries rather than the algorithms it\n * was encrypted with, and it states outright whether the enclosed key\n * matches the enclosed certificate - the question a bundle that will not\n * load is usually being asked. Treat the text as something to read, not to\n * parse; use {@link extractAllFromPFX} to get at the parts.\n *\n * @returns the human-readable dump as a string.\n */\nexport async function dumpPFX(pfxFile: Filename, passphrase = \"\"): Promise<string> {\n const { certificate, certificateChain, privateKey, friendlyName } = await readPfxFile({ pfxFile, passphrase });\n\n const lines = [`PFX bundle : ${pfxFile}`];\n if (friendlyName) {\n lines.push(`friendly name : ${friendlyName}`);\n }\n\n const keyVerdict = certificateMatchesPrivateKey(certificate, privateKey)\n ? \"matches this certificate\"\n : \"DOES NOT match this certificate\";\n\n lines.push(\"\", \"certificate\", ...describeCertificate(certificate, \" \"), ` private key : ${keyVerdict}`);\n\n lines.push(\"\", `issuer chain : ${certificateChain.length} certificate(s)`);\n for (const [index, issuer] of certificateChain.entries()) {\n lines.push(` [${index}]`, ...describeCertificate(issuer, \" \"));\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n"],"mappings":"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","names":["PrivateKeyUnavailableError","fs","path","Subject","child_process","fs","os","byline","chalk","doDebug","opensslFolder","opensslExecPath","opensslPath","execute","chalk","display","child_process","byline","os","fs","chalk","execute","assert","fs","path","path","fs","s","n","path","Subject","fs","fs","path","chalk","assert","fs","Subject","assert","fs","Subject","assert","fs","coercePrivateKeyPem","pemToPrivateKey","Subject","assert","fs","Subject","pemToPrivateKey","coercePrivateKeyPem","fs","VerificationStatus","path","CertificateManagerState","ChainCompletionStatus","chalk","assert","fs","coercePrivateKeyPem","makeSHA1Thumbprint","readCertificateChain","readPrivateKey","toPem"]}
|