Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client certificate not included by HttpClientHandler in .net core #26531

Closed
janobrestad opened this issue Jun 18, 2018 · 16 comments
Closed

Client certificate not included by HttpClientHandler in .net core #26531

janobrestad opened this issue Jun 18, 2018 · 16 comments

Comments

@janobrestad
Copy link

I have code in a .net Standard 2.0 project that uses HttpClientHandler to include a client certificate in the request.
It works perfectly when called from a console program running on .net 4.7.1, but if I run the code from a .net core console app the client certificate is not sent. There are no exceptions, no error messages, the client certificate is just not sent to the server.

To have a simple way of reproducing this I changed the default ValuesController in a new WebApi project (not .net core) to this.

        // GET api/values
        public IEnumerable<string> Get()
        {
            var clientCert = this.Request.GetClientCertificate();
            if (clientCert == null) throw new HttpResponseException(HttpStatusCode.BadRequest);
            return new string[] {clientCert?.SerialNumber };
        }

Basically just returning the serialnumber of the client certificate or returning a BadRequest error if it is not included.

I then have this code in a .Net Standard 2.0 library:

    public class Tester
    {
        public static async Task TestClientCert()
        {
            var result = await TestClientCert(StoreLocation.LocalMachine, 
                "3100778c4d432fde24200c8f09b7315bb74244dd", 
                "https://localhost:44314/api/values");
            Console.WriteLine();
            Console.WriteLine("Certificate info from api: " + result?.FirstOrDefault());
        }

        public static async Task<IEnumerable<string>> TestClientCert(StoreLocation storeLocation, string thumbPrint, string url)
        {
            var handler = new HttpClientHandler
            {
                ClientCertificateOptions = ClientCertificateOption.Manual
            };
            var clientCertificate = LoadCertificate(storeLocation, thumbPrint);
            Console.WriteLine("Client Certificate SerialNumber: " + clientCertificate?.SerialNumber);
            handler.ClientCertificates.Add(clientCertificate);
            var httpClient = new HttpClient(handler);
            var res = await httpClient.GetAsync(url);
           Console.WriteLine("StatusCode: " + res.StatusCode);
            var json = await res.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<IEnumerable<string>>(json);
        }

        private static X509Certificate2 LoadCertificate(StoreLocation storeLocation, string thumbPrint)
        {
            X509Store store = new X509Store(storeLocation);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection cers = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, false);

            X509Certificate2 certificate = null;
            if (cers.Count > 0)
            {
                certificate = cers[0];
            }
            store.Close();
            return certificate;
        }
    }

The calling code on .net core and full framework is identitical:

            Tester.TestClientCert().Wait();
            Console.ReadLine();

The full framework version prints this:

Client Certificate SerialNumber: 0108FDA8EF8C77F6F17EF7
StatusCode: OK

Certificate info from api: 0108FDA8EF8C77F6F17EF7 

While the output from the .net core version is:

Client Certificate SerialNumber: 0108FDA8EF8C77F6F17EF7
StatusCode: BadRequest

Certificate info from api:

Am I doing something wrong here? Or is there a bug in .net core?

@karelz
Copy link
Member

karelz commented Jun 18, 2018

Did you try it on .NET Core 2.1? Which OS do you run on?

@davidsh
Copy link
Contributor

davidsh commented Jun 18, 2018

Please check that your client certificate has the EKU of ClientAuthentication (or no EKU attribute at all). And please verify that it is not using SHA1. Violating these constraints may result in the HTTP stack ignoring your certificate and not sending it.

@Spongman
Copy link

I'm seeing this issue also. Client certificates are no longer being sent where they were being sent with libcurl.

here's how i create my certs: https://gist.github.com/mtigas/952344

@davidsh
Copy link
Contributor

davidsh commented Jun 19, 2018

@janobrestad Could you post the exact steps you use to create your client certificate? And, if possible, could you attach the *.CER file of the client certificate? You can export it out of the certificate store. Please do not export the file as PFX, i.e. don't export the private key.

Alternatively, please post a complete picture of all the X509v3 fields of the certificate. Here is a sample picture:

image
image
image

@janobrestad
Copy link
Author

I did not create the client certificate myself. I used a test certificate issued by a well known issuer.

There was actually a set of certificates from this issuer, and when I used the other one it worked on .net core.

So to sum it up:
For one certificate it worked on both .net core 2.1 and .net framework 4.7.1,
for the other it only worked on the full framework 4.7.1.

Here are some details from the certificate that failed on .net core:
image

It looks like .net core is more strict about which certificates you can use as client certificates.

Is the reason for rejecting a client certificate logged somewhere?

@davidsh
Copy link
Contributor

davidsh commented Jun 19, 2018

It looks like .net core is more strict about which certificates you can use as client certificates.

.NET Core requires Key Usage attribute, if present, to have "Digital Signature" value.
https://github.com/dotnet/corefx/blob/fe7adb2bf92bba926268c2e9f562b11c84932a07/src/Common/src/System/Net/Security/CertificateHelper.cs#L53-L68

Is the reason for rejecting a client certificate logged somewhere?

We do have EventSource logging for .NET Core networking:
https://github.com/dotnet/corefx/blob/master/Documentation/debugging/windows-instructions.md

However, we probably should add more logging for scenarios like this.

cc: @bartonjs

@davidsh
Copy link
Contributor

davidsh commented Jul 22, 2018

We have added more logging to help developers track why certain client certificates might not be sent to the server, dotnet/corefx#31168

@davidsh davidsh closed this as completed Jul 22, 2018
@lexugax
Copy link

lexugax commented Nov 20, 2018

It's been a while since this post, but I have the same issue, yet my certificate does have the Digital Signature value.
I am using .net Core 2.1

@caesar-chen
Copy link
Contributor

@lexugax Have you checked the log to find out why the certificate is not included?

You can find the instructions here (check for: *Microsoft-System-Net-Security {066c0e27-a02d-5a98-9a4d-078cc3b1a896}: Security-related traces.).

@lexugax
Copy link

lexugax commented Nov 21, 2018

@Caesar1995 Thanks. I have not. I am having a hard time following that article. There are many things that the article presupposes the reader already knows. I am quite lost. I just wonder why doesn't the application throw an exception if there is an issue with the certificate being used.

@davidsh
Copy link
Contributor

davidsh commented Nov 21, 2018

I just wonder why doesn't the application throw an exception if there is an issue with the certificate being used.

The design of the .NET Core HTTP stack is to not throw exceptions for client certificates that don't match the required properties. The certificate is simply skipped and other possible client certificates are examined to see if they match.

In order to diagnose this, you will need to take trace as described below.

@davidsh
Copy link
Contributor

davidsh commented Nov 21, 2018

Also, in general, it is not good to comment on closed issues like this because they are not tracked. If you are having an issue with the current version of .NET Core, you should open a new issue and include all information necessary to reproduce the issue.

@lexugax
Copy link

lexugax commented Nov 21, 2018

@daflame76 Thanks. Will try to make the logs work. It is just not very clear. And thanks for the advice on closed issues. I just thought that since it was the same issue, it would be better to follow up as supposed to open a new one.

@davidsh
Copy link
Contributor

davidsh commented Nov 21, 2018

I just thought that since it was the same issue, it would be better to follow up as supposed to open a new one.

It is better to open a new issue. Many times, the root cause is different despite the symptoms being similar. It is ok to reference the closed issue number in your new issue though.

@alcoforado
Copy link

Guys, if you don't wanna use a certificate We pass to you, throw an exception, just don't ignore it and let us in the dark.

@scalablecory
Copy link
Contributor

@alcoforado: we do not actively monitor old issues. please open a new issue with your concern to ensure it gets attention and can be tracked.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 3.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants