node-opcua-pki 6.21.0 → 6.22.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 +115 -11
- package/dist/bin/pki.mjs.map +1 -1
- package/dist/{chunk-2HTNH73K.mjs → chunk-EQ4DGI4M.mjs} +5 -1
- package/dist/chunk-EQ4DGI4M.mjs.map +1 -0
- package/dist/{core-C6faZClF.d.mts → core-BwqSJPHc.d.mts} +19 -10
- package/dist/{core-C6faZClF.d.ts → core-BwqSJPHc.d.ts} +19 -10
- package/dist/core.d.mts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/index.d.mts +48 -4
- package/dist/index.d.ts +48 -4
- package/dist/index.js +115 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-2HTNH73K.mjs.map +0 -1
package/dist/bin/pki.mjs
CHANGED
|
@@ -93,6 +93,9 @@ var init_hostname = __esm({
|
|
|
93
93
|
|
|
94
94
|
// packages/node-opcua-pki/lib/toolbox/common.ts
|
|
95
95
|
import assert2 from "assert";
|
|
96
|
+
function isOpaqueSigner(privateKey) {
|
|
97
|
+
return typeof privateKey !== "string" && typeof privateKey.sign === "function";
|
|
98
|
+
}
|
|
96
99
|
async function resolvePrivateKeyPassphrase(passphrase) {
|
|
97
100
|
if (passphrase === void 0) {
|
|
98
101
|
return void 0;
|
|
@@ -276,8 +279,9 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
276
279
|
assert4(typeof certificateSigningRequestFilename === "string");
|
|
277
280
|
const subject = params.subject ? new Subject(params.subject).toString() : void 0;
|
|
278
281
|
displaySubtitle("- Creating a Certificate Signing Request with subtile");
|
|
279
|
-
const
|
|
280
|
-
|
|
282
|
+
const privateKey = isOpaqueSigner(params.privateKey) ? params.privateKey : await pemToPrivateKey(
|
|
283
|
+
typeof params.privateKey === "string" ? await fs2.promises.readFile(params.privateKey, "utf-8") : coercePrivateKeyPem(params.privateKey)
|
|
284
|
+
);
|
|
281
285
|
const { csr } = await createCertificateSigningRequest({
|
|
282
286
|
privateKey,
|
|
283
287
|
dns: params.dns,
|
|
@@ -287,13 +291,16 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
287
291
|
purpose: params.purpose
|
|
288
292
|
});
|
|
289
293
|
await fs2.promises.writeFile(certificateSigningRequestFilename, csr, "utf-8");
|
|
290
|
-
display(
|
|
294
|
+
display(
|
|
295
|
+
`- privateKey ${typeof params.privateKey === "string" ? params.privateKey : isOpaqueSigner(params.privateKey) ? "<opaque-signer>" : "<in-memory>"}`
|
|
296
|
+
);
|
|
291
297
|
display(`- certificateSigningRequestFilename ${certificateSigningRequestFilename}`);
|
|
292
298
|
}
|
|
293
299
|
var init_create_certificate_signing_request = __esm({
|
|
294
300
|
"packages/node-opcua-pki/lib/toolbox/without_openssl/create_certificate_signing_request.ts"() {
|
|
295
301
|
"use strict";
|
|
296
302
|
init_esm_shims();
|
|
303
|
+
init_common();
|
|
297
304
|
init_display();
|
|
298
305
|
}
|
|
299
306
|
});
|
|
@@ -327,8 +334,9 @@ async function createSelfSignedCertificateAsync(certificate, params) {
|
|
|
327
334
|
subject = subject.toString();
|
|
328
335
|
const purpose = params.purpose;
|
|
329
336
|
displayTitle("Generate a certificate request");
|
|
330
|
-
const
|
|
331
|
-
|
|
337
|
+
const privateKey = isOpaqueSigner(params.privateKey) ? params.privateKey : await pemToPrivateKey2(
|
|
338
|
+
typeof params.privateKey === "string" ? await fs3.promises.readFile(params.privateKey, "utf-8") : coercePrivateKeyPem2(params.privateKey)
|
|
339
|
+
);
|
|
332
340
|
const { cert } = await createSelfSignedCertificate1({
|
|
333
341
|
privateKey,
|
|
334
342
|
notBefore: params.startDate,
|
|
@@ -383,11 +391,14 @@ import { drainPendingLocks, withLock } from "@ster5/global-mutex";
|
|
|
383
391
|
import chalk3 from "chalk";
|
|
384
392
|
import chokidar from "chokidar";
|
|
385
393
|
import {
|
|
394
|
+
caSignerFromKeyOperations,
|
|
386
395
|
exploreCertificate,
|
|
387
396
|
exploreCertificateInfo,
|
|
388
397
|
exploreCertificateRevocationList,
|
|
389
398
|
generatePrivateKeyFile,
|
|
399
|
+
keyOperationsFromPrivateKey,
|
|
390
400
|
makeSHA1Thumbprint,
|
|
401
|
+
PrivateKeyUnavailableError,
|
|
391
402
|
readCertificateChain,
|
|
392
403
|
readCertificateChainAsync,
|
|
393
404
|
readCertificateRevocationList,
|
|
@@ -640,6 +651,9 @@ var init_certificate_manager = __esm({
|
|
|
640
651
|
#disableFileWatchers;
|
|
641
652
|
#privateKeyPassphrase;
|
|
642
653
|
#privateKeyProvider;
|
|
654
|
+
#keyOperations;
|
|
655
|
+
/** The stable lazy wrap handed out by {@link getKeyOperations} for non-opaque configurations. */
|
|
656
|
+
#lazyLocalKeyOperations;
|
|
643
657
|
/**
|
|
644
658
|
* The on-disk key, decrypted once and kept for the instance's lifetime,
|
|
645
659
|
* so the passphrase (or its resolver function) is consulted at most
|
|
@@ -683,6 +697,17 @@ var init_certificate_manager = __esm({
|
|
|
683
697
|
this.#disableFileWatchers = options.disableFileWatchers ?? process.env.OPCUA_PKI_DISABLE_FILE_WATCHERS === "true";
|
|
684
698
|
this.#privateKeyPassphrase = options.privateKeyPassphrase;
|
|
685
699
|
this.#privateKeyProvider = options.privateKeyProvider;
|
|
700
|
+
this.#keyOperations = options.keyOperations;
|
|
701
|
+
if (this.#keyOperations && this.#privateKeyProvider) {
|
|
702
|
+
throw new Error(
|
|
703
|
+
"CertificateManager: 'keyOperations' and 'privateKeyProvider' are mutually exclusive: one hides the key, the other sources its material"
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
if (this.#keyOperations && this.#privateKeyPassphrase) {
|
|
707
|
+
throw new Error(
|
|
708
|
+
"CertificateManager: 'privateKeyPassphrase' is meaningless with 'keyOperations': there is no key material for a passphrase to protect"
|
|
709
|
+
);
|
|
710
|
+
}
|
|
686
711
|
mkdirRecursiveSync(options.location);
|
|
687
712
|
if (!fs4.existsSync(this.#location)) {
|
|
688
713
|
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
@@ -724,6 +749,11 @@ var init_certificate_manager = __esm({
|
|
|
724
749
|
* authority on what the current key is.
|
|
725
750
|
*/
|
|
726
751
|
async getPrivateKey() {
|
|
752
|
+
if (this.#keyOperations) {
|
|
753
|
+
throw new PrivateKeyUnavailableError(
|
|
754
|
+
"CertificateManager.getPrivateKey is not available when keyOperations is configured: the private key is held by the key-operations provider (HSM/KMS) and cannot be read \u2014 use getKeyOperations() instead"
|
|
755
|
+
);
|
|
756
|
+
}
|
|
727
757
|
if (this.#privateKeyProvider) {
|
|
728
758
|
return await this.#privateKeyProvider.getPrivateKey();
|
|
729
759
|
}
|
|
@@ -749,6 +779,75 @@ var init_certificate_manager = __esm({
|
|
|
749
779
|
}
|
|
750
780
|
/** In-flight first read of the on-disk key, so concurrent callers share one passphrase resolution. */
|
|
751
781
|
#privateKeyPromise;
|
|
782
|
+
/**
|
|
783
|
+
* True when this manager's key is opaque — configured through
|
|
784
|
+
* `keyOperations`, held by an HSM/KMS, never obtainable as material.
|
|
785
|
+
* When true, {@link getPrivateKey} throws `PrivateKeyUnavailableError`
|
|
786
|
+
* and {@link getKeyOperations} is the only way to use the key.
|
|
787
|
+
*/
|
|
788
|
+
isPrivateKeyOpaque() {
|
|
789
|
+
return !!this.#keyOperations;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* The key as an opaque {@link IKeyOperations} — the recommended way to
|
|
793
|
+
* *use* the private key regardless of where it lives.
|
|
794
|
+
*
|
|
795
|
+
* Returns the configured `keyOperations` object when the key is opaque.
|
|
796
|
+
* Otherwise returns a stable lazy wrap over {@link getPrivateKey}: its
|
|
797
|
+
* methods resolve the key on first use (disk read, passphrase,
|
|
798
|
+
* `privateKeyProvider` — all async), so the wrap offers no synchronous
|
|
799
|
+
* fast path; callers that need one resolve the key themselves and build
|
|
800
|
+
* a `LocalKeyOperations` over it. The wrap follows key rotation: a
|
|
801
|
+
* `privateKeyProvider` that starts returning a different key gets a
|
|
802
|
+
* fresh underlying `LocalKeyOperations`.
|
|
803
|
+
*/
|
|
804
|
+
getKeyOperations() {
|
|
805
|
+
if (this.#keyOperations) {
|
|
806
|
+
return this.#keyOperations;
|
|
807
|
+
}
|
|
808
|
+
if (!this.#lazyLocalKeyOperations) {
|
|
809
|
+
let cached;
|
|
810
|
+
const resolve = async () => {
|
|
811
|
+
const key = await this.getPrivateKey();
|
|
812
|
+
if (!cached || cached.key !== key) {
|
|
813
|
+
cached = { key, ops: keyOperationsFromPrivateKey(key) };
|
|
814
|
+
}
|
|
815
|
+
return cached.ops;
|
|
816
|
+
};
|
|
817
|
+
this.#lazyLocalKeyOperations = {
|
|
818
|
+
sign: async (data, params) => (await resolve()).sign(data, params),
|
|
819
|
+
decryptBlock: async (block, params) => (await resolve()).decryptBlock(block, params),
|
|
820
|
+
getKeyMetadata: async () => (await resolve()).getKeyMetadata(),
|
|
821
|
+
getPublicKey: async () => (await resolve()).getPublicKey()
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
return this.#lazyLocalKeyOperations;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Fail closed at `initialize()` time, not on the first certificate
|
|
828
|
+
* operation, if the key cannot actually be used: wrong/missing
|
|
829
|
+
* passphrase on an encrypted key, a broken `privateKeyProvider`, or an
|
|
830
|
+
* unreachable `keyOperations` provider (probed via `getKeyMetadata`).
|
|
831
|
+
*/
|
|
832
|
+
async #probePrivateKey() {
|
|
833
|
+
if (this.#keyOperations) {
|
|
834
|
+
await this.#keyOperations.getKeyMetadata();
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
await this.getPrivateKey();
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* The key to hand to a certificate-issuance primitive: the raw
|
|
841
|
+
* {@link PrivateKey} for a local configuration, or a {@link CaSigner}
|
|
842
|
+
* adapted from `keyOperations` when the key is opaque — which requires
|
|
843
|
+
* the provider to implement `getPublicKey` (the adapter says so if not).
|
|
844
|
+
*/
|
|
845
|
+
async #resolveSigningKey() {
|
|
846
|
+
if (this.#keyOperations) {
|
|
847
|
+
return caSignerFromKeyOperations(this.#keyOperations);
|
|
848
|
+
}
|
|
849
|
+
return await this.getPrivateKey();
|
|
850
|
+
}
|
|
752
851
|
/**
|
|
753
852
|
* Enable, disable, or rotate the passphrase protecting the on-disk
|
|
754
853
|
* private key: decrypt with `oldPassphrase` (omit if the key is
|
|
@@ -770,6 +869,11 @@ var init_certificate_manager = __esm({
|
|
|
770
869
|
* disk file for this method to rewrite).
|
|
771
870
|
*/
|
|
772
871
|
async reencryptPrivateKey(oldPassphrase, newPassphrase) {
|
|
872
|
+
if (this.#keyOperations) {
|
|
873
|
+
throw new PrivateKeyUnavailableError(
|
|
874
|
+
"reencryptPrivateKey: not supported when keyOperations is configured \u2014 there is no key material to rewrite"
|
|
875
|
+
);
|
|
876
|
+
}
|
|
773
877
|
if (this.#privateKeyProvider) {
|
|
774
878
|
throw new Error("reencryptPrivateKey: not supported when a privateKeyProvider is configured");
|
|
775
879
|
}
|
|
@@ -1118,7 +1222,7 @@ var init_certificate_manager = __esm({
|
|
|
1118
1222
|
mkdirRecursiveSync(path2.join(pkiDir, "issuers"));
|
|
1119
1223
|
mkdirRecursiveSync(path2.join(pkiDir, "issuers/certs"));
|
|
1120
1224
|
mkdirRecursiveSync(path2.join(pkiDir, "issuers/crl"));
|
|
1121
|
-
const ownsDiskKey = !this.#privateKeyProvider;
|
|
1225
|
+
const ownsDiskKey = !this.#privateKeyProvider && !this.#keyOperations;
|
|
1122
1226
|
const needsKeyGeneration = ownsDiskKey && !fs4.existsSync(this.privateKey);
|
|
1123
1227
|
const needsKeyEncryption = ownsDiskKey && !needsKeyGeneration && this.#privateKeyPassphrase !== void 0 && !isEncryptedPrivateKeyFile(this.privateKey);
|
|
1124
1228
|
if (!fs4.existsSync(this.configFile) || needsKeyGeneration || needsKeyEncryption) {
|
|
@@ -1144,14 +1248,14 @@ var init_certificate_manager = __esm({
|
|
|
1144
1248
|
if (ownsDiskKey) {
|
|
1145
1249
|
restrictPrivateFilePermissions(this.privateKey, 384);
|
|
1146
1250
|
}
|
|
1147
|
-
await this
|
|
1251
|
+
await this.#probePrivateKey();
|
|
1148
1252
|
await this.#readCertificates();
|
|
1149
1253
|
});
|
|
1150
1254
|
} else {
|
|
1151
1255
|
if (ownsDiskKey) {
|
|
1152
1256
|
restrictPrivateFilePermissions(this.privateKey, 384);
|
|
1153
1257
|
}
|
|
1154
|
-
await this
|
|
1258
|
+
await this.#probePrivateKey();
|
|
1155
1259
|
await this.#readCertificates();
|
|
1156
1260
|
}
|
|
1157
1261
|
}
|
|
@@ -1236,7 +1340,7 @@ var init_certificate_manager = __esm({
|
|
|
1236
1340
|
if (typeof params.applicationUri !== "string") {
|
|
1237
1341
|
throw new Error("createSelfSignedCertificate: expecting applicationUri to be a string");
|
|
1238
1342
|
}
|
|
1239
|
-
if (!this.#privateKeyProvider && !fs4.existsSync(this.privateKey)) {
|
|
1343
|
+
if (!this.#privateKeyProvider && !this.#keyOperations && !fs4.existsSync(this.privateKey)) {
|
|
1240
1344
|
throw new Error(`Cannot find private key ${this.privateKey}`);
|
|
1241
1345
|
}
|
|
1242
1346
|
let certificateFilename = path2.join(this.rootDir, "own/certs/self_signed_certificate.pem");
|
|
@@ -1245,7 +1349,7 @@ var init_certificate_manager = __esm({
|
|
|
1245
1349
|
...params,
|
|
1246
1350
|
rootDir: this.rootDir,
|
|
1247
1351
|
configFile: this.configFile,
|
|
1248
|
-
privateKey: await this
|
|
1352
|
+
privateKey: await this.#resolveSigningKey(),
|
|
1249
1353
|
subject: params.subject || "CN=FIXME"
|
|
1250
1354
|
};
|
|
1251
1355
|
await this.withLock2(async () => {
|
|
@@ -1273,7 +1377,7 @@ var init_certificate_manager = __esm({
|
|
|
1273
1377
|
...params,
|
|
1274
1378
|
rootDir: path2.resolve(this.rootDir),
|
|
1275
1379
|
configFile: path2.resolve(this.configFile),
|
|
1276
|
-
privateKey: await this
|
|
1380
|
+
privateKey: await this.#resolveSigningKey()
|
|
1277
1381
|
};
|
|
1278
1382
|
return await this.withLock2(async () => {
|
|
1279
1383
|
const now = /* @__PURE__ */ new Date();
|