Phase 3 PR 70 -- Apply SecurityPolicy explicitly + expand to standard OPC UA policy list. Before this PR SecurityPolicy was a string field that got ignored -- the driver only passed useSecurity=SecurityMode!=None to SelectEndpointAsync, so an operator asking for Basic256Sha256 on a server that also advertised Basic128Rsa15 could silently end up on the weaker cipher (the SDK's SelectEndpoint returns whichever matching endpoint the server listed first). PR 70 makes policy matching explicit. SecurityPolicy is now an OpcUaSecurityPolicy enum covering the six standard policies documented in OPC UA 1.04: None, Basic128Rsa15 (deprecated, brownfield interop only), Basic256 (deprecated), Basic256Sha256 (recommended baseline), Aes128_Sha256_RsaOaep, Aes256_Sha256_RsaPss. Each maps through MapSecurityPolicy to the SecurityPolicies URI constant the SDK uses for endpoint matching. New SelectMatchingEndpointAsync replaces CoreClientUtils.SelectEndpointAsync. Flow: opens a DiscoveryClient via the non-obsolete DiscoveryClient.CreateAsync(ApplicationConfiguration, Uri, DiagnosticsMasks, ct) path, calls GetEndpointsAsync to enumerate every endpoint the server advertises, filters client-side by policy URI AND mode. When no endpoint matches, throws InvalidOperationException with the full list of what the server DID advertise formatted as 'Policy/Mode' pairs so the operator sees exactly what to fix in their config without a Wireshark trace. Fail-loud behaviour intentional -- a silent fall-through to weaker crypto is worse than a clear config error. MapSecurityPolicy is internal-visible to tests via InternalsVisibleTo from PR 66. Unit tests (OpcUaClientSecurityPolicyTests, 5 facts): MapSecurityPolicy_returns_known_non_empty_uri_for_every_enum_value theory covers all 6 policies; URI contains the enum name for non-None so operators can grep logs back to the config value; MapSecurityPolicy_None_matches_SDK_None_URI, MapSecurityPolicy_Basic256Sha256_matches_SDK_URI, MapSecurityPolicy_Aes256_Sha256_RsaPss_matches_SDK_URI all cross-check against the SDK's SecurityPolicies.* constants to catch a future enum-vs-URI drift; Every_enum_value_has_a_mapping walks Enum.GetValues to ensure adding a new case doesn't silently fall through the switch. Scaffold test updated to assert SecurityPolicy default = None (was previously unchecked). 23/23 OpcUaClient.Tests pass (13 prior + 5 scaffold + 5 new policy). dotnet build clean. Note on DiscoveryClient: the synchronous DiscoveryClient.Create(...) overloads are all [Obsolete] in SDK 1.5.378; must use DiscoveryClient.CreateAsync. GetEndpointsAsync(null, ct) returns EndpointDescriptionCollection directly (not a wrapper).
This commit is contained in:
@@ -16,8 +16,16 @@ public sealed class OpcUaClientDriverOptions
|
||||
/// <summary>Remote OPC UA endpoint URL, e.g. <c>opc.tcp://plc.internal:4840</c>.</summary>
|
||||
public string EndpointUrl { get; init; } = "opc.tcp://localhost:4840";
|
||||
|
||||
/// <summary>Security policy. One of <c>None</c>, <c>Basic256Sha256</c>, <c>Aes128_Sha256_RsaOaep</c>.</summary>
|
||||
public string SecurityPolicy { get; init; } = "None";
|
||||
/// <summary>
|
||||
/// Security policy to require when selecting an endpoint. Either a
|
||||
/// <see cref="OpcUaSecurityPolicy"/> enum constant or a free-form string (for
|
||||
/// forward-compatibility with future OPC UA policies not yet in the enum).
|
||||
/// Matched against <c>EndpointDescription.SecurityPolicyUri</c> suffix — the driver
|
||||
/// connects to the first endpoint whose policy name matches AND whose mode matches
|
||||
/// <see cref="SecurityMode"/>. When set to <see cref="OpcUaSecurityPolicy.None"/>
|
||||
/// the driver picks any unsecured endpoint regardless of policy string.
|
||||
/// </summary>
|
||||
public OpcUaSecurityPolicy SecurityPolicy { get; init; } = OpcUaSecurityPolicy.None;
|
||||
|
||||
/// <summary>Security mode.</summary>
|
||||
public OpcUaSecurityMode SecurityMode { get; init; } = OpcUaSecurityMode.None;
|
||||
@@ -96,6 +104,33 @@ public enum OpcUaSecurityMode
|
||||
SignAndEncrypt,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OPC UA security policies recognized by the driver. Maps to the standard
|
||||
/// <c>http://opcfoundation.org/UA/SecurityPolicy#</c> URI suffixes the SDK uses for
|
||||
/// endpoint matching.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="Basic128Rsa15"/> and <see cref="Basic256"/> are <b>deprecated</b> per OPC UA
|
||||
/// spec v1.04 — they remain in the enum only for brownfield interop with older servers.
|
||||
/// Prefer <see cref="Basic256Sha256"/>, <see cref="Aes128_Sha256_RsaOaep"/>, or
|
||||
/// <see cref="Aes256_Sha256_RsaPss"/> for new deployments.
|
||||
/// </remarks>
|
||||
public enum OpcUaSecurityPolicy
|
||||
{
|
||||
/// <summary>No security. Unsigned, unencrypted wire.</summary>
|
||||
None,
|
||||
/// <summary>Deprecated (OPC UA 1.04). Retained for legacy server interop.</summary>
|
||||
Basic128Rsa15,
|
||||
/// <summary>Deprecated (OPC UA 1.04). Retained for legacy server interop.</summary>
|
||||
Basic256,
|
||||
/// <summary>Recommended baseline for current deployments.</summary>
|
||||
Basic256Sha256,
|
||||
/// <summary>Current OPC UA policy; AES-128 + SHA-256 + RSA-OAEP.</summary>
|
||||
Aes128_Sha256_RsaOaep,
|
||||
/// <summary>Current OPC UA policy; AES-256 + SHA-256 + RSA-PSS.</summary>
|
||||
Aes256_Sha256_RsaPss,
|
||||
}
|
||||
|
||||
/// <summary>User authentication type sent to the remote server.</summary>
|
||||
public enum OpcUaAuthType
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user