node-opcua-client 2.180.0 → 2.181.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.
Files changed (87) hide show
  1. package/dist/alarms_and_conditions/client_alarm_tools.d.ts +1 -1
  2. package/dist/alarms_and_conditions/client_tools.js +7 -7
  3. package/dist/alarms_and_conditions/client_tools.js.map +1 -1
  4. package/dist/client_base.d.ts +2 -2
  5. package/dist/client_monitored_item.d.ts +2 -2
  6. package/dist/client_monitored_item_base.d.ts +1 -1
  7. package/dist/client_monitored_item_group.d.ts +2 -2
  8. package/dist/client_monitored_item_toolbox.d.ts +2 -2
  9. package/dist/client_monitored_item_toolbox.js +2 -2
  10. package/dist/client_monitored_item_toolbox.js.map +1 -1
  11. package/dist/client_session.d.ts +1 -1
  12. package/dist/client_session_keepalive_manager.d.ts +1 -1
  13. package/dist/client_session_keepalive_manager.js +3 -3
  14. package/dist/client_session_keepalive_manager.js.map +1 -1
  15. package/dist/client_subscription.d.ts +17 -4
  16. package/dist/client_subscription.js.map +1 -1
  17. package/dist/index.d.ts +17 -17
  18. package/dist/index.js +18 -18
  19. package/dist/index.js.map +1 -1
  20. package/dist/opcua_client.d.ts +4 -4
  21. package/dist/opcua_client.js +5 -5
  22. package/dist/opcua_client.js.map +1 -1
  23. package/dist/private/client_base_impl.d.ts +6 -6
  24. package/dist/private/client_base_impl.js +19 -16
  25. package/dist/private/client_base_impl.js.map +1 -1
  26. package/dist/private/client_monitored_item_group_impl.d.ts +3 -3
  27. package/dist/private/client_monitored_item_group_impl.js +9 -9
  28. package/dist/private/client_monitored_item_group_impl.js.map +1 -1
  29. package/dist/private/client_monitored_item_impl.d.ts +4 -4
  30. package/dist/private/client_monitored_item_impl.js +10 -10
  31. package/dist/private/client_monitored_item_impl.js.map +1 -1
  32. package/dist/private/client_publish_engine.d.ts +3 -3
  33. package/dist/private/client_publish_engine.js +3 -3
  34. package/dist/private/client_publish_engine.js.map +1 -1
  35. package/dist/private/client_session_impl.d.ts +6 -6
  36. package/dist/private/client_session_impl.js +12 -12
  37. package/dist/private/client_session_impl.js.map +1 -1
  38. package/dist/private/client_subscription_impl.d.ts +8 -8
  39. package/dist/private/client_subscription_impl.js +13 -13
  40. package/dist/private/client_subscription_impl.js.map +1 -1
  41. package/dist/private/i_private_client.d.ts +4 -4
  42. package/dist/private/opcua_client_impl.d.ts +7 -7
  43. package/dist/private/opcua_client_impl.js +58 -25
  44. package/dist/private/opcua_client_impl.js.map +1 -1
  45. package/dist/private/reconnection/client_publish_engine_reconnection.d.ts +1 -1
  46. package/dist/private/reconnection/client_publish_engine_reconnection.js +5 -5
  47. package/dist/private/reconnection/client_publish_engine_reconnection.js.map +1 -1
  48. package/dist/private/reconnection/client_reconnection.d.ts +1 -1
  49. package/dist/private/reconnection/client_subscription_reconnection.d.ts +1 -1
  50. package/dist/private/reconnection/client_subscription_reconnection.js +8 -5
  51. package/dist/private/reconnection/client_subscription_reconnection.js.map +1 -1
  52. package/dist/private/reconnection/reconnection.d.ts +3 -3
  53. package/dist/private/reconnection/reconnection.js +4 -4
  54. package/dist/private/reconnection/reconnection.js.map +1 -1
  55. package/dist/tools/findservers.js +3 -3
  56. package/dist/tools/findservers.js.map +1 -1
  57. package/dist/tools/read_history_server_capabilities.d.ts +1 -1
  58. package/dist/verify.js +60 -20
  59. package/dist/verify.js.map +1 -1
  60. package/package.json +48 -47
  61. package/source/alarms_and_conditions/client_alarm_tools.ts +3 -3
  62. package/source/alarms_and_conditions/client_tools.ts +1 -1
  63. package/source/client_base.ts +2 -2
  64. package/source/client_monitored_item.ts +2 -2
  65. package/source/client_monitored_item_base.ts +1 -1
  66. package/source/client_monitored_item_group.ts +2 -2
  67. package/source/client_monitored_item_toolbox.ts +8 -8
  68. package/source/client_session.ts +1 -1
  69. package/source/client_session_keepalive_manager.ts +5 -5
  70. package/source/client_subscription.ts +21 -4
  71. package/source/index.ts +17 -17
  72. package/source/opcua_client.ts +5 -5
  73. package/source/private/client_base_impl.ts +21 -13
  74. package/source/private/client_monitored_item_group_impl.ts +8 -8
  75. package/source/private/client_monitored_item_impl.ts +8 -8
  76. package/source/private/client_publish_engine.ts +8 -8
  77. package/source/private/client_session_impl.ts +13 -13
  78. package/source/private/client_subscription_impl.ts +11 -11
  79. package/source/private/i_private_client.ts +4 -4
  80. package/source/private/opcua_client_impl.ts +85 -30
  81. package/source/private/reconnection/client_publish_engine_reconnection.ts +5 -5
  82. package/source/private/reconnection/client_reconnection.ts +1 -1
  83. package/source/private/reconnection/client_subscription_reconnection.ts +8 -4
  84. package/source/private/reconnection/reconnection.ts +5 -5
  85. package/source/tools/findservers.ts +1 -1
  86. package/source/tools/read_history_server_capabilities.ts +1 -1
  87. package/source/verify.ts +75 -24
@@ -131,22 +131,22 @@ import type {
131
131
  SetMonitoringModeRequestLike,
132
132
  SubscriptionId,
133
133
  TransferSubscriptionsRequestLike
134
- } from "../client_session";
135
- import { ClientSessionKeepAliveManager } from "../client_session_keepalive_manager";
136
- import type { ClientSubscription } from "../client_subscription";
137
- import type { Request, Response } from "../common";
138
- import type { UserIdentityInfo } from "../user_identity_info";
134
+ } from "../client_session.js";
135
+ import { ClientSessionKeepAliveManager } from "../client_session_keepalive_manager.js";
136
+ import type { ClientSubscription } from "../client_subscription.js";
137
+ import type { Request, Response } from "../common.js";
138
+ import type { UserIdentityInfo } from "../user_identity_info.js";
139
139
 
140
- import { ClientSidePublishEngine } from "./client_publish_engine";
141
- import { ClientSubscriptionImpl } from "./client_subscription_impl";
142
- import type { IClientBase } from "./i_private_client";
143
- import { repair_client_session } from "./reconnection/reconnection";
140
+ import { ClientSidePublishEngine } from "./client_publish_engine.js";
141
+ import { ClientSubscriptionImpl } from "./client_subscription_impl.js";
142
+ import type { IClientBase } from "./i_private_client.js";
143
+ import { repair_client_session } from "./reconnection/reconnection.js";
144
144
 
145
145
  const helpAPIChange = process.env.DEBUG?.match(/API/);
146
- const debugLog = make_debugLog(__filename);
147
- const doDebug = checkDebugFlag(__filename);
148
- const warningLog = make_warningLog(__filename);
149
- const _errorLog = make_errorLog(__filename);
146
+ const debugLog = make_debugLog("client_session_impl");
147
+ const doDebug = checkDebugFlag("client_session_impl");
148
+ const warningLog = make_warningLog("client_session_impl");
149
+ const _errorLog = make_errorLog("client_session_impl");
150
150
 
151
151
  let pendingTransactionMessageDisplayed = false;
152
152
 
@@ -29,11 +29,11 @@ import {
29
29
  } from "node-opcua-service-subscription";
30
30
  import { type Callback, type ErrorCallback, type StatusCode, StatusCodes } from "node-opcua-status-code";
31
31
  import { isNullOrUndefined } from "node-opcua-utils";
32
- import type { ClientMonitoredItem } from "../client_monitored_item";
33
- import type { ClientMonitoredItemBase } from "../client_monitored_item_base";
34
- import type { ClientMonitoredItemGroup } from "../client_monitored_item_group";
35
- import { ClientMonitoredItemToolbox } from "../client_monitored_item_toolbox";
36
- import type { ClientSession, MonitoredItemData, SubscriptionId } from "../client_session";
32
+ import type { ClientMonitoredItem } from "../client_monitored_item.js";
33
+ import type { ClientMonitoredItemBase } from "../client_monitored_item_base.js";
34
+ import type { ClientMonitoredItemGroup } from "../client_monitored_item_group.js";
35
+ import { ClientMonitoredItemToolbox } from "../client_monitored_item_toolbox.js";
36
+ import type { ClientSession, MonitoredItemData, SubscriptionId } from "../client_session.js";
37
37
  import {
38
38
  type ClientHandle,
39
39
  type ClientMonitoredItemBaseMap,
@@ -41,17 +41,17 @@ import {
41
41
  type ClientSubscriptionOptions,
42
42
  type ModifySubscriptionOptions,
43
43
  type ModifySubscriptionResult
44
- } from "../client_subscription";
45
- import { ClientMonitoredItemGroupImpl } from "./client_monitored_item_group_impl";
46
- import { ClientMonitoredItemImpl } from "./client_monitored_item_impl";
47
- import type { ClientSidePublishEngine } from "./client_publish_engine";
48
- import type { ClientSessionImpl } from "./client_session_impl";
44
+ } from "../client_subscription.js";
45
+ import { ClientMonitoredItemGroupImpl } from "./client_monitored_item_group_impl.js";
46
+ import { ClientMonitoredItemImpl } from "./client_monitored_item_impl.js";
47
+ import type { ClientSidePublishEngine } from "./client_publish_engine.js";
48
+ import type { ClientSessionImpl } from "./client_session_impl.js";
49
49
 
50
50
  interface ModifySubscriptionOptionsEx extends ModifySubscriptionOptions {
51
51
  subscriptionId?: SubscriptionId;
52
52
  }
53
53
 
54
- import { detectLongOperation } from "./performance";
54
+ import { detectLongOperation } from "./performance.js";
55
55
 
56
56
  const debugLog = make_debugLog("CLIENT_SUBSCRIPTION");
57
57
  const doDebug = checkDebugFlag("CLIENT_SUBSCRIPTION");
@@ -2,10 +2,10 @@ import type { IBasicTransportSettings, ResponseCallback } from "node-opcua-pseud
2
2
  import type { ClientSecureChannelLayer } from "node-opcua-secure-channel";
3
3
  import type { EndpointDescription } from "node-opcua-service-endpoints";
4
4
 
5
- import type { ClientSession } from "../client_session";
6
- import type { Request, Response } from "../common";
7
- import type { UserIdentityInfo } from "../user_identity_info";
8
- import type { ClientSessionImpl } from "./client_session_impl";
5
+ import type { ClientSession } from "../client_session.js";
6
+ import type { Request, Response } from "../common.js";
7
+ import type { UserIdentityInfo } from "../user_identity_info.js";
8
+ import type { ClientSessionImpl } from "./client_session_impl.js";
9
9
 
10
10
  export interface IClientBase {
11
11
  __createSession_step2(session: ClientSessionImpl, callback: (err: Error | null, session?: ClientSessionImpl) => void): void;
@@ -13,6 +13,7 @@ import {
13
13
  combine_der,
14
14
  exploreCertificate,
15
15
  extractPublicKeyFromCertificateSync,
16
+ type IKeyOperations,
16
17
  makePrivateKeyFromPem,
17
18
  type Nonce,
18
19
  type PrivateKey,
@@ -25,7 +26,7 @@ import type { NodeId } from "node-opcua-nodeid";
25
26
  import { readNamespaceArray } from "node-opcua-pseudo-session";
26
27
  import {
27
28
  type ClientSecureChannelLayer,
28
- computeSignature,
29
+ computeSignatureAsync,
29
30
  fromURI,
30
31
  getCryptoFactory,
31
32
  SecurityPolicy
@@ -49,32 +50,45 @@ import {
49
50
  import { type Callback, type CallbackT, type StatusCode, StatusCodes } from "node-opcua-status-code";
50
51
  import type { SignatureDataOptions, UserIdentityToken } from "node-opcua-types";
51
52
  import { isNullOrUndefined, matchUri, randomBytes } from "node-opcua-utils";
52
- import type { OPCUAClientBaseEvents } from "../client_base";
53
- import type { ClientSession } from "../client_session";
54
- import type { ClientSubscriptionOptions } from "../client_subscription";
55
- import type { Response } from "../common";
53
+ import type { OPCUAClientBaseEvents } from "../client_base.js";
54
+ import type { ClientSession } from "../client_session.js";
55
+ import type { ClientSubscriptionOptions } from "../client_subscription.js";
56
+ import type { Response } from "../common.js";
56
57
  import {
57
58
  type EndpointWithUserIdentity,
58
59
  OPCUAClient,
59
60
  type OPCUAClientOptions,
60
61
  type WithSessionFuncP,
61
62
  type WithSubscriptionFuncP
62
- } from "../opcua_client";
63
- import type { AnonymousIdentity, UserIdentityInfo, UserIdentityInfoUserName, UserIdentityInfoX509 } from "../user_identity_info";
64
- import { ClientBaseImpl } from "./client_base_impl";
65
- import { ClientSessionImpl } from "./client_session_impl";
66
- import type { IClientBase } from "./i_private_client";
67
- import { repair_client_sessions } from "./reconnection/reconnection";
63
+ } from "../opcua_client.js";
64
+ import type { AnonymousIdentity, UserIdentityInfo, UserIdentityInfoUserName, UserIdentityInfoX509 } from "../user_identity_info.js";
65
+ import { ClientBaseImpl } from "./client_base_impl.js";
66
+ import { ClientSessionImpl } from "./client_session_impl.js";
67
+ import type { IClientBase } from "./i_private_client.js";
68
+ import { repair_client_sessions } from "./reconnection/reconnection.js";
68
69
 
69
70
  interface TokenAndSignature {
70
71
  userIdentityToken: UserIdentityToken | null;
71
72
  userTokenSignature: SignatureDataOptions;
72
73
  }
73
74
 
74
- const doDebug = checkDebugFlag(__filename);
75
- const debugLog = make_debugLog(__filename);
76
- const errorLog = make_errorLog(__filename);
77
- const warningLog = make_warningLog(__filename);
75
+ const doDebug = checkDebugFlag("opcua_client_impl");
76
+ const debugLog = make_debugLog("opcua_client_impl");
77
+ const errorLog = make_errorLog("opcua_client_impl");
78
+ const warningLog = make_warningLog("opcua_client_impl");
79
+
80
+ let x509PemPrivateKeyWarningEmitted = false;
81
+ function warnOnceAboutX509PemPrivateKey() {
82
+ if (x509PemPrivateKeyWarningEmitted) {
83
+ return;
84
+ }
85
+ x509PemPrivateKeyWarningEmitted = true;
86
+ warningLog(
87
+ "[NODE-OPCUA-W37] UserIdentityInfoX509.privateKey (raw PEM) is deprecated: pass keyOperations instead" +
88
+ " — keyOperationsFromPrivateKey(readPrivateKey(file)) from node-opcua-crypto is a one-line migration," +
89
+ " and an HSM/smartcard-held user key becomes possible."
90
+ );
91
+ }
78
92
 
79
93
  function validateServerNonce(serverNonce: Nonce | null): boolean {
80
94
  return !(serverNonce && serverNonce.length < 32) || (serverNonce && serverNonce.length === 0);
@@ -191,13 +205,14 @@ interface X509TokenAndSignature {
191
205
  *
192
206
  * @param context
193
207
  * @param certificate - the user certificate
194
- * @param privateKey - the private key associated with the user certificate
208
+ * @param userKey - the key associated with the user certificate: raw
209
+ * material, or an opaque IKeyOperations (HSM/smartcard)
195
210
  */
196
- function createX509IdentityToken(
211
+ async function createX509IdentityToken(
197
212
  context: IdentityTokenContext,
198
213
  certificate: Certificate,
199
- privateKey: PrivateKey
200
- ): X509TokenAndSignature {
214
+ userKey: PrivateKey | IKeyOperations
215
+ ): Promise<X509TokenAndSignature> {
201
216
  const endpoint = context.endpoint;
202
217
  assert(endpoint instanceof EndpointDescription);
203
218
  const userTokenPolicy = findUserTokenPolicy(endpoint, UserTokenType.Certificate);
@@ -260,8 +275,14 @@ function createX509IdentityToken(
260
275
  // now create the proof of possession, by creating a signature
261
276
  // The data to sign is created by appending the last serverNonce to the serverCertificate
262
277
 
263
- // The signature generated with private key associated with the User Certificate
264
- const userTokenSignature = computeSignature(serverCertificate, serverNonce, privateKey, securityPolicy) as SignatureDataOptions;
278
+ // The signature generated with the key associated with the User Certificate
279
+ // possibly produced inside an HSM/smartcard, when the user supplied keyOperations
280
+ const userTokenSignature = (await computeSignatureAsync(
281
+ serverCertificate,
282
+ serverNonce,
283
+ userKey,
284
+ securityPolicy
285
+ )) as SignatureDataOptions;
265
286
 
266
287
  return { userIdentityToken, userTokenSignature };
267
288
  }
@@ -965,12 +986,28 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
965
986
  serverNonce: serverNonce as Buffer // please check this !
966
987
  };
967
988
 
968
- this.createUserIdentityToken(context, userIdentityInfo, (err: Error | null, data?: TokenAndSignature | null) => {
989
+ this.createUserIdentityToken(context, userIdentityInfo, async (err: Error | null, data?: TokenAndSignature | null) => {
969
990
  if (err) {
970
991
  session._client = _old_client;
971
992
  return callback(err);
972
993
  }
973
994
 
995
+ // the client application signature is asynchronous now (the key
996
+ // may be HSM/KMS-held), so it is computed before the request
997
+ // object is assembled
998
+ let clientSignature: SignatureDataOptions | undefined;
999
+ try {
1000
+ clientSignature =
1001
+ (await this.computeClientSignature(
1002
+ this._secureChannel as ClientSecureChannelLayer,
1003
+ serverCertificate,
1004
+ serverNonce
1005
+ )) || undefined;
1006
+ } catch (signatureError) {
1007
+ session._client = _old_client;
1008
+ return callback(signatureError as Error);
1009
+ }
1010
+
974
1011
  data = data as TokenAndSignature;
975
1012
  const userIdentityToken: UserIdentityToken = data.userIdentityToken as UserIdentityToken;
976
1013
  const userTokenSignature: SignatureDataOptions = data.userTokenSignature as SignatureDataOptions;
@@ -981,9 +1018,7 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
981
1018
  // clientCertificate. The SignatureAlgorithm shall be the AsymmetricSignatureAlgorithm
982
1019
  // specified in the SecurityPolicy for the Endpoint. The SignatureData type is defined in 7.30.
983
1020
 
984
- clientSignature:
985
- this.computeClientSignature(this._secureChannel as ClientSecureChannelLayer, serverCertificate, serverNonce) ||
986
- undefined,
1021
+ clientSignature,
987
1022
 
988
1023
  // These are the SoftwareCertificates which have been issued to the Client application.
989
1024
  // The productUri contained in the SoftwareCertificates shall match the productUri in the
@@ -1218,7 +1253,14 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1218
1253
  * @private
1219
1254
  */
1220
1255
  private computeClientSignature(channel: ClientSecureChannelLayer, serverCertificate: Buffer, serverNonce: Nonce | undefined) {
1221
- return computeSignature(serverCertificate, serverNonce || Buffer.alloc(0), this.getPrivateKey(), channel.securityPolicy);
1256
+ // through the opaque key-operations path: works whether the client's
1257
+ // application instance key is local or HSM/KMS-held
1258
+ return computeSignatureAsync(
1259
+ serverCertificate,
1260
+ serverNonce || Buffer.alloc(0),
1261
+ this.getKeyOperations(),
1262
+ channel.securityPolicy
1263
+ );
1222
1264
  }
1223
1265
  /**
1224
1266
  *
@@ -1261,7 +1303,7 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1261
1303
 
1262
1304
  let userIdentityToken: UserIdentityToken;
1263
1305
 
1264
- let userTokenSignature: SignatureDataOptions = {
1306
+ const userTokenSignature: SignatureDataOptions = {
1265
1307
  algorithm: undefined,
1266
1308
  signature: undefined
1267
1309
  };
@@ -1281,9 +1323,22 @@ export class OPCUAClientImpl extends ClientBaseImpl<OPCUAClientBaseEvents> {
1281
1323
 
1282
1324
  case UserTokenType.Certificate: {
1283
1325
  const certificate = userIdentityInfo.certificateData;
1284
- const privateKey = makePrivateKeyFromPem(userIdentityInfo.privateKey);
1285
- ({ userIdentityToken, userTokenSignature } = createX509IdentityToken(context, certificate, privateKey));
1286
- break;
1326
+ if (!userIdentityInfo.privateKey === !userIdentityInfo.keyOperations) {
1327
+ return callback(
1328
+ new Error("CLIENT: a X509 user identity requires exactly one of 'privateKey' (PEM) or 'keyOperations'")
1329
+ );
1330
+ }
1331
+ if (userIdentityInfo.privateKey) {
1332
+ warnOnceAboutX509PemPrivateKey();
1333
+ }
1334
+ const userKey: PrivateKey | IKeyOperations = userIdentityInfo.keyOperations
1335
+ ? userIdentityInfo.keyOperations
1336
+ : makePrivateKeyFromPem(userIdentityInfo.privateKey as string);
1337
+ createX509IdentityToken(context, certificate, userKey).then(
1338
+ (x509Data) => callback(null, x509Data),
1339
+ (err: Error) => callback(err)
1340
+ );
1341
+ return;
1287
1342
  }
1288
1343
 
1289
1344
  default:
@@ -3,11 +3,11 @@ import assert from "node-opcua-assert";
3
3
  import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
4
4
  import { type StatusCode, StatusCodes } from "node-opcua-status-code";
5
5
  import { RepublishRequest, type RepublishResponse } from "node-opcua-types";
6
- import type { ClientSidePublishEngine } from "../client_publish_engine";
7
- import type { ClientSessionImpl } from "../client_session_impl";
8
- import type { ClientSubscriptionImpl } from "../client_subscription_impl";
9
- import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection";
10
- import { _shouldNotContinue2 } from "./reconnection";
6
+ import type { ClientSidePublishEngine } from "../client_publish_engine.js";
7
+ import type { ClientSessionImpl } from "../client_session_impl.js";
8
+ import type { ClientSubscriptionImpl } from "../client_subscription_impl.js";
9
+ import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection.js";
10
+ import { _shouldNotContinue2 } from "./reconnection.js";
11
11
 
12
12
  const debugLog = make_debugLog("RECONNECTION");
13
13
  const doDebug = checkDebugFlag("RECONNECTION");
@@ -1,4 +1,4 @@
1
- import type { ClientBaseImpl } from "../client_base_impl";
1
+ import type { ClientBaseImpl } from "../client_base_impl.js";
2
2
 
3
3
  export function waitUntilReconnectionIsCanceled(client: ClientBaseImpl, callback: () => void) {
4
4
  const interval = 100;
@@ -13,10 +13,10 @@ import {
13
13
  type CreateMonitoredItemsResponse,
14
14
  type MonitoredItemCreateRequestOptions
15
15
  } from "node-opcua-types";
16
- import type { ClientMonitoredItemBaseMap, ClientSubscription } from "../../client_subscription";
17
- import type { ClientMonitoredItemImpl } from "../client_monitored_item_impl";
18
- import { __create_subscription, type ClientSubscriptionImpl, TERMINATED_SUBSCRIPTION_ID } from "../client_subscription_impl";
19
- import { _shouldNotContinue } from "./reconnection";
16
+ import type { ClientMonitoredItemBaseMap, ClientSubscription } from "../../client_subscription.js";
17
+ import type { ClientMonitoredItemImpl } from "../client_monitored_item_impl.js";
18
+ import { __create_subscription, type ClientSubscriptionImpl, TERMINATED_SUBSCRIPTION_ID } from "../client_subscription_impl.js";
19
+ import { _shouldNotContinue } from "./reconnection.js";
20
20
 
21
21
  const debugLog = make_debugLog("RECONNECTION");
22
22
  const doDebug = checkDebugFlag("RECONNECTION");
@@ -65,6 +65,10 @@ export async function recreateSubscriptionAndMonitoredItem(_subscription: Client
65
65
  throw _err;
66
66
  }
67
67
 
68
+ if (subscription.subscriptionId !== oldSubscriptionId) {
69
+ subscription.emit("subscriptionId_changed", subscription.subscriptionId, oldSubscriptionId);
70
+ }
71
+
68
72
  const _test = subscription.publishEngine.getSubscription(subscription.subscriptionId);
69
73
 
70
74
  // c8 ignore next
@@ -8,11 +8,11 @@ import { invalidateExtraDataTypeManager } from "node-opcua-client-dynamic-extens
8
8
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
9
9
  import { TransferSubscriptionsRequest, type TransferSubscriptionsResponse } from "node-opcua-service-subscription";
10
10
  import { CloseSessionRequest } from "node-opcua-types";
11
- import type { ClientSessionImpl, Reconnectable } from "../client_session_impl";
12
- import type { ClientSubscriptionImpl } from "../client_subscription_impl";
13
- import type { IClientBase } from "../i_private_client";
14
- import { republish } from "./client_publish_engine_reconnection";
15
- import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection";
11
+ import type { ClientSessionImpl, Reconnectable } from "../client_session_impl.js";
12
+ import type { ClientSubscriptionImpl } from "../client_subscription_impl.js";
13
+ import type { IClientBase } from "../i_private_client.js";
14
+ import { republish } from "./client_publish_engine_reconnection.js";
15
+ import { recreateSubscriptionAndMonitoredItem } from "./client_subscription_reconnection.js";
16
16
 
17
17
  const debugLog = make_debugLog("RECONNECTION");
18
18
  const doDebug = checkDebugFlag("RECONNECTION");
@@ -5,7 +5,7 @@
5
5
  import type { ServerOnNetwork } from "node-opcua-service-discovery";
6
6
  import type { ApplicationDescription, EndpointDescription } from "node-opcua-service-endpoints";
7
7
 
8
- import { ClientBaseImpl } from "../private/client_base_impl";
8
+ import { ClientBaseImpl } from "../private/client_base_impl.js";
9
9
 
10
10
  export interface FindServerResults {
11
11
  servers: ApplicationDescription[];
@@ -11,7 +11,7 @@ import { type BrowsePath, type BrowsePathResult, makeBrowsePath } from "node-opc
11
11
  import { lowerFirstLetter } from "node-opcua-utils";
12
12
  import type { Variant } from "node-opcua-variant";
13
13
 
14
- import type { ClientSession } from "../client_session";
14
+ import type { ClientSession } from "../client_session.js";
15
15
 
16
16
  export interface HistoryServerCapabilities {
17
17
  [key: string]: Variant;
package/source/verify.ts CHANGED
@@ -1,12 +1,50 @@
1
+ import { createPublicKey } from "node:crypto";
1
2
  import type { ICertificateStore, OPCUASecureObject } from "node-opcua-common";
2
3
 
3
- import { type Certificate, exploreCertificate, explorePrivateKey, publicKeyAndPrivateKeyMatches } from "node-opcua-crypto/web";
4
+ import { PrivateKeyUnavailableError } from "node-opcua-crypto";
5
+ import {
6
+ type Certificate,
7
+ exploreCertificate,
8
+ explorePrivateKey,
9
+ extractPublicKeyFromCertificateSync,
10
+ type PrivateKey,
11
+ publicKeyAndPrivateKeyMatches
12
+ } from "node-opcua-crypto/web";
4
13
  import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
5
14
 
6
- const _doDebug = checkDebugFlag(__filename);
7
- const _debugLog = make_debugLog(__filename);
8
- const errorLog = make_errorLog(__filename);
9
- const warningLog = make_warningLog(__filename);
15
+ const _doDebug = checkDebugFlag("verify");
16
+ const _debugLog = make_debugLog("verify");
17
+ const errorLog = make_errorLog("verify");
18
+ const warningLog = make_warningLog("verify");
19
+
20
+ /**
21
+ * The opaque-key variant of the certificate/key match check: compares the
22
+ * certificate's SPKI against the one the key-operations provider reports.
23
+ * A provider without `getPublicKey` cannot be checked — that is worth a
24
+ * warning, not a refusal to start (the first real handshake will tell).
25
+ */
26
+ async function verifyOpaqueKeyMatchesCertificate(secureObject: OPCUASecureObject, certificate: Certificate): Promise<void> {
27
+ const keyOperations = secureObject.getKeyOperations();
28
+ if (!keyOperations.getPublicKey) {
29
+ warningLog(
30
+ "[NODE-OPCUA-W36] the key-operations provider does not implement getPublicKey():" +
31
+ " cannot verify that the certificate matches the (opaque) private key"
32
+ );
33
+ return;
34
+ }
35
+ const providerSpki = Buffer.from(await keyOperations.getPublicKey());
36
+ const certificateSpki = createPublicKey(extractPublicKeyFromCertificateSync(certificate)).export({
37
+ type: "spki",
38
+ format: "der"
39
+ });
40
+ if (!providerSpki.equals(certificateSpki)) {
41
+ errorLog("[NODE-OPCUA-E01] Configuration error : the certificate and the (opaque) private key do not match !");
42
+ errorLog(" certificateFile= ", secureObject.certificateFile);
43
+ throw new Error(
44
+ "[NODE-OPCUA-E01] Configuration error : the certificate does not carry the key-operations provider's public key ! please fix your configuration"
45
+ );
46
+ }
47
+ }
10
48
 
11
49
  export function verifyIsOPCUAValidCertificate(
12
50
  certificate: Certificate,
@@ -120,26 +158,39 @@ export async function performCertificateSanityCheck(
120
158
  ): Promise<void> {
121
159
  // verify that certificate is matching private key, and inform the developer if not
122
160
  const certificate = secureObject.getCertificate();
123
- const privateKey = secureObject.getPrivateKey();
124
- //
125
- if (!publicKeyAndPrivateKeyMatches(certificate, privateKey)) {
126
- errorLog("[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match !");
127
- errorLog(" please check the configuration of the OPCUA Server");
128
- errorLog(" privateKey= ", secureObject.privateKeyFile);
129
- errorLog(" certificateFile= ", secureObject.certificateFile);
130
- throw new Error(
131
- "[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match ! please fix your configuration"
132
- );
161
+ let privateKey: PrivateKey | undefined;
162
+ try {
163
+ privateKey = secureObject.getPrivateKey();
164
+ } catch (err) {
165
+ if (!(err instanceof PrivateKeyUnavailableError)) {
166
+ throw err;
167
+ }
168
+ // The key is opaque (HSM/KMS-held): no material to compare or
169
+ // measure. Check what can be checked without it that the
170
+ // certificate carries the provider's public key — and warn (not
171
+ // fail) when the provider cannot even produce that.
172
+ await verifyOpaqueKeyMatchesCertificate(secureObject, certificate);
133
173
  }
134
- // verify that the certificate provided has the right key length ( at least 2048)
135
- const privateKeyInfo = explorePrivateKey(privateKey);
136
- const keyLengthInBits = privateKeyInfo.modulus.length * 8;
137
- if (keyLengthInBits <= 1024) {
138
- warningLog(
139
- `[NODE-OPCUA-W04] The public/private key pair uses a key length which is equal or lower than 1024 bits. ( key length was ${keyLengthInBits} )\n` +
140
- `OPCUA version 1.04 requires that security key length are greater or equal to 2048 bits.\n` +
141
- `The ${serverOrClient} is operating at risk. `
142
- );
174
+ if (privateKey) {
175
+ if (!publicKeyAndPrivateKeyMatches(certificate, privateKey)) {
176
+ errorLog("[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match !");
177
+ errorLog(" please check the configuration of the OPCUA Server");
178
+ errorLog(" privateKey= ", secureObject.privateKeyFile);
179
+ errorLog(" certificateFile= ", secureObject.certificateFile);
180
+ throw new Error(
181
+ "[NODE-OPCUA-E01] Configuration error : the certificate and the private key do not match ! please fix your configuration"
182
+ );
183
+ }
184
+ // verify that the certificate provided has the right key length ( at least 2048)
185
+ const privateKeyInfo = explorePrivateKey(privateKey);
186
+ const keyLengthInBits = privateKeyInfo.modulus.length * 8;
187
+ if (keyLengthInBits <= 1024) {
188
+ warningLog(
189
+ `[NODE-OPCUA-W04] The public/private key pair uses a key length which is equal or lower than 1024 bits. ( key length was ${keyLengthInBits} )\n` +
190
+ `OPCUA version 1.04 requires that security key length are greater or equal to 2048 bits.\n` +
191
+ `The ${serverOrClient} is operating at risk. `
192
+ );
193
+ }
143
194
  }
144
195
 
145
196
  const options = {