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/index.mjs
CHANGED
|
@@ -13,16 +13,20 @@ import {
|
|
|
13
13
|
ensurePrivateDirectory,
|
|
14
14
|
g_config,
|
|
15
15
|
isEncryptedPrivateKeyFile,
|
|
16
|
+
isOpaqueSigner,
|
|
16
17
|
makePath,
|
|
17
18
|
mkdirRecursiveSync,
|
|
18
19
|
quote,
|
|
19
20
|
resolvePrivateKeyPassphrase,
|
|
20
21
|
restrictPrivateFilePermissions,
|
|
21
22
|
warningLog
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-EQ4DGI4M.mjs";
|
|
23
24
|
|
|
24
25
|
// packages/node-opcua-pki/lib/index.ts
|
|
25
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
PrivateKeyPassphraseRequiredError,
|
|
28
|
+
PrivateKeyUnavailableError as PrivateKeyUnavailableError2
|
|
29
|
+
} from "node-opcua-crypto";
|
|
26
30
|
|
|
27
31
|
// packages/node-opcua-pki/lib/ca/backends/openssl_ca_backend.ts
|
|
28
32
|
import fs4 from "fs";
|
|
@@ -975,11 +979,14 @@ import { drainPendingLocks, withLock } from "@ster5/global-mutex";
|
|
|
975
979
|
import chalk3 from "chalk";
|
|
976
980
|
import chokidar from "chokidar";
|
|
977
981
|
import {
|
|
982
|
+
caSignerFromKeyOperations,
|
|
978
983
|
exploreCertificate,
|
|
979
984
|
exploreCertificateInfo,
|
|
980
985
|
exploreCertificateRevocationList,
|
|
981
986
|
generatePrivateKeyFile,
|
|
987
|
+
keyOperationsFromPrivateKey,
|
|
982
988
|
makeSHA1Thumbprint,
|
|
989
|
+
PrivateKeyUnavailableError,
|
|
983
990
|
readCertificateChain,
|
|
984
991
|
readCertificateChainAsync,
|
|
985
992
|
readCertificateRevocationList,
|
|
@@ -1006,8 +1013,9 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
1006
1013
|
assert3(typeof certificateSigningRequestFilename === "string");
|
|
1007
1014
|
const subject = params.subject ? new Subject3(params.subject).toString() : void 0;
|
|
1008
1015
|
displaySubtitle("- Creating a Certificate Signing Request with subtile");
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1016
|
+
const privateKey = isOpaqueSigner(params.privateKey) ? params.privateKey : await pemToPrivateKey(
|
|
1017
|
+
typeof params.privateKey === "string" ? await fs5.promises.readFile(params.privateKey, "utf-8") : coercePrivateKeyPem(params.privateKey)
|
|
1018
|
+
);
|
|
1011
1019
|
const { csr } = await createCertificateSigningRequest({
|
|
1012
1020
|
privateKey,
|
|
1013
1021
|
dns: params.dns,
|
|
@@ -1017,7 +1025,9 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
1017
1025
|
purpose: params.purpose
|
|
1018
1026
|
});
|
|
1019
1027
|
await fs5.promises.writeFile(certificateSigningRequestFilename, csr, "utf-8");
|
|
1020
|
-
display(
|
|
1028
|
+
display(
|
|
1029
|
+
`- privateKey ${typeof params.privateKey === "string" ? params.privateKey : isOpaqueSigner(params.privateKey) ? "<opaque-signer>" : "<in-memory>"}`
|
|
1030
|
+
);
|
|
1021
1031
|
display(`- certificateSigningRequestFilename ${certificateSigningRequestFilename}`);
|
|
1022
1032
|
}
|
|
1023
1033
|
|
|
@@ -1050,8 +1060,9 @@ async function createSelfSignedCertificateAsync(certificate, params) {
|
|
|
1050
1060
|
subject = subject.toString();
|
|
1051
1061
|
const purpose = params.purpose;
|
|
1052
1062
|
displayTitle("Generate a certificate request");
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1063
|
+
const privateKey = isOpaqueSigner(params.privateKey) ? params.privateKey : await pemToPrivateKey2(
|
|
1064
|
+
typeof params.privateKey === "string" ? await fs6.promises.readFile(params.privateKey, "utf-8") : coercePrivateKeyPem2(params.privateKey)
|
|
1065
|
+
);
|
|
1055
1066
|
const { cert } = await createSelfSignedCertificate1({
|
|
1056
1067
|
privateKey,
|
|
1057
1068
|
notBefore: params.startDate,
|
|
@@ -1356,6 +1367,9 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1356
1367
|
#disableFileWatchers;
|
|
1357
1368
|
#privateKeyPassphrase;
|
|
1358
1369
|
#privateKeyProvider;
|
|
1370
|
+
#keyOperations;
|
|
1371
|
+
/** The stable lazy wrap handed out by {@link getKeyOperations} for non-opaque configurations. */
|
|
1372
|
+
#lazyLocalKeyOperations;
|
|
1359
1373
|
/**
|
|
1360
1374
|
* The on-disk key, decrypted once and kept for the instance's lifetime,
|
|
1361
1375
|
* so the passphrase (or its resolver function) is consulted at most
|
|
@@ -1399,6 +1413,17 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1399
1413
|
this.#disableFileWatchers = options.disableFileWatchers ?? process.env.OPCUA_PKI_DISABLE_FILE_WATCHERS === "true";
|
|
1400
1414
|
this.#privateKeyPassphrase = options.privateKeyPassphrase;
|
|
1401
1415
|
this.#privateKeyProvider = options.privateKeyProvider;
|
|
1416
|
+
this.#keyOperations = options.keyOperations;
|
|
1417
|
+
if (this.#keyOperations && this.#privateKeyProvider) {
|
|
1418
|
+
throw new Error(
|
|
1419
|
+
"CertificateManager: 'keyOperations' and 'privateKeyProvider' are mutually exclusive: one hides the key, the other sources its material"
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
if (this.#keyOperations && this.#privateKeyPassphrase) {
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
"CertificateManager: 'privateKeyPassphrase' is meaningless with 'keyOperations': there is no key material for a passphrase to protect"
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1402
1427
|
mkdirRecursiveSync(options.location);
|
|
1403
1428
|
if (!fs7.existsSync(this.#location)) {
|
|
1404
1429
|
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
@@ -1440,6 +1465,11 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1440
1465
|
* authority on what the current key is.
|
|
1441
1466
|
*/
|
|
1442
1467
|
async getPrivateKey() {
|
|
1468
|
+
if (this.#keyOperations) {
|
|
1469
|
+
throw new PrivateKeyUnavailableError(
|
|
1470
|
+
"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"
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1443
1473
|
if (this.#privateKeyProvider) {
|
|
1444
1474
|
return await this.#privateKeyProvider.getPrivateKey();
|
|
1445
1475
|
}
|
|
@@ -1465,6 +1495,75 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1465
1495
|
}
|
|
1466
1496
|
/** In-flight first read of the on-disk key, so concurrent callers share one passphrase resolution. */
|
|
1467
1497
|
#privateKeyPromise;
|
|
1498
|
+
/**
|
|
1499
|
+
* True when this manager's key is opaque — configured through
|
|
1500
|
+
* `keyOperations`, held by an HSM/KMS, never obtainable as material.
|
|
1501
|
+
* When true, {@link getPrivateKey} throws `PrivateKeyUnavailableError`
|
|
1502
|
+
* and {@link getKeyOperations} is the only way to use the key.
|
|
1503
|
+
*/
|
|
1504
|
+
isPrivateKeyOpaque() {
|
|
1505
|
+
return !!this.#keyOperations;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* The key as an opaque {@link IKeyOperations} — the recommended way to
|
|
1509
|
+
* *use* the private key regardless of where it lives.
|
|
1510
|
+
*
|
|
1511
|
+
* Returns the configured `keyOperations` object when the key is opaque.
|
|
1512
|
+
* Otherwise returns a stable lazy wrap over {@link getPrivateKey}: its
|
|
1513
|
+
* methods resolve the key on first use (disk read, passphrase,
|
|
1514
|
+
* `privateKeyProvider` — all async), so the wrap offers no synchronous
|
|
1515
|
+
* fast path; callers that need one resolve the key themselves and build
|
|
1516
|
+
* a `LocalKeyOperations` over it. The wrap follows key rotation: a
|
|
1517
|
+
* `privateKeyProvider` that starts returning a different key gets a
|
|
1518
|
+
* fresh underlying `LocalKeyOperations`.
|
|
1519
|
+
*/
|
|
1520
|
+
getKeyOperations() {
|
|
1521
|
+
if (this.#keyOperations) {
|
|
1522
|
+
return this.#keyOperations;
|
|
1523
|
+
}
|
|
1524
|
+
if (!this.#lazyLocalKeyOperations) {
|
|
1525
|
+
let cached;
|
|
1526
|
+
const resolve = async () => {
|
|
1527
|
+
const key = await this.getPrivateKey();
|
|
1528
|
+
if (!cached || cached.key !== key) {
|
|
1529
|
+
cached = { key, ops: keyOperationsFromPrivateKey(key) };
|
|
1530
|
+
}
|
|
1531
|
+
return cached.ops;
|
|
1532
|
+
};
|
|
1533
|
+
this.#lazyLocalKeyOperations = {
|
|
1534
|
+
sign: async (data, params) => (await resolve()).sign(data, params),
|
|
1535
|
+
decryptBlock: async (block, params) => (await resolve()).decryptBlock(block, params),
|
|
1536
|
+
getKeyMetadata: async () => (await resolve()).getKeyMetadata(),
|
|
1537
|
+
getPublicKey: async () => (await resolve()).getPublicKey()
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
return this.#lazyLocalKeyOperations;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Fail closed at `initialize()` time, not on the first certificate
|
|
1544
|
+
* operation, if the key cannot actually be used: wrong/missing
|
|
1545
|
+
* passphrase on an encrypted key, a broken `privateKeyProvider`, or an
|
|
1546
|
+
* unreachable `keyOperations` provider (probed via `getKeyMetadata`).
|
|
1547
|
+
*/
|
|
1548
|
+
async #probePrivateKey() {
|
|
1549
|
+
if (this.#keyOperations) {
|
|
1550
|
+
await this.#keyOperations.getKeyMetadata();
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
await this.getPrivateKey();
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* The key to hand to a certificate-issuance primitive: the raw
|
|
1557
|
+
* {@link PrivateKey} for a local configuration, or a {@link CaSigner}
|
|
1558
|
+
* adapted from `keyOperations` when the key is opaque — which requires
|
|
1559
|
+
* the provider to implement `getPublicKey` (the adapter says so if not).
|
|
1560
|
+
*/
|
|
1561
|
+
async #resolveSigningKey() {
|
|
1562
|
+
if (this.#keyOperations) {
|
|
1563
|
+
return caSignerFromKeyOperations(this.#keyOperations);
|
|
1564
|
+
}
|
|
1565
|
+
return await this.getPrivateKey();
|
|
1566
|
+
}
|
|
1468
1567
|
/**
|
|
1469
1568
|
* Enable, disable, or rotate the passphrase protecting the on-disk
|
|
1470
1569
|
* private key: decrypt with `oldPassphrase` (omit if the key is
|
|
@@ -1486,6 +1585,11 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1486
1585
|
* disk file for this method to rewrite).
|
|
1487
1586
|
*/
|
|
1488
1587
|
async reencryptPrivateKey(oldPassphrase, newPassphrase) {
|
|
1588
|
+
if (this.#keyOperations) {
|
|
1589
|
+
throw new PrivateKeyUnavailableError(
|
|
1590
|
+
"reencryptPrivateKey: not supported when keyOperations is configured \u2014 there is no key material to rewrite"
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1489
1593
|
if (this.#privateKeyProvider) {
|
|
1490
1594
|
throw new Error("reencryptPrivateKey: not supported when a privateKeyProvider is configured");
|
|
1491
1595
|
}
|
|
@@ -1834,7 +1938,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1834
1938
|
mkdirRecursiveSync(path4.join(pkiDir, "issuers"));
|
|
1835
1939
|
mkdirRecursiveSync(path4.join(pkiDir, "issuers/certs"));
|
|
1836
1940
|
mkdirRecursiveSync(path4.join(pkiDir, "issuers/crl"));
|
|
1837
|
-
const ownsDiskKey = !this.#privateKeyProvider;
|
|
1941
|
+
const ownsDiskKey = !this.#privateKeyProvider && !this.#keyOperations;
|
|
1838
1942
|
const needsKeyGeneration = ownsDiskKey && !fs7.existsSync(this.privateKey);
|
|
1839
1943
|
const needsKeyEncryption = ownsDiskKey && !needsKeyGeneration && this.#privateKeyPassphrase !== void 0 && !isEncryptedPrivateKeyFile(this.privateKey);
|
|
1840
1944
|
if (!fs7.existsSync(this.configFile) || needsKeyGeneration || needsKeyEncryption) {
|
|
@@ -1860,14 +1964,14 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1860
1964
|
if (ownsDiskKey) {
|
|
1861
1965
|
restrictPrivateFilePermissions(this.privateKey, 384);
|
|
1862
1966
|
}
|
|
1863
|
-
await this
|
|
1967
|
+
await this.#probePrivateKey();
|
|
1864
1968
|
await this.#readCertificates();
|
|
1865
1969
|
});
|
|
1866
1970
|
} else {
|
|
1867
1971
|
if (ownsDiskKey) {
|
|
1868
1972
|
restrictPrivateFilePermissions(this.privateKey, 384);
|
|
1869
1973
|
}
|
|
1870
|
-
await this
|
|
1974
|
+
await this.#probePrivateKey();
|
|
1871
1975
|
await this.#readCertificates();
|
|
1872
1976
|
}
|
|
1873
1977
|
}
|
|
@@ -1952,7 +2056,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1952
2056
|
if (typeof params.applicationUri !== "string") {
|
|
1953
2057
|
throw new Error("createSelfSignedCertificate: expecting applicationUri to be a string");
|
|
1954
2058
|
}
|
|
1955
|
-
if (!this.#privateKeyProvider && !fs7.existsSync(this.privateKey)) {
|
|
2059
|
+
if (!this.#privateKeyProvider && !this.#keyOperations && !fs7.existsSync(this.privateKey)) {
|
|
1956
2060
|
throw new Error(`Cannot find private key ${this.privateKey}`);
|
|
1957
2061
|
}
|
|
1958
2062
|
let certificateFilename = path4.join(this.rootDir, "own/certs/self_signed_certificate.pem");
|
|
@@ -1961,7 +2065,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1961
2065
|
...params,
|
|
1962
2066
|
rootDir: this.rootDir,
|
|
1963
2067
|
configFile: this.configFile,
|
|
1964
|
-
privateKey: await this
|
|
2068
|
+
privateKey: await this.#resolveSigningKey(),
|
|
1965
2069
|
subject: params.subject || "CN=FIXME"
|
|
1966
2070
|
};
|
|
1967
2071
|
await this.withLock2(async () => {
|
|
@@ -1989,7 +2093,7 @@ var CertificateManager = class _CertificateManager extends EventEmitter {
|
|
|
1989
2093
|
...params,
|
|
1990
2094
|
rootDir: path4.resolve(this.rootDir),
|
|
1991
2095
|
configFile: path4.resolve(this.configFile),
|
|
1992
|
-
privateKey: await this
|
|
2096
|
+
privateKey: await this.#resolveSigningKey()
|
|
1993
2097
|
};
|
|
1994
2098
|
return await this.withLock2(async () => {
|
|
1995
2099
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -2976,6 +3080,7 @@ export {
|
|
|
2976
3080
|
NativeCaBackend,
|
|
2977
3081
|
OpenSslCaBackend,
|
|
2978
3082
|
PrivateKeyPassphraseRequiredError,
|
|
3083
|
+
PrivateKeyUnavailableError2 as PrivateKeyUnavailableError,
|
|
2979
3084
|
Subject,
|
|
2980
3085
|
VerificationStatus,
|
|
2981
3086
|
adjustApplicationUri,
|
|
@@ -2992,6 +3097,7 @@ export {
|
|
|
2992
3097
|
install_prerequisite,
|
|
2993
3098
|
isIntermediateIssuer,
|
|
2994
3099
|
isIssuer,
|
|
3100
|
+
isOpaqueSigner,
|
|
2995
3101
|
isRootIssuer,
|
|
2996
3102
|
makeFingerprint,
|
|
2997
3103
|
quote,
|