Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Require SSL Client Certificate only for specific routes or controllers

I have an ASP.NET MVC Core project using Kestrel as the server. It is both serving up user content (asp.net mvc) and hosts web API controllers that agents (software) communicate with. I have enabled HTTPS and client certificate support. The issue is that I want to require client certificates for agents (software) that call Web APIs but I do not want to require/prompt for client certificates for regular browser based users.

I have enabled HTTPS/client certificate support the following way:

var host = new WebHostBuilder()
.UseKestrel(options =>
{
    HttpsConnectionFilterOptions httpsoptions = new    HttpsConnectionFilterOptions();
    httpsoptions.ServerCertificate = CertUtil.GetServerCert();
    httpsoptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
    httpsoptions.CheckCertificateRevocation = false;

    options.UseHttps(httpsoptions);
})
.UseUrls("http://0.0.0.0:5000", "https://0.0.0.0:5001")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();

I have a separate middleware handler setup in Startup.cs to handle custom verification of client certificates. This code does successfully execute and everything works fine in that sense.

The problem is this happens globally and I am only looking to apply client certificates to specific controllers and/or routes; or really I would take any granularity at this point.

Essentially trying to create the same sort of behavior you can get in IIS by creating two virtual directories and then setting SSL Settings to Accept on one and Ignore on the other. The one with Accept will prompt the browser for a cert and the one with Ignore will not.

I tried setting HttpsConnectionFilterOptions to only specify ServerCertificate in hopes that not setting any client certificate related options would allow the server to receive client certificates if they are sent but otherwise not prompt browsers for them. This did not seem to work as my middleware client certificate handler never sees a client cert when calling this function (it does when ClientCertificateMode is set to AllowCertificate.

context.Connection.GetClientCertificateAsync();

I guess in short does Kestrel hosting even allow for more granular client certificate mapping/handling or is it only possible using IIS? IIS is not an option for this project and I would rather prefer not having to create a separate project/process just for the client cert api aspects. Appreciate any help!

like image 820
Paul W. Avatar asked Jul 29 '16 19:07

Paul W.


2 Answers

I've been trying to do the same thing, with exactly the same requirements as you.

I've come to the conclusion that it's not possible. My workaround is to use 2 WebHostBuilder objects - one for locations that don't need client certs, and one for those that do. This does have the downside that each IWebHost must listen on a different port, but from the scenario you describe I guess that's not a big issue.

I do this within the same process, so this solution fits that requirement.

like image 127
Cocowalla Avatar answered Sep 21 '22 15:09

Cocowalla


I had the same issue with context.Connection.GetClientCertificateAsync(); it was always returning null. Then I noticed that I was running Kestrel thru IIS Express all the time.

So in Visual Studio from the Debuger toolbar I changed from IIS Express to my project. Kestrel was started as console application and I was able to get the client certificate.

enter image description here

I think that IIS Express does not support client certificates so the certificate was always ignored.

For the other part of the question; I think Kestrel dos not support this granularity that you are looking out of the box when using the HttpsConnectionFilterOptions. From the Kestrel Connection Filter Options source code the connection will be dropped if the client certificate is null. Maybe you can modify the source code for the HttpsConnectionFilterOptions and create your own filter from it. Then you can use the ClientCertificateValidation property to specify custom certificate validation method that will allow the connection when no client certificate is send.

Hope this helps.

like image 26
The Tech Geek Avatar answered Sep 22 '22 15:09

The Tech Geek