node-opcua-client 2.169.0 → 2.172.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/client_base.d.ts +20 -4
- package/dist/client_base.js.map +1 -1
- package/dist/client_session.d.ts +3 -3
- package/dist/client_session.js.map +1 -1
- package/dist/client_session_keepalive_manager.d.ts +2 -0
- package/dist/client_session_keepalive_manager.js +60 -5
- package/dist/client_session_keepalive_manager.js.map +1 -1
- package/dist/private/client_base_impl.d.ts +4 -4
- package/dist/private/client_base_impl.js +48 -16
- package/dist/private/client_base_impl.js.map +1 -1
- package/dist/verify.d.ts +2 -3
- package/dist/verify.js +3 -6
- package/dist/verify.js.map +1 -1
- package/package.json +44 -43
- package/source/client_base.ts +22 -4
- package/source/client_session.ts +19 -22
- package/source/client_session_keepalive_manager.ts +71 -11
- package/source/private/client_base_impl.ts +56 -22
- package/source/verify.ts +5 -10
|
@@ -12,9 +12,19 @@ import type { DataValue } from "node-opcua-data-value";
|
|
|
12
12
|
import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
|
|
13
13
|
import { coerceNodeId } from "node-opcua-nodeid";
|
|
14
14
|
import { ClientSecureChannelLayer } from "node-opcua-secure-channel";
|
|
15
|
+
import type { StatusCode } from "node-opcua-status-code";
|
|
16
|
+
import { StatusCodes } from "node-opcua-status-code";
|
|
15
17
|
import type { ClientSessionImpl } from "./private/client_session_impl";
|
|
16
18
|
import type { IClientBase } from "./private/i_private_client";
|
|
17
19
|
|
|
20
|
+
interface ServiceFaultAnnotatedError extends Error {
|
|
21
|
+
response?: {
|
|
22
|
+
responseHeader?: {
|
|
23
|
+
serviceResult?: StatusCode;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
const serverStatusStateNodeId = coerceNodeId(VariableIds.Server_ServerStatus_State);
|
|
19
29
|
|
|
20
30
|
const debugLog = make_debugLog(__filename);
|
|
@@ -24,14 +34,18 @@ const warningLog = make_warningLog(__filename);
|
|
|
24
34
|
export interface ClientSessionKeepAliveManagerEvents {
|
|
25
35
|
on(event: "keepalive", eventHandler: (lastKnownServerState: ServerState, count: number) => void): this;
|
|
26
36
|
on(event: "failure", eventHandler: () => void): this;
|
|
37
|
+
on(event: "keepalive_failure", eventHandler: () => void): this;
|
|
27
38
|
}
|
|
28
39
|
|
|
40
|
+
const maxBackoffInterval = 60_000;
|
|
41
|
+
|
|
29
42
|
export class ClientSessionKeepAliveManager extends EventEmitter implements ClientSessionKeepAliveManagerEvents {
|
|
30
43
|
private readonly session: ClientSessionImpl;
|
|
31
44
|
private timerId?: NodeJS.Timeout;
|
|
32
45
|
private pingTimeout: number;
|
|
33
46
|
private lastKnownState?: ServerState;
|
|
34
47
|
private transactionInProgress = false;
|
|
48
|
+
private consecutiveFailures = 0;
|
|
35
49
|
public count = 0;
|
|
36
50
|
public checkInterval: number;
|
|
37
51
|
|
|
@@ -86,7 +100,9 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
|
|
|
86
100
|
return; // stop here
|
|
87
101
|
}
|
|
88
102
|
if (this.timerId) {
|
|
89
|
-
|
|
103
|
+
// When delta exceeds checkInterval it is an explicit backoff requested by _ping_server;
|
|
104
|
+
// otherwise delta is the time already consumed this cycle.
|
|
105
|
+
const timeout = delta > this.checkInterval ? delta : Math.max(1, this.checkInterval - delta);
|
|
90
106
|
this.timerId = setTimeout(() => this.ping_server(), timeout);
|
|
91
107
|
}
|
|
92
108
|
});
|
|
@@ -153,25 +169,68 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
|
|
|
153
169
|
(err: Error | null, dataValue?: DataValue) => {
|
|
154
170
|
this.transactionInProgress = false;
|
|
155
171
|
|
|
156
|
-
if (err
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
172
|
+
if (err) {
|
|
173
|
+
warningLog(chalk.cyan(" warning : ClientSessionKeepAliveManager#ping_server "), chalk.yellow(err.message));
|
|
174
|
+
const serviceFaultResponse = (err as ServiceFaultAnnotatedError).response;
|
|
175
|
+
if (serviceFaultResponse) {
|
|
176
|
+
const sc = serviceFaultResponse.responseHeader?.serviceResult;
|
|
177
|
+
if (sc?.equals(StatusCodes.BadSessionIdInvalid) || sc?.equals(StatusCodes.BadSessionClosed)) {
|
|
178
|
+
this.emit("failure");
|
|
179
|
+
warningLog(
|
|
180
|
+
"Keep alive has failed, considering a network outage is in place, forcing a reconnection"
|
|
181
|
+
);
|
|
182
|
+
terminateConnection(session._client);
|
|
183
|
+
resolve(0);
|
|
184
|
+
} else {
|
|
185
|
+
if (sc?.equals(StatusCodes.BadInvalidTimestamp)) {
|
|
186
|
+
// BadInvalidTimestamp (OPC UA Part 4 7.38.2, Table 178:
|
|
187
|
+
// "The timestamp is outside the range allowed by the Server")
|
|
188
|
+
// refers to the timestamp field of the RequestHeader
|
|
189
|
+
// (OPC UA Part 4 7.32), which the spec states is used
|
|
190
|
+
// "only for diagnostic and logging purposes in the Server".
|
|
191
|
+
//
|
|
192
|
+
// The server responded at the OPC UA application layer:
|
|
193
|
+
// the SecureChannel and Session are intact. The cause is
|
|
194
|
+
// clock skew between client and server; this is an
|
|
195
|
+
// infrastructure concern outside the scope of the keepalive
|
|
196
|
+
// manager.
|
|
197
|
+
//
|
|
198
|
+
// Treating this as a keepalive failure is semantically
|
|
199
|
+
// incorrect: the round-trip succeeded. Incrementing
|
|
200
|
+
// consecutiveFailures leads to unbounded exponential backoff
|
|
201
|
+
// and eventual session expiry server-side, triggering an
|
|
202
|
+
// unnecessary reconnect loop.
|
|
203
|
+
//
|
|
204
|
+
// See: https://reference.opcfoundation.org/Core/Part4/v105/docs/7.38.2
|
|
205
|
+
// https://reference.opcfoundation.org/Core/Part4/v105/docs/7.32
|
|
206
|
+
this.consecutiveFailures = 0;
|
|
207
|
+
debugLog("emit keepalive (BadInvalidTimestamp: session alive, clock skew on request timestamp)");
|
|
208
|
+
this.emit("keepalive", this.lastKnownState ?? ServerState.Unknown, this.count);
|
|
209
|
+
resolve(0);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.consecutiveFailures++;
|
|
213
|
+
warningLog("Keep alive received ServiceFault from server (session intact):", sc?.toString());
|
|
214
|
+
this.emit("keepalive_failure");
|
|
215
|
+
resolve(Math.min(this.checkInterval * 2 ** this.consecutiveFailures, maxBackoffInterval));
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
this.emit("failure");
|
|
219
|
+
warningLog("Keep alive has failed, considering a network outage is in place, forcing a reconnection");
|
|
220
|
+
terminateConnection(session._client);
|
|
221
|
+
resolve(0);
|
|
162
222
|
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (!dataValue || !dataValue.value) {
|
|
163
226
|
/**
|
|
164
227
|
* @event failure
|
|
165
228
|
* raised when the server is not responding or is responding with en error to
|
|
166
229
|
* the keep alive read Variable value transaction
|
|
167
230
|
*/
|
|
168
231
|
this.emit("failure");
|
|
169
|
-
|
|
170
|
-
// also simulate a connection by closing the channel abruptly from our end ...
|
|
171
232
|
warningLog("Keep alive has failed, considering a network outage is in place, forcing a reconnection");
|
|
172
|
-
|
|
173
233
|
terminateConnection(session._client);
|
|
174
|
-
|
|
175
234
|
resolve(0);
|
|
176
235
|
return;
|
|
177
236
|
}
|
|
@@ -190,6 +249,7 @@ export class ClientSessionKeepAliveManager extends EventEmitter implements Clien
|
|
|
190
249
|
this.lastKnownState = newState;
|
|
191
250
|
this.count++; // increase successful counter
|
|
192
251
|
}
|
|
252
|
+
this.consecutiveFailures = 0;
|
|
193
253
|
debugLog("emit keepalive");
|
|
194
254
|
this.emit("keepalive", this.lastKnownState, this.count);
|
|
195
255
|
resolve(0);
|
|
@@ -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,
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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(
|
|
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
|
-
|
|
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)`);
|
|
@@ -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
|
|
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(
|
package/source/verify.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import type {
|
|
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
|
-
|
|
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
|
|
149
|
-
acceptOutdatedCertificate: false
|
|
150
|
-
acceptOutDatedIssuerCertificate: false,
|
|
151
|
-
acceptPendingCertificate: false
|
|
145
|
+
const options = {
|
|
146
|
+
acceptOutdatedCertificate: false
|
|
152
147
|
};
|
|
153
148
|
|
|
154
|
-
const status = await
|
|
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.
|