Require SSL Client Certificate only for specific routes or controllers

Paul W. picture Paul W. · Jul 29, 2016 · Viewed 13.6k times · Source

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!

Answer

Cocowalla picture Cocowalla · Oct 23, 2016

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.