node-opcua-pki 6.21.0 → 6.22.1
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 +159 -18
- 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 +51 -5
- package/dist/index.d.ts +51 -5
- package/dist/index.js +164 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +165 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-2HTNH73K.mjs.map +0 -1
package/dist/core.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PrivateKey, Certificate, SubjectOptions, DER, CertificateRevocationList } from 'node-opcua-crypto';
|
|
2
|
-
export { PrivateKeyPassphraseRequiredError, Subject, SubjectOptions } from 'node-opcua-crypto';
|
|
3
|
-
import { C as CaBackend, a as CertificateAuthorityCore, P as Params, b as ProcessAltNamesParam, c as CertificateAuthorityCoreOptions, K as KeySize, d as PrivateKeyPassphrase, e as PrivateKeyProvider, f as CreateSelfSignCertificateParam, F as Filename } from './core-
|
|
4
|
-
export { g as CertificateStatus, h as CreateCertificateSigningRequestOptions, i as CreateCertificateSigningRequestWithConfigOptions, j as CreateSelfSignCertificateWithConfigParam, G as GenerateKeyPairAndSignOptions, k as GenerateKeyPairAndSignPFXOptions, I as InitializeCSRResult, l as InstallCACertificateResult, m as KeyLength, N as NativeCaBackend, n as PkiBackendCapabilities, S as SignCertificateOptions, o as StartDateEndDateParam, T as Thumbprint, p as adjustApplicationUri, q as adjustDate, r as
|
|
1
|
+
import { IKeyOperations, PrivateKey, Certificate, SubjectOptions, DER, CertificateRevocationList } from 'node-opcua-crypto';
|
|
2
|
+
export { AsymmetricDecryptParams, AsymmetricSignParams, IKeyOperations, KeyMetadata, PrivateKeyPassphraseRequiredError, PrivateKeyUnavailableError, Subject, SubjectOptions } from 'node-opcua-crypto';
|
|
3
|
+
import { C as CaBackend, a as CertificateAuthorityCore, P as Params, b as ProcessAltNamesParam, c as CertificateAuthorityCoreOptions, K as KeySize, d as PrivateKeyPassphrase, e as PrivateKeyProvider, f as CreateSelfSignCertificateParam, F as Filename } from './core-BwqSJPHc.mjs';
|
|
4
|
+
export { g as CertificateStatus, h as CreateCertificateSigningRequestOptions, i as CreateCertificateSigningRequestWithConfigOptions, j as CreateSelfSignCertificateWithConfigParam, G as GenerateKeyPairAndSignOptions, k as GenerateKeyPairAndSignPFXOptions, I as InitializeCSRResult, l as InstallCACertificateResult, m as KeyLength, N as NativeCaBackend, n as PkiBackendCapabilities, S as SignCertificateOptions, o as StartDateEndDateParam, T as Thumbprint, p as adjustApplicationUri, q as adjustDate, r as isOpaqueSigner, s as quote, t as resolvePrivateKeyPassphrase } from './core-BwqSJPHc.mjs';
|
|
5
5
|
import { EventEmitter } from 'node:events';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -239,6 +239,29 @@ interface CertificateManagerOptions {
|
|
|
239
239
|
* ignored.
|
|
240
240
|
*/
|
|
241
241
|
privateKeyProvider?: PrivateKeyProvider;
|
|
242
|
+
/**
|
|
243
|
+
* Use a private key this manager can never read: an opaque
|
|
244
|
+
* {@link IKeyOperations} provider (HSM, KMS, TPM, OS keystore, ...).
|
|
245
|
+
*
|
|
246
|
+
* The distinction with `privateKeyProvider` matters: a
|
|
247
|
+
* `privateKeyProvider` *sources raw key material* from elsewhere and
|
|
248
|
+
* hands it back; `keyOperations` never reveals the key — only sign and
|
|
249
|
+
* decrypt operations on it. Prefer `keyOperations` whenever the key
|
|
250
|
+
* does not need to be exportable.
|
|
251
|
+
*
|
|
252
|
+
* When set:
|
|
253
|
+
* - {@link CertificateManager.getPrivateKey} and
|
|
254
|
+
* {@link CertificateManager.reencryptPrivateKey} throw
|
|
255
|
+
* `PrivateKeyUnavailableError` — there is no key material to return
|
|
256
|
+
* or rewrite, and no on-disk key is generated or expected;
|
|
257
|
+
* - {@link CertificateManager.getKeyOperations} returns this object;
|
|
258
|
+
* - `initialize()` fails closed if the provider cannot answer
|
|
259
|
+
* `getKeyMetadata()` (unreachable HSM, misconfiguration);
|
|
260
|
+
* - mutually exclusive with `privateKeyProvider` and
|
|
261
|
+
* `privateKeyPassphrase` — both describe key *material*, which an
|
|
262
|
+
* opaque configuration does not have.
|
|
263
|
+
*/
|
|
264
|
+
keyOperations?: IKeyOperations;
|
|
242
265
|
}
|
|
243
266
|
/**
|
|
244
267
|
* Parameters for {@link createSelfSignedCertificate}.
|
|
@@ -341,6 +364,8 @@ declare enum VerificationStatus {
|
|
|
341
364
|
}
|
|
342
365
|
declare function coerceCertificateChain(certificate: Certificate | Certificate[]): Certificate[];
|
|
343
366
|
declare function makeFingerprint(certificate: Certificate | Certificate[] | CertificateRevocationList): string;
|
|
367
|
+
declare function toFilenameLabel(commonName: string): string;
|
|
368
|
+
declare function safeStoreJoin(folder: string, filename: string): string;
|
|
344
369
|
/**
|
|
345
370
|
* Check if the provided certificate acts as an issuer (CA)
|
|
346
371
|
* @param certificate - the DER-encoded certificate
|
|
@@ -533,6 +558,27 @@ declare class CertificateManager extends EventEmitter {
|
|
|
533
558
|
* authority on what the current key is.
|
|
534
559
|
*/
|
|
535
560
|
getPrivateKey(): Promise<PrivateKey>;
|
|
561
|
+
/**
|
|
562
|
+
* True when this manager's key is opaque — configured through
|
|
563
|
+
* `keyOperations`, held by an HSM/KMS, never obtainable as material.
|
|
564
|
+
* When true, {@link getPrivateKey} throws `PrivateKeyUnavailableError`
|
|
565
|
+
* and {@link getKeyOperations} is the only way to use the key.
|
|
566
|
+
*/
|
|
567
|
+
isPrivateKeyOpaque(): boolean;
|
|
568
|
+
/**
|
|
569
|
+
* The key as an opaque {@link IKeyOperations} — the recommended way to
|
|
570
|
+
* *use* the private key regardless of where it lives.
|
|
571
|
+
*
|
|
572
|
+
* Returns the configured `keyOperations` object when the key is opaque.
|
|
573
|
+
* Otherwise returns a stable lazy wrap over {@link getPrivateKey}: its
|
|
574
|
+
* methods resolve the key on first use (disk read, passphrase,
|
|
575
|
+
* `privateKeyProvider` — all async), so the wrap offers no synchronous
|
|
576
|
+
* fast path; callers that need one resolve the key themselves and build
|
|
577
|
+
* a `LocalKeyOperations` over it. The wrap follows key rotation: a
|
|
578
|
+
* `privateKeyProvider` that starts returning a different key gets a
|
|
579
|
+
* fresh underlying `LocalKeyOperations`.
|
|
580
|
+
*/
|
|
581
|
+
getKeyOperations(): IKeyOperations;
|
|
536
582
|
/**
|
|
537
583
|
* Enable, disable, or rotate the passphrase protecting the on-disk
|
|
538
584
|
* private key: decrypt with `oldPassphrase` (omit if the key is
|
|
@@ -954,4 +1000,4 @@ declare function convertPFXtoPEM(pfxFile: Filename, pemFile: Filename, passphras
|
|
|
954
1000
|
*/
|
|
955
1001
|
declare function dumpPFX(pfxFile: Filename, passphrase?: string): Promise<string>;
|
|
956
1002
|
|
|
957
|
-
export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint };
|
|
1003
|
+
export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint, safeStoreJoin, toFilenameLabel };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PrivateKey, Certificate, SubjectOptions, DER, CertificateRevocationList } from 'node-opcua-crypto';
|
|
2
|
-
export { PrivateKeyPassphraseRequiredError, Subject, SubjectOptions } from 'node-opcua-crypto';
|
|
3
|
-
import { C as CaBackend, a as CertificateAuthorityCore, P as Params, b as ProcessAltNamesParam, c as CertificateAuthorityCoreOptions, K as KeySize, d as PrivateKeyPassphrase, e as PrivateKeyProvider, f as CreateSelfSignCertificateParam, F as Filename } from './core-
|
|
4
|
-
export { g as CertificateStatus, h as CreateCertificateSigningRequestOptions, i as CreateCertificateSigningRequestWithConfigOptions, j as CreateSelfSignCertificateWithConfigParam, G as GenerateKeyPairAndSignOptions, k as GenerateKeyPairAndSignPFXOptions, I as InitializeCSRResult, l as InstallCACertificateResult, m as KeyLength, N as NativeCaBackend, n as PkiBackendCapabilities, S as SignCertificateOptions, o as StartDateEndDateParam, T as Thumbprint, p as adjustApplicationUri, q as adjustDate, r as
|
|
1
|
+
import { IKeyOperations, PrivateKey, Certificate, SubjectOptions, DER, CertificateRevocationList } from 'node-opcua-crypto';
|
|
2
|
+
export { AsymmetricDecryptParams, AsymmetricSignParams, IKeyOperations, KeyMetadata, PrivateKeyPassphraseRequiredError, PrivateKeyUnavailableError, Subject, SubjectOptions } from 'node-opcua-crypto';
|
|
3
|
+
import { C as CaBackend, a as CertificateAuthorityCore, P as Params, b as ProcessAltNamesParam, c as CertificateAuthorityCoreOptions, K as KeySize, d as PrivateKeyPassphrase, e as PrivateKeyProvider, f as CreateSelfSignCertificateParam, F as Filename } from './core-BwqSJPHc.js';
|
|
4
|
+
export { g as CertificateStatus, h as CreateCertificateSigningRequestOptions, i as CreateCertificateSigningRequestWithConfigOptions, j as CreateSelfSignCertificateWithConfigParam, G as GenerateKeyPairAndSignOptions, k as GenerateKeyPairAndSignPFXOptions, I as InitializeCSRResult, l as InstallCACertificateResult, m as KeyLength, N as NativeCaBackend, n as PkiBackendCapabilities, S as SignCertificateOptions, o as StartDateEndDateParam, T as Thumbprint, p as adjustApplicationUri, q as adjustDate, r as isOpaqueSigner, s as quote, t as resolvePrivateKeyPassphrase } from './core-BwqSJPHc.js';
|
|
5
5
|
import { EventEmitter } from 'node:events';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -239,6 +239,29 @@ interface CertificateManagerOptions {
|
|
|
239
239
|
* ignored.
|
|
240
240
|
*/
|
|
241
241
|
privateKeyProvider?: PrivateKeyProvider;
|
|
242
|
+
/**
|
|
243
|
+
* Use a private key this manager can never read: an opaque
|
|
244
|
+
* {@link IKeyOperations} provider (HSM, KMS, TPM, OS keystore, ...).
|
|
245
|
+
*
|
|
246
|
+
* The distinction with `privateKeyProvider` matters: a
|
|
247
|
+
* `privateKeyProvider` *sources raw key material* from elsewhere and
|
|
248
|
+
* hands it back; `keyOperations` never reveals the key — only sign and
|
|
249
|
+
* decrypt operations on it. Prefer `keyOperations` whenever the key
|
|
250
|
+
* does not need to be exportable.
|
|
251
|
+
*
|
|
252
|
+
* When set:
|
|
253
|
+
* - {@link CertificateManager.getPrivateKey} and
|
|
254
|
+
* {@link CertificateManager.reencryptPrivateKey} throw
|
|
255
|
+
* `PrivateKeyUnavailableError` — there is no key material to return
|
|
256
|
+
* or rewrite, and no on-disk key is generated or expected;
|
|
257
|
+
* - {@link CertificateManager.getKeyOperations} returns this object;
|
|
258
|
+
* - `initialize()` fails closed if the provider cannot answer
|
|
259
|
+
* `getKeyMetadata()` (unreachable HSM, misconfiguration);
|
|
260
|
+
* - mutually exclusive with `privateKeyProvider` and
|
|
261
|
+
* `privateKeyPassphrase` — both describe key *material*, which an
|
|
262
|
+
* opaque configuration does not have.
|
|
263
|
+
*/
|
|
264
|
+
keyOperations?: IKeyOperations;
|
|
242
265
|
}
|
|
243
266
|
/**
|
|
244
267
|
* Parameters for {@link createSelfSignedCertificate}.
|
|
@@ -341,6 +364,8 @@ declare enum VerificationStatus {
|
|
|
341
364
|
}
|
|
342
365
|
declare function coerceCertificateChain(certificate: Certificate | Certificate[]): Certificate[];
|
|
343
366
|
declare function makeFingerprint(certificate: Certificate | Certificate[] | CertificateRevocationList): string;
|
|
367
|
+
declare function toFilenameLabel(commonName: string): string;
|
|
368
|
+
declare function safeStoreJoin(folder: string, filename: string): string;
|
|
344
369
|
/**
|
|
345
370
|
* Check if the provided certificate acts as an issuer (CA)
|
|
346
371
|
* @param certificate - the DER-encoded certificate
|
|
@@ -533,6 +558,27 @@ declare class CertificateManager extends EventEmitter {
|
|
|
533
558
|
* authority on what the current key is.
|
|
534
559
|
*/
|
|
535
560
|
getPrivateKey(): Promise<PrivateKey>;
|
|
561
|
+
/**
|
|
562
|
+
* True when this manager's key is opaque — configured through
|
|
563
|
+
* `keyOperations`, held by an HSM/KMS, never obtainable as material.
|
|
564
|
+
* When true, {@link getPrivateKey} throws `PrivateKeyUnavailableError`
|
|
565
|
+
* and {@link getKeyOperations} is the only way to use the key.
|
|
566
|
+
*/
|
|
567
|
+
isPrivateKeyOpaque(): boolean;
|
|
568
|
+
/**
|
|
569
|
+
* The key as an opaque {@link IKeyOperations} — the recommended way to
|
|
570
|
+
* *use* the private key regardless of where it lives.
|
|
571
|
+
*
|
|
572
|
+
* Returns the configured `keyOperations` object when the key is opaque.
|
|
573
|
+
* Otherwise returns a stable lazy wrap over {@link getPrivateKey}: its
|
|
574
|
+
* methods resolve the key on first use (disk read, passphrase,
|
|
575
|
+
* `privateKeyProvider` — all async), so the wrap offers no synchronous
|
|
576
|
+
* fast path; callers that need one resolve the key themselves and build
|
|
577
|
+
* a `LocalKeyOperations` over it. The wrap follows key rotation: a
|
|
578
|
+
* `privateKeyProvider` that starts returning a different key gets a
|
|
579
|
+
* fresh underlying `LocalKeyOperations`.
|
|
580
|
+
*/
|
|
581
|
+
getKeyOperations(): IKeyOperations;
|
|
536
582
|
/**
|
|
537
583
|
* Enable, disable, or rotate the passphrase protecting the on-disk
|
|
538
584
|
* private key: decrypt with `oldPassphrase` (omit if the key is
|
|
@@ -954,4 +1000,4 @@ declare function convertPFXtoPEM(pfxFile: Filename, pemFile: Filename, passphras
|
|
|
954
1000
|
*/
|
|
955
1001
|
declare function dumpPFX(pfxFile: Filename, passphrase?: string): Promise<string>;
|
|
956
1002
|
|
|
957
|
-
export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint };
|
|
1003
|
+
export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint, safeStoreJoin, toFilenameLabel };
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ __export(lib_exports, {
|
|
|
38
38
|
NativeCaBackend: () => NativeCaBackend,
|
|
39
39
|
OpenSslCaBackend: () => OpenSslCaBackend,
|
|
40
40
|
PrivateKeyPassphraseRequiredError: () => import_node_opcua_crypto10.PrivateKeyPassphraseRequiredError,
|
|
41
|
+
PrivateKeyUnavailableError: () => import_node_opcua_crypto10.PrivateKeyUnavailableError,
|
|
41
42
|
Subject: () => import_node_opcua_crypto3.Subject,
|
|
42
43
|
VerificationStatus: () => VerificationStatus,
|
|
43
44
|
adjustApplicationUri: () => adjustApplicationUri,
|
|
@@ -54,10 +55,13 @@ __export(lib_exports, {
|
|
|
54
55
|
install_prerequisite: () => install_prerequisite,
|
|
55
56
|
isIntermediateIssuer: () => isIntermediateIssuer,
|
|
56
57
|
isIssuer: () => isIssuer,
|
|
58
|
+
isOpaqueSigner: () => isOpaqueSigner,
|
|
57
59
|
isRootIssuer: () => isRootIssuer,
|
|
58
60
|
makeFingerprint: () => makeFingerprint,
|
|
59
61
|
quote: () => quote,
|
|
60
|
-
resolvePrivateKeyPassphrase: () => resolvePrivateKeyPassphrase
|
|
62
|
+
resolvePrivateKeyPassphrase: () => resolvePrivateKeyPassphrase,
|
|
63
|
+
safeStoreJoin: () => safeStoreJoin,
|
|
64
|
+
toFilenameLabel: () => toFilenameLabel
|
|
61
65
|
});
|
|
62
66
|
module.exports = __toCommonJS(lib_exports);
|
|
63
67
|
var import_node_opcua_crypto10 = require("node-opcua-crypto");
|
|
@@ -72,6 +76,9 @@ var import_node_assert = __toESM(require("assert"));
|
|
|
72
76
|
function quote(str) {
|
|
73
77
|
return `"${str || ""}"`;
|
|
74
78
|
}
|
|
79
|
+
function isOpaqueSigner(privateKey) {
|
|
80
|
+
return typeof privateKey !== "string" && typeof privateKey.sign === "function";
|
|
81
|
+
}
|
|
75
82
|
async function resolvePrivateKeyPassphrase(passphrase) {
|
|
76
83
|
if (passphrase === void 0) {
|
|
77
84
|
return void 0;
|
|
@@ -1060,8 +1067,32 @@ function passinArg(passphrase = "") {
|
|
|
1060
1067
|
function renderForDisplay(file, args) {
|
|
1061
1068
|
return [file, ...args].map((a) => a === "" || /[\s"'`$\\]/.test(a) ? JSON.stringify(a) : a).join(" ");
|
|
1062
1069
|
}
|
|
1070
|
+
function hasUnsafeArgChar(value) {
|
|
1071
|
+
for (const ch of value) {
|
|
1072
|
+
const code = ch.codePointAt(0);
|
|
1073
|
+
if (code < 32 || code === 127) {
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
if (code < 128) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (/[\p{L}\p{N}\p{M}]/u.test(ch)) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
return true;
|
|
1083
|
+
}
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
function checkArgs(file, args) {
|
|
1087
|
+
for (const value of [file, ...args]) {
|
|
1088
|
+
if (typeof value === "string" && hasUnsafeArgChar(value)) {
|
|
1089
|
+
throw new Error(`execute_openssl: refusing argument with an unexpected character: ${JSON.stringify(value)}`);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1063
1093
|
async function execute2(file, args, options) {
|
|
1064
1094
|
const from = new Error();
|
|
1095
|
+
checkArgs(file, args);
|
|
1065
1096
|
options.cwd = options.cwd || process.cwd();
|
|
1066
1097
|
if (!g_config.silent) {
|
|
1067
1098
|
warningLog(import_chalk4.default.cyan(" CWD "), options.cwd);
|
|
@@ -2887,8 +2918,9 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
2887
2918
|
(0, import_node_assert6.default)(typeof certificateSigningRequestFilename === "string");
|
|
2888
2919
|
const subject = params.subject ? new import_node_opcua_crypto6.Subject(params.subject).toString() : void 0;
|
|
2889
2920
|
displaySubtitle("- Creating a Certificate Signing Request with subtile");
|
|
2890
|
-
const
|
|
2891
|
-
|
|
2921
|
+
const privateKey = isOpaqueSigner(params.privateKey) ? params.privateKey : await (0, import_node_opcua_crypto6.pemToPrivateKey)(
|
|
2922
|
+
typeof params.privateKey === "string" ? await import_node_fs9.default.promises.readFile(params.privateKey, "utf-8") : (0, import_node_opcua_crypto6.coercePrivateKeyPem)(params.privateKey)
|
|
2923
|
+
);
|
|
2892
2924
|
const { csr } = await (0, import_node_opcua_crypto6.createCertificateSigningRequest)({
|
|
2893
2925
|
privateKey,
|
|
2894
2926
|
dns: params.dns,
|
|
@@ -2898,7 +2930,9 @@ async function createCertificateSigningRequestAsync(certificateSigningRequestFil
|
|
|
2898
2930
|
purpose: params.purpose
|
|
2899
2931
|
});
|
|
2900
2932
|
await import_node_fs9.default.promises.writeFile(certificateSigningRequestFilename, csr, "utf-8");
|
|
2901
|
-
display(
|
|
2933
|
+
display(
|
|
2934
|
+
`- privateKey ${typeof params.privateKey === "string" ? params.privateKey : isOpaqueSigner(params.privateKey) ? "<opaque-signer>" : "<in-memory>"}`
|
|
2935
|
+
);
|
|
2902
2936
|
display(`- certificateSigningRequestFilename ${certificateSigningRequestFilename}`);
|
|
2903
2937
|
}
|
|
2904
2938
|
|
|
@@ -2925,8 +2959,9 @@ async function createSelfSignedCertificateAsync(certificate, params) {
|
|
|
2925
2959
|
subject = subject.toString();
|
|
2926
2960
|
const purpose = params.purpose;
|
|
2927
2961
|
displayTitle("Generate a certificate request");
|
|
2928
|
-
const
|
|
2929
|
-
|
|
2962
|
+
const privateKey = isOpaqueSigner(params.privateKey) ? params.privateKey : await (0, import_node_opcua_crypto7.pemToPrivateKey)(
|
|
2963
|
+
typeof params.privateKey === "string" ? await import_node_fs10.default.promises.readFile(params.privateKey, "utf-8") : (0, import_node_opcua_crypto7.coercePrivateKeyPem)(params.privateKey)
|
|
2964
|
+
);
|
|
2930
2965
|
const { cert } = await (0, import_node_opcua_crypto7.createSelfSignedCertificate)({
|
|
2931
2966
|
privateKey,
|
|
2932
2967
|
notBefore: params.startDate,
|
|
@@ -2992,18 +3027,31 @@ function makeFingerprint(certificate) {
|
|
|
2992
3027
|
function short(stringToShorten) {
|
|
2993
3028
|
return stringToShorten.substring(0, 10);
|
|
2994
3029
|
}
|
|
2995
|
-
var
|
|
3030
|
+
var disallowedInLabel = /[^A-Za-z0-9._ -]/g;
|
|
3031
|
+
function toFilenameLabel(commonName) {
|
|
3032
|
+
const label = (commonName || "").normalize("NFKC").replace(disallowedInLabel, "_").replace(/[^\x20-\x7E]/g, "").slice(0, 64).trim();
|
|
3033
|
+
return label || "certificate";
|
|
3034
|
+
}
|
|
2996
3035
|
function buildIdealCertificateName(certificate) {
|
|
2997
3036
|
const chain = coerceCertificateChain(certificate);
|
|
2998
3037
|
const fingerprint = makeFingerprint(chain);
|
|
2999
3038
|
try {
|
|
3000
3039
|
const commonName = (0, import_node_opcua_crypto8.exploreCertificate)(chain[0]).tbsCertificate.subject.commonName || "";
|
|
3001
|
-
const sanitizedCommonName = commonName
|
|
3040
|
+
const sanitizedCommonName = toFilenameLabel(commonName);
|
|
3002
3041
|
return `${sanitizedCommonName}[${fingerprint}]`;
|
|
3003
3042
|
} catch (_err) {
|
|
3004
3043
|
return `invalid_certificate_[${fingerprint}]`;
|
|
3005
3044
|
}
|
|
3006
3045
|
}
|
|
3046
|
+
function safeStoreJoin(folder, filename) {
|
|
3047
|
+
const full = import_node_path8.default.resolve(folder, filename);
|
|
3048
|
+
const base = import_node_path8.default.resolve(folder);
|
|
3049
|
+
if (full !== base && !full.startsWith(base + import_node_path8.default.sep)) {
|
|
3050
|
+
warningLog("refusing to write certificate outside its store folder:", filename);
|
|
3051
|
+
throw new Error(`certificate filename escapes its store folder: ${filename}`);
|
|
3052
|
+
}
|
|
3053
|
+
return full;
|
|
3054
|
+
}
|
|
3007
3055
|
function findMatchingIssuerKey(entries, wantedIssuerKey) {
|
|
3008
3056
|
return entries.filter((entry) => {
|
|
3009
3057
|
const info = getOrComputeInfo(entry);
|
|
@@ -3231,6 +3279,9 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3231
3279
|
#disableFileWatchers;
|
|
3232
3280
|
#privateKeyPassphrase;
|
|
3233
3281
|
#privateKeyProvider;
|
|
3282
|
+
#keyOperations;
|
|
3283
|
+
/** The stable lazy wrap handed out by {@link getKeyOperations} for non-opaque configurations. */
|
|
3284
|
+
#lazyLocalKeyOperations;
|
|
3234
3285
|
/**
|
|
3235
3286
|
* The on-disk key, decrypted once and kept for the instance's lifetime,
|
|
3236
3287
|
* so the passphrase (or its resolver function) is consulted at most
|
|
@@ -3274,6 +3325,17 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3274
3325
|
this.#disableFileWatchers = options.disableFileWatchers ?? process.env.OPCUA_PKI_DISABLE_FILE_WATCHERS === "true";
|
|
3275
3326
|
this.#privateKeyPassphrase = options.privateKeyPassphrase;
|
|
3276
3327
|
this.#privateKeyProvider = options.privateKeyProvider;
|
|
3328
|
+
this.#keyOperations = options.keyOperations;
|
|
3329
|
+
if (this.#keyOperations && this.#privateKeyProvider) {
|
|
3330
|
+
throw new Error(
|
|
3331
|
+
"CertificateManager: 'keyOperations' and 'privateKeyProvider' are mutually exclusive: one hides the key, the other sources its material"
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
if (this.#keyOperations && this.#privateKeyPassphrase) {
|
|
3335
|
+
throw new Error(
|
|
3336
|
+
"CertificateManager: 'privateKeyPassphrase' is meaningless with 'keyOperations': there is no key material for a passphrase to protect"
|
|
3337
|
+
);
|
|
3338
|
+
}
|
|
3277
3339
|
mkdirRecursiveSync(options.location);
|
|
3278
3340
|
if (!import_node_fs11.default.existsSync(this.#location)) {
|
|
3279
3341
|
throw new Error(`CertificateManager cannot access location ${this.#location}`);
|
|
@@ -3315,6 +3377,11 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3315
3377
|
* authority on what the current key is.
|
|
3316
3378
|
*/
|
|
3317
3379
|
async getPrivateKey() {
|
|
3380
|
+
if (this.#keyOperations) {
|
|
3381
|
+
throw new import_node_opcua_crypto8.PrivateKeyUnavailableError(
|
|
3382
|
+
"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"
|
|
3383
|
+
);
|
|
3384
|
+
}
|
|
3318
3385
|
if (this.#privateKeyProvider) {
|
|
3319
3386
|
return await this.#privateKeyProvider.getPrivateKey();
|
|
3320
3387
|
}
|
|
@@ -3340,6 +3407,75 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3340
3407
|
}
|
|
3341
3408
|
/** In-flight first read of the on-disk key, so concurrent callers share one passphrase resolution. */
|
|
3342
3409
|
#privateKeyPromise;
|
|
3410
|
+
/**
|
|
3411
|
+
* True when this manager's key is opaque — configured through
|
|
3412
|
+
* `keyOperations`, held by an HSM/KMS, never obtainable as material.
|
|
3413
|
+
* When true, {@link getPrivateKey} throws `PrivateKeyUnavailableError`
|
|
3414
|
+
* and {@link getKeyOperations} is the only way to use the key.
|
|
3415
|
+
*/
|
|
3416
|
+
isPrivateKeyOpaque() {
|
|
3417
|
+
return !!this.#keyOperations;
|
|
3418
|
+
}
|
|
3419
|
+
/**
|
|
3420
|
+
* The key as an opaque {@link IKeyOperations} — the recommended way to
|
|
3421
|
+
* *use* the private key regardless of where it lives.
|
|
3422
|
+
*
|
|
3423
|
+
* Returns the configured `keyOperations` object when the key is opaque.
|
|
3424
|
+
* Otherwise returns a stable lazy wrap over {@link getPrivateKey}: its
|
|
3425
|
+
* methods resolve the key on first use (disk read, passphrase,
|
|
3426
|
+
* `privateKeyProvider` — all async), so the wrap offers no synchronous
|
|
3427
|
+
* fast path; callers that need one resolve the key themselves and build
|
|
3428
|
+
* a `LocalKeyOperations` over it. The wrap follows key rotation: a
|
|
3429
|
+
* `privateKeyProvider` that starts returning a different key gets a
|
|
3430
|
+
* fresh underlying `LocalKeyOperations`.
|
|
3431
|
+
*/
|
|
3432
|
+
getKeyOperations() {
|
|
3433
|
+
if (this.#keyOperations) {
|
|
3434
|
+
return this.#keyOperations;
|
|
3435
|
+
}
|
|
3436
|
+
if (!this.#lazyLocalKeyOperations) {
|
|
3437
|
+
let cached;
|
|
3438
|
+
const resolve = async () => {
|
|
3439
|
+
const key = await this.getPrivateKey();
|
|
3440
|
+
if (!cached || cached.key !== key) {
|
|
3441
|
+
cached = { key, ops: (0, import_node_opcua_crypto8.keyOperationsFromPrivateKey)(key) };
|
|
3442
|
+
}
|
|
3443
|
+
return cached.ops;
|
|
3444
|
+
};
|
|
3445
|
+
this.#lazyLocalKeyOperations = {
|
|
3446
|
+
sign: async (data, params) => (await resolve()).sign(data, params),
|
|
3447
|
+
decryptBlock: async (block, params) => (await resolve()).decryptBlock(block, params),
|
|
3448
|
+
getKeyMetadata: async () => (await resolve()).getKeyMetadata(),
|
|
3449
|
+
getPublicKey: async () => (await resolve()).getPublicKey()
|
|
3450
|
+
};
|
|
3451
|
+
}
|
|
3452
|
+
return this.#lazyLocalKeyOperations;
|
|
3453
|
+
}
|
|
3454
|
+
/**
|
|
3455
|
+
* Fail closed at `initialize()` time, not on the first certificate
|
|
3456
|
+
* operation, if the key cannot actually be used: wrong/missing
|
|
3457
|
+
* passphrase on an encrypted key, a broken `privateKeyProvider`, or an
|
|
3458
|
+
* unreachable `keyOperations` provider (probed via `getKeyMetadata`).
|
|
3459
|
+
*/
|
|
3460
|
+
async #probePrivateKey() {
|
|
3461
|
+
if (this.#keyOperations) {
|
|
3462
|
+
await this.#keyOperations.getKeyMetadata();
|
|
3463
|
+
return;
|
|
3464
|
+
}
|
|
3465
|
+
await this.getPrivateKey();
|
|
3466
|
+
}
|
|
3467
|
+
/**
|
|
3468
|
+
* The key to hand to a certificate-issuance primitive: the raw
|
|
3469
|
+
* {@link PrivateKey} for a local configuration, or a {@link CaSigner}
|
|
3470
|
+
* adapted from `keyOperations` when the key is opaque — which requires
|
|
3471
|
+
* the provider to implement `getPublicKey` (the adapter says so if not).
|
|
3472
|
+
*/
|
|
3473
|
+
async #resolveSigningKey() {
|
|
3474
|
+
if (this.#keyOperations) {
|
|
3475
|
+
return (0, import_node_opcua_crypto8.caSignerFromKeyOperations)(this.#keyOperations);
|
|
3476
|
+
}
|
|
3477
|
+
return await this.getPrivateKey();
|
|
3478
|
+
}
|
|
3343
3479
|
/**
|
|
3344
3480
|
* Enable, disable, or rotate the passphrase protecting the on-disk
|
|
3345
3481
|
* private key: decrypt with `oldPassphrase` (omit if the key is
|
|
@@ -3361,6 +3497,11 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3361
3497
|
* disk file for this method to rewrite).
|
|
3362
3498
|
*/
|
|
3363
3499
|
async reencryptPrivateKey(oldPassphrase, newPassphrase) {
|
|
3500
|
+
if (this.#keyOperations) {
|
|
3501
|
+
throw new import_node_opcua_crypto8.PrivateKeyUnavailableError(
|
|
3502
|
+
"reencryptPrivateKey: not supported when keyOperations is configured \u2014 there is no key material to rewrite"
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3364
3505
|
if (this.#privateKeyProvider) {
|
|
3365
3506
|
throw new Error("reencryptPrivateKey: not supported when a privateKeyProvider is configured");
|
|
3366
3507
|
}
|
|
@@ -3486,7 +3627,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3486
3627
|
} catch (_err) {
|
|
3487
3628
|
return "BadCertificateInvalid";
|
|
3488
3629
|
}
|
|
3489
|
-
const filename =
|
|
3630
|
+
const filename = safeStoreJoin(this.rejectedFolder, `${buildIdealCertificateName(leafCertificate)}.pem`);
|
|
3490
3631
|
debugLog("certificate has never been seen before and is now rejected (untrusted) ", filename);
|
|
3491
3632
|
await fsWriteFile(filename, (0, import_node_opcua_crypto8.toPem)(chain, "CERTIFICATE"));
|
|
3492
3633
|
this.#thumbs.rejected.set(fingerprint, { certificate: leafCertificate, filename });
|
|
@@ -3709,7 +3850,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3709
3850
|
mkdirRecursiveSync(import_node_path8.default.join(pkiDir, "issuers"));
|
|
3710
3851
|
mkdirRecursiveSync(import_node_path8.default.join(pkiDir, "issuers/certs"));
|
|
3711
3852
|
mkdirRecursiveSync(import_node_path8.default.join(pkiDir, "issuers/crl"));
|
|
3712
|
-
const ownsDiskKey = !this.#privateKeyProvider;
|
|
3853
|
+
const ownsDiskKey = !this.#privateKeyProvider && !this.#keyOperations;
|
|
3713
3854
|
const needsKeyGeneration = ownsDiskKey && !import_node_fs11.default.existsSync(this.privateKey);
|
|
3714
3855
|
const needsKeyEncryption = ownsDiskKey && !needsKeyGeneration && this.#privateKeyPassphrase !== void 0 && !isEncryptedPrivateKeyFile(this.privateKey);
|
|
3715
3856
|
if (!import_node_fs11.default.existsSync(this.configFile) || needsKeyGeneration || needsKeyEncryption) {
|
|
@@ -3735,14 +3876,14 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3735
3876
|
if (ownsDiskKey) {
|
|
3736
3877
|
restrictPrivateFilePermissions(this.privateKey, 384);
|
|
3737
3878
|
}
|
|
3738
|
-
await this
|
|
3879
|
+
await this.#probePrivateKey();
|
|
3739
3880
|
await this.#readCertificates();
|
|
3740
3881
|
});
|
|
3741
3882
|
} else {
|
|
3742
3883
|
if (ownsDiskKey) {
|
|
3743
3884
|
restrictPrivateFilePermissions(this.privateKey, 384);
|
|
3744
3885
|
}
|
|
3745
|
-
await this
|
|
3886
|
+
await this.#probePrivateKey();
|
|
3746
3887
|
await this.#readCertificates();
|
|
3747
3888
|
}
|
|
3748
3889
|
}
|
|
@@ -3827,7 +3968,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3827
3968
|
if (typeof params.applicationUri !== "string") {
|
|
3828
3969
|
throw new Error("createSelfSignedCertificate: expecting applicationUri to be a string");
|
|
3829
3970
|
}
|
|
3830
|
-
if (!this.#privateKeyProvider && !import_node_fs11.default.existsSync(this.privateKey)) {
|
|
3971
|
+
if (!this.#privateKeyProvider && !this.#keyOperations && !import_node_fs11.default.existsSync(this.privateKey)) {
|
|
3831
3972
|
throw new Error(`Cannot find private key ${this.privateKey}`);
|
|
3832
3973
|
}
|
|
3833
3974
|
let certificateFilename = import_node_path8.default.join(this.rootDir, "own/certs/self_signed_certificate.pem");
|
|
@@ -3836,7 +3977,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3836
3977
|
...params,
|
|
3837
3978
|
rootDir: this.rootDir,
|
|
3838
3979
|
configFile: this.configFile,
|
|
3839
|
-
privateKey: await this
|
|
3980
|
+
privateKey: await this.#resolveSigningKey(),
|
|
3840
3981
|
subject: params.subject || "CN=FIXME"
|
|
3841
3982
|
};
|
|
3842
3983
|
await this.withLock2(async () => {
|
|
@@ -3864,7 +4005,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3864
4005
|
...params,
|
|
3865
4006
|
rootDir: import_node_path8.default.resolve(this.rootDir),
|
|
3866
4007
|
configFile: import_node_path8.default.resolve(this.configFile),
|
|
3867
|
-
privateKey: await this
|
|
4008
|
+
privateKey: await this.#resolveSigningKey()
|
|
3868
4009
|
};
|
|
3869
4010
|
return await this.withLock2(async () => {
|
|
3870
4011
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -3894,7 +4035,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
3894
4035
|
if (this.#thumbs.issuers.certs.has(fingerprint)) {
|
|
3895
4036
|
return "Good" /* Good */;
|
|
3896
4037
|
}
|
|
3897
|
-
const filename =
|
|
4038
|
+
const filename = safeStoreJoin(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
|
|
3898
4039
|
await import_node_fs11.default.promises.writeFile(filename, pemCertificate, "ascii");
|
|
3899
4040
|
this.#thumbs.issuers.certs.set(fingerprint, { certificate, filename });
|
|
3900
4041
|
if (addInTrustList) {
|
|
@@ -4353,7 +4494,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
4353
4494
|
let status = await this.#checkRejectedOrTrusted(certificate);
|
|
4354
4495
|
if (status === "unknown") {
|
|
4355
4496
|
const pem = (0, import_node_opcua_crypto8.toPem)(chain, "CERTIFICATE");
|
|
4356
|
-
const filename =
|
|
4497
|
+
const filename = safeStoreJoin(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
|
|
4357
4498
|
await import_node_fs11.default.promises.writeFile(filename, pem);
|
|
4358
4499
|
this.#thumbs.rejected.set(fingerprint, { certificate, filename });
|
|
4359
4500
|
status = "rejected";
|
|
@@ -4370,7 +4511,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
|
|
|
4370
4511
|
throw new Error(`#moveCertificate: certificate ${fingerprint.substring(0, 10)} not found in ${status} index`);
|
|
4371
4512
|
}
|
|
4372
4513
|
const destFolder = newStatus === "trusted" ? this.trustedFolder : this.rejectedFolder;
|
|
4373
|
-
const certificateDest =
|
|
4514
|
+
const certificateDest = safeStoreJoin(destFolder, import_node_path8.default.basename(srcEntry.filename));
|
|
4374
4515
|
debugLog("#moveCertificate", fingerprint.substring(0, 10), "old name", srcEntry.filename);
|
|
4375
4516
|
debugLog("#moveCertificate", fingerprint.substring(0, 10), "new name", certificateDest);
|
|
4376
4517
|
await import_node_fs11.default.promises.rename(srcEntry.filename, certificateDest);
|
|
@@ -4840,6 +4981,7 @@ async function dumpPFX(pfxFile, passphrase = "") {
|
|
|
4840
4981
|
NativeCaBackend,
|
|
4841
4982
|
OpenSslCaBackend,
|
|
4842
4983
|
PrivateKeyPassphraseRequiredError,
|
|
4984
|
+
PrivateKeyUnavailableError,
|
|
4843
4985
|
Subject,
|
|
4844
4986
|
VerificationStatus,
|
|
4845
4987
|
adjustApplicationUri,
|
|
@@ -4856,9 +4998,12 @@ async function dumpPFX(pfxFile, passphrase = "") {
|
|
|
4856
4998
|
install_prerequisite,
|
|
4857
4999
|
isIntermediateIssuer,
|
|
4858
5000
|
isIssuer,
|
|
5001
|
+
isOpaqueSigner,
|
|
4859
5002
|
isRootIssuer,
|
|
4860
5003
|
makeFingerprint,
|
|
4861
5004
|
quote,
|
|
4862
|
-
resolvePrivateKeyPassphrase
|
|
5005
|
+
resolvePrivateKeyPassphrase,
|
|
5006
|
+
safeStoreJoin,
|
|
5007
|
+
toFilenameLabel
|
|
4863
5008
|
});
|
|
4864
5009
|
//# sourceMappingURL=index.js.map
|