refactor: migrate OPC UA driver and browser to SDK 2.0

This commit is contained in:
Joseph Doherty
2026-11-10 10:08:12 -05:00
parent d2076e75c6
commit b583840fbc
10 changed files with 171 additions and 113 deletions
@@ -1,4 +1,5 @@
using Opc.Ua;
using Opc.Ua.Security.Certificates;
using ZB.MOM.WW.Secrets.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
@@ -37,7 +38,37 @@ public static class OpcUaUserIdentityFactory
/// </summary>
/// <param name="options">Driver options containing certificate configuration.</param>
/// <returns>The certificate-backed <see cref="UserIdentity"/>.</returns>
public static UserIdentity FromCertificate(OpcUaClientDriverOptions options) => new(LoadCertificate(options));
public static UserIdentity FromCertificate(OpcUaClientDriverOptions options)
{
using var certificate = LoadCertificate(options);
var identifier = new CertificateIdentifier { Thumbprint = certificate.Thumbprint };
return new UserIdentity(new X509IdentityTokenHandler(identifier,
new CertificatePasswordProvider(), new FileCertificateProvider(options, certificate.Thumbprint)));
}
// The configured credential is a PFX file, rather than an SDK certificate store. SDK 2 resolves
// signing keys on demand and disposes each returned Certificate. Pin the public identity for this
// lifecycle; a file/secret rotation is picked up only by the next initialise/open.
private sealed class FileCertificateProvider(OpcUaClientDriverOptions options, string thumbprint) : ICertificateProvider
{
public Certificate? TryGetPrivateKeyCertificate(string requestedThumbprint) => null;
public ValueTask<Certificate?> GetPrivateKeyCertificateAsync(CertificateIdentifier identifier,
ICertificatePasswordProvider? passwordProvider = null, string? applicationUri = null,
CancellationToken ct = default)
{
ct.ThrowIfCancellationRequested();
var certificate = LoadCertificate(options);
if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase)
|| !string.Equals(identifier.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase))
{
certificate.Dispose();
throw new ServiceResultException(StatusCodes.BadIdentityTokenInvalid,
"The user certificate changed; reopen the session to use the rotated credential.");
}
return ValueTask.FromResult<Certificate?>(Certificate.From(certificate));
}
}
/// <summary>
/// Loads (and validates) the user-authentication certificate <see cref="FromCertificate"/> wraps. The caller owns