using System.IdentityModel.Selectors; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Security; using AVEVA.Historian.Client; using AVEVA.Historian.Client.Wcf; using AVEVA.Historian.Client.Wcf.Contracts; namespace AVEVA.Historian.Client.Tests; public sealed class HistorianWcfCertOptionTests { private static HistorianClientOptions BaseOptions(bool allowUntrusted = false, string? dnsIdentity = null) => new() { Host = "10.0.0.1", Port = HistorianClientOptions.DefaultPort, Transport = HistorianTransport.RemoteTcpCertificate, IntegratedSecurity = false, UserName = "user", Password = "pass", AllowUntrustedServerCertificate = allowUntrusted, ServerDnsIdentity = dnsIdentity, }; [Fact] public void ClientCredentialsHelper_Disabled_LeavesValidationModeAtDefault() { Binding binding = HistorianWcfBindingFactory.CreateMdasNetTcpBinding(TimeSpan.FromSeconds(5)); ChannelFactory factory = new(binding, new EndpointAddress("net.tcp://10.0.0.1:32568/Hist")); try { HistorianWcfClientCredentialsHelper.Configure(factory, BaseOptions(allowUntrusted: false)); X509ServiceCertificateAuthentication auth = factory.Credentials.ServiceCertificate.SslCertificateAuthentication ?? factory.Credentials.ServiceCertificate.Authentication; // Default validation mode is ChainTrust — explicitly NOT None / Custom. Assert.NotEqual(X509CertificateValidationMode.None, auth.CertificateValidationMode); Assert.Null(auth.CustomCertificateValidator); } finally { factory.Abort(); } } [Fact] public void ClientCredentialsHelper_Enabled_InstallsAcceptAnyValidator() { Binding binding = HistorianWcfBindingFactory.CreateMdasNetTcpBinding(TimeSpan.FromSeconds(5)); ChannelFactory factory = new(binding, new EndpointAddress("net.tcp://10.0.0.1:32568/Hist")); try { HistorianWcfClientCredentialsHelper.Configure(factory, BaseOptions(allowUntrusted: true)); X509ServiceCertificateAuthentication auth = factory.Credentials.ServiceCertificate.SslCertificateAuthentication; Assert.NotNull(auth); Assert.Equal(X509CertificateValidationMode.Custom, auth.CertificateValidationMode); Assert.Equal(X509RevocationMode.NoCheck, auth.RevocationMode); Assert.NotNull(auth.CustomCertificateValidator); Assert.IsAssignableFrom(auth.CustomCertificateValidator); } finally { factory.Abort(); } } [Fact] public void CreateEndpointAddress_WithoutDnsIdentity_HasNullIdentity() { EndpointAddress address = HistorianWcfBindingFactory.CreateEndpointAddress("10.0.0.1", 32568, "Hist"); Assert.Null(address.Identity); } [Fact] public void CreateEndpointAddress_WithDnsIdentity_AttachesDnsEndpointIdentity() { EndpointAddress address = HistorianWcfBindingFactory.CreateEndpointAddress("10.0.0.1", 32568, "HistCert", "localhost"); Assert.NotNull(address.Identity); DnsEndpointIdentity dns = Assert.IsType(address.Identity); Assert.Equal("localhost", dns.IdentityClaim.Resource); } [Fact] public void CreateBindingPair_RemoteTcpCertificate_PropagatesServerDnsIdentity() { HistorianClientOptions options = BaseOptions(dnsIdentity: "localhost"); var (_, historyEndpoint, _, retrievalEndpoint) = HistorianWcfBindingFactory.CreateBindingPair(options); DnsEndpointIdentity historyIdentity = Assert.IsType(historyEndpoint.Identity); Assert.Equal("localhost", historyIdentity.IdentityClaim.Resource); // The Retrieval endpoint uses plain MdasNetTcp without TLS — no DNS identity needed. Assert.Null(retrievalEndpoint.Identity); } }