Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient does not send client certificate on Windows using .NET Core

I am unable to get the HttpClient class to send a client certificate using .NET Core on Windows.

Here is the code I am using:

X509Certificate2 certificate = new X509Certificate2(@"C:\Repos\selly\client1.pfx", "password");
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = (a,b,c,d) => { return true; };
handler.ClientCertificates.Add(certificate);

HttpClient client = new HttpClient(handler);
var content = new StringContent("");
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
client.PostAsync("https://client2:5002/api/values", content).Wait();

The code works as expected on Linux (Ubuntu 16.04) (obviously, with the path to the certificate changed). It does not work on Windows.

Looking at the exchange in Wireshark, the client does not send the certificate when running on Windows (10 v1703).

I have run similar code using .NET Framework (using 'WebRequestHandler' instead of 'HttpClientHandler'). It correctly sends the client certificate.

I found the following online, but unlike the problem described there, the server certificate callback is executed. I did generate the certificates myself, but they are all signed by a Root CA, (which is installed on both client and server) and all the details are correct.

I also found this, which suggests that client certificates do work with HttpClient on .NET Core on Windows.

I have also tried explicitly setting the TLS version, however the problem persists.

I am using Kestrel as the web server. It is configured as follows:

.UseKestrel(options =>
{
    var sslOps = new HttpsConnectionFilterOptions();
    sslOps.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
    sslOps.ClientCertificateValidation = CheckClientCertificateLogic.CheckClientCertificate;
    sslOps.ServerCertificate = new X509Certificate2(@"C:\Repos\selly\client2.pfx", "password");
    options.UseHttps(sslOps);
})

The 'ClientCertificateValidation' callback does not execute on requests received from a Windows client; probably because it hasn't received a certificate for it to check...

Is this a bug in .NET Core? Is there a workaround?

like image 337
Alex Stevens Avatar asked Jun 30 '17 12:06

Alex Stevens


1 Answers

Not entirely sure if my reproducing this issue is due to the same root cause. However, I had the same problem and it turns out that the certificate is not sent because the client cannot validate it. After adding the issuer certificate to my trusted issuers list in the "Certificates" MMC snap-in on the client, Windows can validate the certificate and HttpClient starts to send it. If you have a self-signed certificate then I assume that you can add that to the trusted issuers store instead. This was tested on .Net 4.6.1.

The relevant code that is setting the client certificate in WinHTTP is here. When you read code comments for GetEligibleClientCertificate, you see that MS has been planning to add additional filtering of the certificates that removes anything that isn't matched by the trusted issuers list. They simply haven't thought about the use case where you keep your certificate in a file and don't care about the certificate store.

like image 63
flodin Avatar answered Sep 29 '22 17:09

flodin