Search code examples
c#iisclient-certificateswcf-security.net-8.0

Client Certificate not passed or accepted by IIS using .NET 8, same code works in .NET 4.8


The following wcf call works in a .NET Framework 4.8 console application, but does not in a .NET8 console application.

The service is hosted by an IIS 10. Inside the ssl settings we set client certificates to "Required". I'm using the Systen.ServiceModel.Http 4.10.3 package, but also tried the newest 8.0.0 package in my .NET8 console project. I also tried to target .NET6.

The IIS log says: 403 7 5 15; so my assumption is, that the certificate isn't passed at all.

public static void Call()
{
    var channelFactory = new ChannelFactory<IExtendedParameterProvisioningPort>(new BasicHttpBinding
    {
        MaxBufferSize = 2147483647,
        MaxBufferPoolSize = 2147483647,
        MaxReceivedMessageSize = 2147483647,
        SendTimeout = new TimeSpan(0, 2, 0),
        ReceiveTimeout = new TimeSpan(0, 1, 0),
        OpenTimeout = new TimeSpan(0, 1, 0),
        CloseTimeout = new TimeSpan(0, 1, 0),
        Security = new BasicHttpSecurity()
        {
            Mode = BasicHttpSecurityMode.Transport,
            Transport = new HttpTransportSecurity()
            {
                ClientCredentialType = HttpClientCredentialType.Certificate
            }
        }
    }, new EndpointAddress("https://example.de:1460/MyWcfService.svc"));

    channelFactory.Credentials.ClientCertificate.SetCertificate(
        StoreLocation.LocalMachine,
        StoreName.My,
        X509FindType.FindByThumbprint,
        "MY-THUMBPRINT");

    channelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication =
        new X509ServiceCertificateAuthentication
        {
            CertificateValidationMode = X509CertificateValidationMode.None,
        };

    var extendedParameterProvisioningPort = channelFactory.CreateChannel();

    var serviceInfoResponse = extendedParameterProvisioningPort.GetServiceInfo(new ServiceInfoRequest());
    Console.WriteLine(serviceInfoResponse.Version);
}

The exception I'm receiving is:

The HTTP request is unauthorized with client authentication scheme 'Anonymous'.

Whats the issue here? Does .NET 6/8 not support client certificates yet, when it comes to ChannelFactory / WCF?


Solution

  • I encountered an issue where the default ChannelFactory in WCF uses the outdated HttpMessageHandler, which fails to pass certain certificates. After some investigation, I came across a helpful suggestion in this post.

    To address this problem, I implemented a solution using a new endpoint behavior. Credit goes to petersladek for this approach. Below is the implementation:

    First, define a new endpoint behavior (by petersladek):

    internal class CustomizeHttpHandlerEndpointBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(handler =>
            {
                var newHandler = new SocketsHttpHandler();
                // copy all properties set by WCF on it's handler (so we keep all configuration from wcf), see source
                // https://github.com/dotnet/wcf/blob/main/src/System.ServiceModel.Http/src/System/ServiceModel/Channels/HttpChannelFactory.cs#L272
                newHandler.AutomaticDecompression = handler.AutomaticDecompression;
                newHandler.Proxy = handler.Proxy;
                newHandler.UseProxy = handler.UseProxy;
                newHandler.UseCookies = handler.UseCookies;
                newHandler.CookieContainer = handler.CookieContainer;
                newHandler.PreAuthenticate = handler.PreAuthenticate;
                newHandler.Credentials = handler.Credentials;
                newHandler.SslOptions.EnabledSslProtocols = handler.SslProtocols;
                newHandler.SslOptions.ClientCertificates = handler.ClientCertificates;
    
                return newHandler;
            }));
        }
    
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
    
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
    
        public void Validate(ServiceEndpoint endpoint) { }
    }
    

    Finally, to integrate this behavior, you need to register it with the ChannelFactory:

    channelFactory.Endpoint.EndpointBehaviors.Add(new CustomizeHttpHandlerEndpointBehavior());