node-opcua-pki 6.12.2 → 6.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/pki.mjs +93 -8
- package/dist/bin/pki.mjs.map +1 -1
- package/dist/index.d.mts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.js +94 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -808,15 +808,16 @@ nsComment = "CA Generated by Node-OPCUA Certificate utility usin
|
|
|
808
808
|
subjectKeyIdentifier = hash
|
|
809
809
|
basicConstraints = CA:TRUE
|
|
810
810
|
keyUsage = critical, cRLSign, keyCertSign
|
|
811
|
+
subjectAltName = $ENV::ALTNAME
|
|
811
812
|
nsComment = "CA CSR generated by Node-OPCUA Certificate utility using openssl"
|
|
812
813
|
[ v3_ca ]
|
|
813
814
|
subjectKeyIdentifier = hash
|
|
814
815
|
authorityKeyIdentifier = keyid:always,issuer:always
|
|
815
816
|
basicConstraints = CA:TRUE
|
|
816
817
|
keyUsage = critical, cRLSign, keyCertSign
|
|
818
|
+
subjectAltName = $ENV::ALTNAME
|
|
817
819
|
nsComment = "CA Certificate generated by Node-OPCUA Certificate utility using openssl"
|
|
818
820
|
#nsCertType = sslCA, emailCA
|
|
819
|
-
#subjectAltName = email:copy
|
|
820
821
|
#issuerAltName = issuer:copy
|
|
821
822
|
#obj = DER:02:03
|
|
822
823
|
crlDistributionPoints = @crl_info
|
|
@@ -903,7 +904,8 @@ async function construct_CertificateAuthority(certificateAuthority) {
|
|
|
903
904
|
await fs7.promises.writeFile(caConfigFile, data);
|
|
904
905
|
}
|
|
905
906
|
const subjectOpt = ` -subj "${subject.toString()}" `;
|
|
906
|
-
|
|
907
|
+
const caCommonName = subject.commonName || "NodeOPCUA-CA";
|
|
908
|
+
setEnv("ALTNAME", `URI:urn:${caCommonName}`);
|
|
907
909
|
const options = { cwd: caRootDir };
|
|
908
910
|
const configFile = generateStaticConfig("conf/caconfig.cnf", options);
|
|
909
911
|
const configOption = ` -config ${q3(n4(configFile))}`;
|
|
@@ -2022,6 +2024,14 @@ var CertificateManagerState = /* @__PURE__ */ ((CertificateManagerState2) => {
|
|
|
2022
2024
|
CertificateManagerState2[CertificateManagerState2["Disposed"] = 4] = "Disposed";
|
|
2023
2025
|
return CertificateManagerState2;
|
|
2024
2026
|
})(CertificateManagerState || {});
|
|
2027
|
+
var ChainCompletionStatus = /* @__PURE__ */ ((ChainCompletionStatus2) => {
|
|
2028
|
+
ChainCompletionStatus2["AlreadyComplete"] = "AlreadyComplete";
|
|
2029
|
+
ChainCompletionStatus2["ChainCompleted"] = "ChainCompleted";
|
|
2030
|
+
ChainCompletionStatus2["IssuerNotFound"] = "IssuerNotFound";
|
|
2031
|
+
ChainCompletionStatus2["EmptyChain"] = "EmptyChain";
|
|
2032
|
+
ChainCompletionStatus2["MaxDepthReached"] = "MaxDepthReached";
|
|
2033
|
+
return ChainCompletionStatus2;
|
|
2034
|
+
})(ChainCompletionStatus || {});
|
|
2025
2035
|
var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
2026
2036
|
// ── Global instance registry ─────────────────────────────────
|
|
2027
2037
|
// Tracks all initialized CertificateManager instances so their
|
|
@@ -2114,6 +2124,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2114
2124
|
#filenameToHash = /* @__PURE__ */ new Map();
|
|
2115
2125
|
#initializingPromise;
|
|
2116
2126
|
#addCertValidation;
|
|
2127
|
+
#disableFileWatchers;
|
|
2117
2128
|
#thumbs = {
|
|
2118
2129
|
rejected: /* @__PURE__ */ new Map(),
|
|
2119
2130
|
trusted: /* @__PURE__ */ new Map(),
|
|
@@ -2147,6 +2158,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
2147
2158
|
ignoreMissingRevocationList: v.ignoreMissingRevocationList ?? false,
|
|
2148
2159
|
maxChainLength: v.maxChainLength ?? 5
|
|
2149
2160
|
};
|
|
2161
|
+
this.#disableFileWatchers = options.disableFileWatchers ?? process.env.OPCUA_PKI_DISABLE_FILE_WATCHERS === "true";
|
|
2150
2162
|
mkdirRecursiveSync(options.location);
|
|
2151
2163
|
if (!fs10.existsSync(this.#location)) {
|
|
2152
2164
|
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
@@ -3011,6 +3023,75 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
3011
3023
|
}
|
|
3012
3024
|
return selectedTrustedCertificates.length > 0 ? selectedTrustedCertificates[0].certificate : null;
|
|
3013
3025
|
}
|
|
3026
|
+
/**
|
|
3027
|
+
* Outcome status for {@link CertificateManager.completeCertificateChain}.
|
|
3028
|
+
*/
|
|
3029
|
+
static ChainCompletionStatus = ChainCompletionStatus;
|
|
3030
|
+
/**
|
|
3031
|
+
* Complete a certificate chain by walking the issuer store.
|
|
3032
|
+
*
|
|
3033
|
+
* Starting from the last certificate in the provided chain, this method
|
|
3034
|
+
* repeatedly calls {@link findIssuerCertificate} to locate the parent
|
|
3035
|
+
* certificate until it reaches a self-signed root or can no longer find
|
|
3036
|
+
* an issuer.
|
|
3037
|
+
*
|
|
3038
|
+
* @param chain - the (potentially partial) certificate chain, leaf first
|
|
3039
|
+
* @param maxDepth - maximum number of issuers to append (default: 10)
|
|
3040
|
+
* @returns a {@link ChainCompletionResult} containing the (possibly completed)
|
|
3041
|
+
* chain, a status code, and an optional diagnostic message.
|
|
3042
|
+
*/
|
|
3043
|
+
async completeCertificateChain(chain, maxDepth = 10) {
|
|
3044
|
+
if (chain.length === 0) {
|
|
3045
|
+
return {
|
|
3046
|
+
chain,
|
|
3047
|
+
status: "EmptyChain" /* EmptyChain */,
|
|
3048
|
+
message: "Input chain is empty \u2014 nothing to complete."
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
await this.#scanCertFolder(this.issuersCertFolder, this.#thumbs.issuers.certs);
|
|
3052
|
+
const result = [...chain];
|
|
3053
|
+
let depth = 0;
|
|
3054
|
+
while (depth < maxDepth) {
|
|
3055
|
+
const lastCert = result[result.length - 1];
|
|
3056
|
+
const lastInfo = exploreCertificate2(lastCert);
|
|
3057
|
+
if (isSelfSigned2(lastInfo)) {
|
|
3058
|
+
const wasExtended = result.length > chain.length;
|
|
3059
|
+
return {
|
|
3060
|
+
chain: result,
|
|
3061
|
+
status: wasExtended ? "ChainCompleted" /* ChainCompleted */ : "AlreadyComplete" /* AlreadyComplete */,
|
|
3062
|
+
message: wasExtended ? `Chain completed: ${result.length - chain.length} issuer(s) appended, ending at self-signed root "${lastInfo.tbsCertificate.subject.commonName}".` : `Chain is already complete (self-signed root "${lastInfo.tbsCertificate.subject.commonName}").`
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
const issuerCert = await this.findIssuerCertificate(lastCert);
|
|
3066
|
+
if (!issuerCert) {
|
|
3067
|
+
const cn = lastInfo.tbsCertificate.subject.commonName ?? "?";
|
|
3068
|
+
const akid = lastInfo.tbsCertificate.extensions?.authorityKeyIdentifier?.keyIdentifier ?? "?";
|
|
3069
|
+
const msg = `Cannot find issuer for "${cn}" (authorityKeyIdentifier: ${akid}). Ensure the CA certificate is present in the issuers/certs folder.`;
|
|
3070
|
+
warningLog(`completeCertificateChain: ${msg}`);
|
|
3071
|
+
return {
|
|
3072
|
+
chain: result,
|
|
3073
|
+
status: "IssuerNotFound" /* IssuerNotFound */,
|
|
3074
|
+
message: msg
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
const issuerFingerprint = makeFingerprint(issuerCert);
|
|
3078
|
+
const alreadyInChain = result.some((c) => makeFingerprint(c) === issuerFingerprint);
|
|
3079
|
+
if (alreadyInChain) {
|
|
3080
|
+
return {
|
|
3081
|
+
chain: result,
|
|
3082
|
+
status: "AlreadyComplete" /* AlreadyComplete */,
|
|
3083
|
+
message: `Chain ends at root "${exploreCertificate2(issuerCert).tbsCertificate.subject.commonName}" (already present in chain).`
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
result.push(issuerCert);
|
|
3087
|
+
depth++;
|
|
3088
|
+
}
|
|
3089
|
+
return {
|
|
3090
|
+
chain: result,
|
|
3091
|
+
status: "MaxDepthReached" /* MaxDepthReached */,
|
|
3092
|
+
message: `Chain completion stopped after ${maxDepth} iterations \u2014 possible circular chain or very deep hierarchy.`
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3014
3095
|
/**
|
|
3015
3096
|
*
|
|
3016
3097
|
* check if the certificate explicitly appear in the trust list, the reject list or none.
|
|
@@ -3206,11 +3287,15 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
3206
3287
|
this.#scanCrlFolder(this.crlFolder, this.#thumbs.crl),
|
|
3207
3288
|
this.#scanCrlFolder(this.issuersCrlFolder, this.#thumbs.issuersCrl)
|
|
3208
3289
|
]);
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3290
|
+
if (this.#disableFileWatchers) {
|
|
3291
|
+
fs10.watch = origWatch;
|
|
3292
|
+
} else {
|
|
3293
|
+
this.#startWatcher(this.trustedFolder, this.#thumbs.trusted, createUnreffedWatcher, "trusted");
|
|
3294
|
+
this.#startWatcher(this.issuersCertFolder, this.#thumbs.issuers.certs, createUnreffedWatcher, "issuersCerts");
|
|
3295
|
+
this.#startWatcher(this.rejectedFolder, this.#thumbs.rejected, createUnreffedWatcher, "rejected");
|
|
3296
|
+
this.#startCrlWatcher(this.crlFolder, this.#thumbs.crl, createUnreffedWatcher, "crl");
|
|
3297
|
+
this.#startCrlWatcher(this.issuersCrlFolder, this.#thumbs.issuersCrl, createUnreffedWatcher, "issuersCrl");
|
|
3298
|
+
}
|
|
3214
3299
|
}
|
|
3215
3300
|
/**
|
|
3216
3301
|
* Scan a certificate folder and populate the in-memory index.
|
|
@@ -3391,6 +3476,7 @@ export {
|
|
|
3391
3476
|
CertificateAuthority,
|
|
3392
3477
|
CertificateManager,
|
|
3393
3478
|
CertificateManagerState,
|
|
3479
|
+
ChainCompletionStatus,
|
|
3394
3480
|
Subject,
|
|
3395
3481
|
VerificationStatus,
|
|
3396
3482
|
adjustApplicationUri,
|