OPC UA secure client cannot connect: application certificate is never configured #121
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during lmxopcua#782 OPC UA SDK 2.0 interoperability validation. This is reproduced against BOTH the original OtOpcUa SDK 1.5.378.182 server artifact and the candidate SDK 2.0.1 server; it is a pre-existing consumer limitation, not a server-upgrade regression.
ScadaBridge source
0e89966da6, actual OpcUaDataConnection -> RealOpcUaClient, pinned OPC Foundation SDK 1.5.378.106. A separate process used task-owned issuer/trusted/rejected stores and explicit server DER trust, AutoAcceptUntrustedCerts=false. The None/API-key consumed subset passed 9 cases against each server (raw/UNS scalar and Int32[] round trips, durable browsing, monitoring quality/time, alarm filtering/refresh, reconnect). The same actual adapter fails SignAndEncrypt against both server versions before activation:ApplicationCertificate for the security profile http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256 cannot be found.(BadConfigurationError, 0x80890000).RealOpcUaClient.ConnectAsync constructs
ApplicationCertificate = new CertificateIdentifier()without an own-store/subject/application-certificate provisioning path. OpcUaGlobalOptions exposes only issuer/trusted/rejected stores. The corresponding endpoint probe builds the same empty application identifier. Do not solve this by enabling autoaccept or weakening the server profile.Implement configurable application identity/certificate ownership (own store, subject and URI), provision/load the appropriate key/certificate through the pinned SDK, retain strict server and mutual-client trust, and dispose owned resources. Add actual trusted Sign and SignAndEncrypt allow/deny/reconnect tests through both ConnectAsync and the endpoint probe. Existing None behavior must remain supported. Verify existing persisted PKI is reused rather than silently rotated.
Reproduction harness will be checked into lmxopcua under scripts/validation/scadabridge-sdk-compatibility; local original evidence: artifacts/opcua2/1840b5cc/scadabridge-matrix1/{baseline,candidate}-SignAndEncrypt/consumer/result.json. These are local artifacts, not a deployment. This blocks claiming secure ScadaBridge interoperability; lmxopcua#782 remains open for remaining gates. No wonder-box remote execution or deployment was performed.
Fixed in
167d04cc(localmain, not pushed, not deployed).Root cause — as reported: both
RealOpcUaClient.ConnectAsyncandVerifyEndpointAsyncbuilt an emptyCertificateIdentifier. A second defect sat on the same path:CertStoreActorwrote operator-trusted server certs to the trusted-store root, but the SDK reads{store}/certs/. So a Central UI Trust never affected validation, and the certs the SDK rejected never showed up in the list.Change
OpcUaApplicationIdentitybuilds the client config for both paths. Sign/SignAndEncrypt load the app cert from a configurable own store, found by subject, carrying the ApplicationUri. A self-signed RSA-2048 cert is created only when none exists, and creation is serialised per store and logged at Warning with the thumbprint. A persisted cert is reused and never rotated: if it no longer validates (e.g. URI mismatch), the connect fails.Nonenever touches the own store. AutoAccept is unchanged and the probe still forces it off.ScadaBridge:OpcUakeys:ApplicationUri,ApplicationCertificateSubject,OwnCertificateStorePath,AutoCreateApplicationCertificate,ApplicationCertificateLifetimeMonths.CertStoreActornow usescerts/and moves legacy root-level DERs into it on start.docker/,docker-env2/) keep their PKI on/app/data/pki/*with a pinned URI.Tests: an in-process strict-trust
StandardServerdrives the real adapter throughConnectAsyncand the endpoint probe, for Sign and SignAndEncrypt:BadCertificateUntrustedwhen the client doesn't trust the serverBadSecurityChecksFailedwhen the server doesn't trust the clientNoneleaves the own store untouchedThe same tests run against the pre-fix client fail with this issue's exact error. DCL 438, SiteRuntime 1000 and Host 597 tests all pass.
Remaining before close: push, rig redeploy, and a re-run of the lmxopcua#782
scadabridge-sdk-compatibilitySignAndEncrypt matrix against this build. Operator note: the client cert DER to hand to servers is in{OwnCertificateStorePath}/certs/.Pushed (
ff835447, rebased onto current main, plus follow-up36c64a6b) and deployed to the docker rig. All 8 nodes are healthy and the pending central migrations ran: the AuditLog clustered-key flip took ~17 min on the rig's first boot of this build.Rig live gate: site →
opc-plc(scadabridge-opcua:50000), SignAndEncrypt, AutoAccept offUntrustedCertificate, with opc-plc's cert0EF03980…captured. Before the fix this step failed withBadConfigurationError. site-a's active node created its identity in/app/data/pki/own.data-connection certs trustwrote the DER to/app/data/pki/trusted/certs/on both site-a nodes, which is where the SDK reads it.success: true, twice.deploy.sh), site-a still had the same single identityCCD0781F…and the same trust, and the probe still succeeded, so nothing rotated.Found on the rig, fixed in
36c64a6b: the site'sDataConnectionManagerActorpassed the probe aNullLogger. A first probe is usually what creates a node's identity, so the creation Warning was being discarded. It now reaches the node log, e.g. on site-b:OPC UA application certificate CREATED: … thumbprint 7C465F1E…, ApplicationUri urn:scadabridge-site-b-node-a:ScadaBridge-DCL, store /app/data/pki/own …. A test pins that it is logged once, not on reuse.Note: the SDK appends
O=OPC Foundationto an auto-created certificate's subject. This is cosmetic, and lookup still works because the configured subject's CN matches.Still open: re-run the lmxopcua#782
scadabridge-sdk-compatibilitySignAndEncrypt matrix against this build, then close. QA (vq03/vq04) and DEV (vd03) are not deployed.