Kestrelhttpserver: SSL Mutual Authentication in .NetCore 1.1 Kestrel

Created on 7 Feb 2017  Â·  26Comments  Â·  Source: aspnet/KestrelHttpServer

I am trying to implement SSL mutual authentication using Kestrel web server in a MVC project. In order to do so, I created self signed client and server certificate on my local machine. I enabled SSL settings in Program.cs by using the ClientCertificateMode equals RequiredCertificate enum. After this when I browse a endpoint from browser, it prompts for a certificate and when I choose an invalid certificate, I can see HTTPS authentication failed message in the console and in log file but still the request is being processed and the JSON output is rendered back. Below is the piece code for SSL authentication.

var host = new WebHostBuilder()
                         .UseConfiguration(config)
                         .UseKestrel(options =>
                         {
                            var signingCertificate = new X509Certificate2("\\ServerCert.pfx", "Test123");
                            HttpsConnectionFilterOptions httpsOptions = new HttpsConnectionFilterOptions();
                            httpsOptions.ServerCertificate = signingCertificate;
                            httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
                            options.UseHttps(httpsOptions);
                          })
                          .UseContentRoot(Directory.GetCurrentDirectory())
                          .UseStartup<Startup>()
                         .Build();
host.Run();

What needs to done to prevent the request from getting processed when SSL authentication fails? Since the MVC project will be consumed within our domain network, we are okay to expose Kestrel as the front facing web server.

Most helpful comment

I have written a middle ware to deal with client certificate based auth here
https://github.com/xavierjohn/ClientCertificateMiddleware

All 26 comments

RequireCertificate is insufficient, the client did send you a certificate. You need to actually inspect the certificate to make sure it meets your criteria and is for the right user. See HttpContext.Connection.ClientCertificate.

Should this be done as part of middleware? Can you please point me to some samples?

@here Can someone point me to samples on checking client certificate through HttpContext? Should this be done as part of middleware?

@Tratcher The kestrel server itself is authenticating the certificate as server https://github.com/aspnet/KestrelHttpServer/blob/dev/src/Microsoft.AspNetCore.Server.Kestrel.Https/HttpsConnectionAdapter.cs#L93 From the code it looks like, connection will be terminated if this authentication fails. When the connection is terminated, how come the request is still getting processed? Do we really need to add certificate validation logic even if we do not intend to have custom validation?

@blowdart

Oh interesting. If it fails then the request should stop IMO.

I believe that it doesn't. Instead you get no client certificate in your httpcontext.connection.clientcertificate property. From there it's simple to make middleware that rejects your client

OK thinking a bit more I think this needs a client cert authentication middleware. It's not really a server concern to make decisions, except perhaps "This cert is revoked, oh heck no", but even then if you're not creating an identity from the cert you're not authenticating or authorizing or acting on it.

@Drawaes HttpContext.Connection.ClientCertificate property still holds the client certificate that was passed in the middleware. It's not NULL.

No but you can validate it there. Is the Certificate invalid or just wrong?
Eg does the client have the private key for the cert?

On 7 Feb 2017 8:50 p.m., "SudharsanN" notifications@github.com wrote:

@Drawaes https://github.com/Drawaes HttpContext.Connection.ClientCertificate
property still holds the client certificate that was passed in the
middleware. It's not NULL.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/aspnet/KestrelHttpServer/issues/1344#issuecomment-278135743,
or mute the thread
https://github.com/notifications/unsubscribe-auth/APpZuSDOgrdt9Cv3yvmmQNU1rt4MGgD2ks5raNkngaJpZM4L4_EY
.

In case you missed it, you can set HttpsConnectionFilterOptions.ClientCertificateValidation which is a Func<X509Certificate2, X509Chain, SslPolicyErrors, bool>. Return true if you want to accept the client certificate; false otherwise. The ClientCertificateValidation is called in SslStream's RemoteCertificateValidationCallback ctor argument.

@halter73 I am planning to use HttpsConnectionFilterOptions.ClientCertificateValidation. By this way, I don't have to introduce a middleware.

@halter73 Can you please let me know what authentication is done in the below code ?https://github.com/aspnet/KestrelHttpServer/blob/dev/src/Microsoft.AspNetCore.Server.Kestrel.Https/HttpsConnectionAdapter.cs#L93

Is it just validating the root issuer of the client by comparing it against the server certificate?
Also, the callback HttpsConnectionFilterOptions.ClientCertificateValidation will be invoked after or before the above call? Whether the callback will be invoked irrespective of client certificate being valid or not?

The reasons I would like to know the above is , in the callback that I am writing should I just do some basic validation like checking the Subject, Issuer properties of the certificate? Or, I should replicate entire logic present in the AuthenticateAsServerAsync method?

It is only defering to SslStream. If you look in SslStream I think you might find

_remoteCertificateOrBytes` = certificate == null ? null : certificate.RawData;
            if (_userCertificateValidationCallback == null)
            {
            if (!_sslState.RemoteCertRequired)
            {
                sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable;
             }
             return (sslPolicyErrors == SslPolicyErrors.None);
            }
            else
            {
                return _userCertificateValidationCallback(this, certificate, chain, sslPolicyErrors);
            }

So I think you will get basic SChannel validation that the certificate is not out of date, and isn't in your list of bad certs, anything else will pass through and you will need to validate to your own criteria.

I could be wrong though.

@SudharsanN If you set ClientCertificateValidation, it's your responsibility to check the SslPolicyErrors flags if you care about them. Without a ClientCertificateValidation callback, Kestrel will require the are no SslPolicyErrors flags set.

Presumably SslStream uses SChannel/OpenSSL to set the SslPolicyErrors flags, but I'm not sure exactly what's being validated at that layer for client certificates.

In my case, context.Connection.ClientCertificate is null
Powershell> Invoke-WebRequest https://localhost:44303/api/values -Certificate $cert

I tried HttpsConnectionFilterOptions with a callback but the function is not getting called.
@SudharsanN: How did you get context.Connection.ClientCertificate to contain the certificate?

In my case I used the Http client from .net so I would say it's an issue with the invoke web request.

Did you check with wireshark or fiddler that your client is sending the cert?

Could you post your sample on github?
Invoke-Request should have worked. I also tried a .net client.
https://msdn.microsoft.com/powershell/reference/5.1/microsoft.powershell.utility/Invoke-WebRequest

The code I tried.
static void Main(string[] args)
{
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(new X509Certificate2("../SampleCert.pfx", "password"));
var client = new HttpClient(handler);
var result = client.GetAsync("https://localhost:44367/api/values").GetAwaiter().GetResult();
}

Sure. When I get home later I can try to make a sample. Can you give me the asp.net core version you are using?

I see it's 1.1 I will try to make a sample. I am using it in prod code so ant copy paste

Thanks... I have tried 2 different sample on two different machines but no success.

  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

and

<PropertyGroup>
    <TargetFramework>net461</TargetFramework>
    <RuntimeIdentifier>win7-x86</RuntimeIdentifier>
 </PropertyGroup>

It feels like UseKestrel options seems to have no effect. It seems to behave the same no matter what I put in the options.

Finally got it to work by NOT using IIS Express and using the below code...

.UseKestrel(options =>
{
     HttpsConnectionFilterOptions httpsOptions = new HttpsConnectionFilterOptions();
     httpsOptions.ServerCertificate = certificate;
     httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
     httpsOptions.SslProtocols = System.Security.Authentication.SslProtocols.Tls;
     options.UseHttps(httpsOptions);
 })
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls("https://*:4430")
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();

If I don't give the SslProtocols.. It gives error "The client and server cannot communicate, because they do not possess a common algorithm"

This article said not to use IIS Express
http://www.blinkingcaret.com/2017/03/01/https-asp-net-core/

I wonder what setting makes the browser pop up and ask which certificate to use. Currently I have it working using PowerShell.

Normally to get the browser to ask the server side has to list the CA's it will accept certificates from not sure how you can configure that with SSLStream or if it's even possible.

I have written a middle ware to deal with client certificate based auth here
https://github.com/xavierjohn/ClientCertificateMiddleware

@xavierjohn that's not a Kestrel related issue, you don't use Kestrel's HTTPS implementation in Azure Web Apps (e.g. in IIS).

Here is a document on how to enable certs on Azure Web App.
https://msftplayground.com/2016/06/azure-app-service-and-client-certificate-authentication/

It has a negative effect of making Client Cert mandatory where as in my example, anyone should be able to hit the anyone controller. Ideally, an option which makes Client Cert optional would be nice.
https://feedback.azure.com/forums/169385-web-apps/suggestions/13481457-support-optional-client-certificates-for-tls-mutua

Was this page helpful?
0 / 5 - 0 ratings