node-opcua-client 2.170.1 → 2.173.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.
@@ -9,8 +9,8 @@ import { withLock } from "@ster5/global-mutex";
9
9
 
10
10
  import chalk from "chalk";
11
11
  import { assert } from "node-opcua-assert";
12
- import { getDefaultCertificateManager, makeSubject, type OPCUACertificateManager } from "node-opcua-certificate-manager";
13
- import { type IOPCUASecureObjectOptions, makeApplicationUrn, OPCUASecureObject } from "node-opcua-common";
12
+ import { getDefaultCertificateManager, type OPCUACertificateManager } from "node-opcua-certificate-manager";
13
+ import { type ICertificateStore, InMemoryCertificateStore, type IOPCUASecureObjectOptions, makeApplicationUrn, makeSubject, OPCUASecureObject } from "node-opcua-common";
14
14
  import { type Certificate, makeSHA1Thumbprint, split_der } from "node-opcua-crypto/web";
15
15
  import { installPeriodicClockAdjustment, periodicClockAdjustment, uninstallPeriodicClockAdjustment } from "node-opcua-date-time";
16
16
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
@@ -27,6 +27,7 @@ import {
27
27
  type Response as Response1,
28
28
  type SecurityPolicy
29
29
  } from "node-opcua-secure-channel";
30
+ import type { IClientTransportFactory } from "node-opcua-transport";
30
31
  import {
31
32
  FindServersOnNetworkRequest,
32
33
  type FindServersOnNetworkRequestOptions,
@@ -228,7 +229,7 @@ function __findEndpoint(this: ClientBaseImpl, endpointUrl: string, params: FindE
228
229
  /**
229
230
  * check if certificate is trusted or untrusted
230
231
  */
231
- async function _verify_serverCertificate(certificateManager: OPCUACertificateManager, serverCertificate: Certificate) {
232
+ async function _verify_serverCertificate(certificateManager: ICertificateStore, serverCertificate: Certificate) {
232
233
  const status = await certificateManager.checkCertificate(serverCertificate);
233
234
  if (!status.isGood()) {
234
235
  // c8 ignore next
@@ -387,8 +388,9 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
387
388
  private _instanceNumber: number;
388
389
  private _transportSettings: TransportSettings;
389
390
  private _transportTimeout?: number;
391
+ private _transportFactory?: IClientTransportFactory;
390
392
 
391
- public clientCertificateManager: OPCUACertificateManager;
393
+ public clientCertificateManager: ICertificateStore;
392
394
 
393
395
  public isUnusable() {
394
396
  return (
@@ -430,14 +432,25 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
430
432
  }
431
433
  constructor(options?: OPCUAClientBaseOptions) {
432
434
  options = options || {};
433
- if (!options.clientCertificateManager) {
434
- options.clientCertificateManager = getDefaultCertificateManager("PKI");
435
- }
436
- options.privateKeyFile = options.privateKeyFile || options.clientCertificateManager.privateKey;
437
- options.certificateFile =
438
- options.certificateFile || path.join(options.clientCertificateManager.rootDir, "own/certs/client_certificate.pem");
439
435
 
440
- super(options as IOPCUASecureObjectOptions);
436
+ if (options.certificateKeyPairProvider) {
437
+ // In-memory path — use a lightweight in-memory store
438
+ // when no cert manager was explicitly provided.
439
+ if (!options.clientCertificateManager) {
440
+ options.clientCertificateManager = new InMemoryCertificateStore();
441
+ }
442
+ super(options as IOPCUASecureObjectOptions);
443
+ } else {
444
+ // Disk path — derive cert/key paths from certificate manager
445
+ if (!options.clientCertificateManager) {
446
+ options.clientCertificateManager = getDefaultCertificateManager("PKI");
447
+ }
448
+ const cm = options.clientCertificateManager as OPCUACertificateManager;
449
+ options.privateKeyFile = options.privateKeyFile || cm.privateKey;
450
+ options.certificateFile =
451
+ options.certificateFile || path.join(cm.rootDir, "own/certs/client_certificate.pem");
452
+ super(options as IOPCUASecureObjectOptions);
453
+ }
441
454
 
442
455
  this._setInternalState("uninitialized");
443
456
 
@@ -450,7 +463,7 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
450
463
  // we need to delay _applicationUri initialization
451
464
  this._applicationUri = options.applicationUri || this._getBuiltApplicationUri();
452
465
 
453
- this.clientCertificateManager = options.clientCertificateManager;
466
+ this.clientCertificateManager = options.clientCertificateManager!;
454
467
  this.clientCertificateManager.referenceCounter++;
455
468
 
456
469
  this._secureChannel = null;
@@ -502,6 +515,7 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
502
515
 
503
516
  this._transportSettings = options.transportSettings || {};
504
517
  this._transportTimeout = options.transportTimeout;
518
+ this._transportFactory = options.transportFactory;
505
519
  }
506
520
 
507
521
  private _cancel_reconnection(callback: ErrorCallback) {
@@ -695,6 +709,7 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
695
709
  tokenRenewalInterval: this.tokenRenewalInterval,
696
710
  transportSettings: this._transportSettings,
697
711
  transportTimeout: this._transportTimeout,
712
+ transportFactory: this._transportFactory,
698
713
  defaultTransactionTimeout: this.defaultTransactionTimeout
699
714
  });
700
715
  secureChannel.on("backoff", (count: number, delay: number) => {
@@ -773,7 +788,7 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
773
788
  }
774
789
 
775
790
  static async createCertificate(
776
- clientCertificateManager: OPCUACertificateManager,
791
+ clientCertificateManager: ICertificateStore,
777
792
  certificateFile: string,
778
793
  applicationName: string,
779
794
  applicationUri: string
@@ -781,7 +796,11 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
781
796
  if (!fs.existsSync(certificateFile)) {
782
797
  const hostname = getHostname();
783
798
  // this.serverInfo.applicationUri!;
784
- await clientCertificateManager.createSelfSignedCertificate({
799
+ // Cast is safe: createDefaultCertificate only calls
800
+ // this in the disk path, where the manager is always
801
+ // an OPCUACertificateManager.
802
+ const cm = clientCertificateManager as unknown as OPCUACertificateManager;
803
+ await cm.createSelfSignedCertificate({
785
804
  applicationUri,
786
805
  dns: [hostname],
787
806
  // ip: await getIpAddresses(),
@@ -819,7 +838,9 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
819
838
  this._getBuiltApplicationUri()
820
839
  );
821
840
  debugLog("privateKey = ", this.privateKeyFile);
822
- debugLog(" = ", this.clientCertificateManager.privateKey);
841
+ if ("privateKey" in this.clientCertificateManager) {
842
+ debugLog(" = ", (this.clientCertificateManager as unknown as OPCUACertificateManager).privateKey);
843
+ }
823
844
  debugLog("certificateFile = ", this.certificateFile);
824
845
  const _certificate = this.getCertificate();
825
846
  const _privateKey = this.getPrivateKey();
@@ -847,10 +868,16 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
847
868
  return;
848
869
  }
849
870
  await this.clientCertificateManager.initialize();
850
- await this.createDefaultCertificate();
851
- // c8 ignore next
852
- if (!fs.existsSync(this.privateKeyFile)) {
853
- throw new Error(` cannot locate private key file ${this.privateKeyFile}`);
871
+
872
+ if (this.certificateFile === "<in-memory>" || this.certificateFile === "<unknown>") {
873
+ // In-memory provider — cert already available, skip disk operations
874
+ } else {
875
+ // Disk path — create default cert if missing
876
+ await this.createDefaultCertificate();
877
+ // c8 ignore next
878
+ if (!fs.existsSync(this.privateKeyFile)) {
879
+ throw new Error(` cannot locate private key file ${this.privateKeyFile}`);
880
+ }
854
881
  }
855
882
  if (this.isUnusable()) return;
856
883
 
@@ -1001,7 +1028,10 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
1001
1028
  // this may happen if the Server has closed the connection abruptly for some unknown reason
1002
1029
  // or if the tcp connection has been broken.
1003
1030
  callback(
1004
- new Error("performMessageTransaction: No SecureChannel , connection may have been canceled abruptly by server")
1031
+ new Error(
1032
+ "performMessageTransaction: No SecureChannel , connection may have been canceled abruptly by server" +
1033
+ " while performing " + request.schema.name
1034
+ )
1005
1035
  );
1006
1036
  return;
1007
1037
  }
@@ -1479,7 +1509,9 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
1479
1509
  })
1480
1510
  .catch((err1: Error) => {
1481
1511
  warningLog("[NODE-OPCUA-W25] client's server certificate verification has failed ", err1.message);
1482
- warningLog(" clientCertificateManager.rootDir = ", this.clientCertificateManager.rootDir);
1512
+ if ("rootDir" in this.clientCertificateManager) {
1513
+ warningLog(" clientCertificateManager.rootDir = ", (this.clientCertificateManager as unknown as { rootDir: string }).rootDir);
1514
+ }
1483
1515
 
1484
1516
  const chain = split_der(endpoint.serverCertificate);
1485
1517
  warningLog(` server certificate chain contains ${chain.length} element(s)`);
@@ -1487,7 +1519,7 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
1487
1519
  const c = chain[i];
1488
1520
  const thumbprint = makeSHA1Thumbprint(c).toString("hex");
1489
1521
  try {
1490
- const { exploreCertificate } = require("node-opcua-crypto");
1522
+ const { exploreCertificate } = require("node-opcua-crypto/web");
1491
1523
  const info = exploreCertificate(c);
1492
1524
  const tbs = info.tbsCertificate;
1493
1525
  const cn = tbs.subject?.commonName ?? "unknown";
@@ -1515,7 +1547,9 @@ export class ClientBaseImpl<Events extends OPCUAClientBaseEvents = OPCUAClientBa
1515
1547
  warningLog(
1516
1548
  " verify also that the issuer certificate is trusted and the issuer's certificate is present in the issuer.cert folder\n" +
1517
1549
  " of the client certificate manager located in ",
1518
- this.clientCertificateManager.rootDir
1550
+ "rootDir" in this.clientCertificateManager
1551
+ ? (this.clientCertificateManager as unknown as { rootDir: string }).rootDir
1552
+ : "<in-memory>"
1519
1553
  );
1520
1554
  } else {
1521
1555
  warningLog(
@@ -2,7 +2,7 @@
2
2
  * @module node-opcua-client-private
3
3
  */
4
4
 
5
- import { createPublicKey, randomBytes } from "node:crypto";
5
+ import { createPublicKey } from "node:crypto";
6
6
  import { callbackify } from "node:util";
7
7
  import chalk from "chalk";
8
8
 
@@ -47,7 +47,7 @@ import {
47
47
  } from "node-opcua-service-session";
48
48
  import { type Callback, type CallbackT, type StatusCode, StatusCodes } from "node-opcua-status-code";
49
49
  import type { SignatureDataOptions, UserIdentityToken } from "node-opcua-types";
50
- import { isNullOrUndefined, matchUri } from "node-opcua-utils";
50
+ import { isNullOrUndefined, matchUri, randomBytes } from "node-opcua-utils";
51
51
  import type { NodeId } from "node-opcua-nodeid";
52
52
  import type { OPCUAClientBaseEvents } from "../client_base";
53
53
  import type { ClientSession } from "../client_session";
package/source/verify.ts CHANGED
@@ -1,9 +1,7 @@
1
- import type { OPCUACertificateManager } from "node-opcua-certificate-manager";
2
- import type { OPCUASecureObject } from "node-opcua-common";
1
+ import type { ICertificateStore, OPCUASecureObject } from "node-opcua-common";
3
2
 
4
3
  import { type Certificate, exploreCertificate, explorePrivateKey, publicKeyAndPrivateKeyMatches } from "node-opcua-crypto/web";
5
4
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
6
- import type { VerifyCertificateOptions } from "node-opcua-pki";
7
5
 
8
6
  const _doDebug = checkDebugFlag(__filename);
9
7
  const _debugLog = make_debugLog(__filename);
@@ -117,7 +115,7 @@ export function verifyIsOPCUAValidCertificate(
117
115
  export async function performCertificateSanityCheck(
118
116
  secureObject: OPCUASecureObject,
119
117
  serverOrClient: "server" | "client",
120
- certificateManager: OPCUACertificateManager,
118
+ certificateStore: ICertificateStore,
121
119
  applicationUri: string
122
120
  ): Promise<void> {
123
121
  // verify that certificate is matching private key, and inform the developer if not
@@ -128,7 +126,6 @@ export async function performCertificateSanityCheck(
128
126
  errorLog("[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match !");
129
127
  errorLog(" please check the configuration of the OPCUA Server");
130
128
  errorLog(" privateKey= ", secureObject.privateKeyFile);
131
- errorLog(" certificateManager.privateKey= ", certificateManager.privateKey);
132
129
  errorLog(" certificateFile= ", secureObject.certificateFile);
133
130
  throw new Error(
134
131
  "[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match ! please fix your configuration"
@@ -145,13 +142,11 @@ export async function performCertificateSanityCheck(
145
142
  );
146
143
  }
147
144
 
148
- const options: VerifyCertificateOptions = {
149
- acceptOutdatedCertificate: false,
150
- acceptOutDatedIssuerCertificate: false,
151
- acceptPendingCertificate: false
145
+ const options = {
146
+ acceptOutdatedCertificate: false
152
147
  };
153
148
 
154
- const status = await certificateManager.verifyCertificate(certificate, options);
149
+ const status = await certificateStore.verifyCertificate(certificate, options);
155
150
 
156
151
  // BadCertificateUntrusted is expected for the application's own
157
152
  // certificate — it does not need to be in its own trust list.