Search code examples
c#authentication.net-corecertificatekestrel

.NET Core adding client certificate to POST to Kestrel REST API on Linux fails server cert validation


I am using .NET Core 2.1, Kestrel on Linux.

My web application acting as client makes a request as so (following guides dotted around, this seems to the way to go):

var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
X509Certificate2 cert = GetClientCertificate();
handler.ClientCertificates.Add(cert);

using (var client = new HttpClient(handler))
     {
        var myRequest= new myRequest()
          {
                foo = bar,
            };

var response = await client.PostAsync(myUrl, myRequest, new JsonMediaTypeFormatter());

I have configured Kestrel as so:

    return WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(options =>
        {
            options.Listen(IPAddress.Any, 443, listenOptions =>
            {

            var httpsConnectionAdapterOptions = new HttpsConnectionAdapterOptions()
                {
                    ClientCertificateMode = ClientCertificateMode.RequireCertificate,
                    SslProtocols = System.Security.Authentication.SslProtocols.Tls12,
                    ServerCertificate = GetSSLCertificate(),
                    ClientCertificateValidation = CertificateValidator.MyCustomerValidatorForLogging
                };
                listenOptions.UseHttps(httpsConnectionAdapterOptions);
            });
        }
        )
    .Build();

I added a custom validator (just to see what is going on, looks like this):

public static bool MyCustomerValidatorForLogging(X509Certificate2 certificate, X509Chain chain, SslPolicyErrors errors)
        {
            Log.Info("Received Request");
            Log.Error(errors.ToString());

            if (errors == SslPolicyErrors.None)
            {
                return true;
            }

            return false;
        }

GetClientCertificate() is the certificate for client authentication SSL cert signed by an intermediate CA.

GetSSLCertificate() is the certificate for a standard server authentication SSL cert.

I have copied the issuing Sub CA and CA certs of the client authentication cert in to /usr/local/share/ca-certificates/ (the "store") and issued the "update-ca-certificates" command. I believe it is these certs that are used to verify the client cert.

When the server receives the request, the errors value is: "RemoteCertificateChainErrors" and rejects the request.

Anyone have any ideas please?


Solution

  • Turns out this is actually working! I just needed to get the correct certificates in place!

    Specifically, I had to include the Root CA and Intermediate CA at the Server side.
    The Client side, I had to include only the Root CA to verify.