I am building a small feature in ASP.NET Core Certificate authentication as given in official docs.
Note: I am not building APIs, I am just trying to secure some Action methods of some controllers so that these secured action methods are opened only when the client has the client certificate.
The below image shows that I am able to secure the Index action method which now requires client certificate. Other action method which is Privacy does not require client certificate. The result is that Index action does opens in browser (403 error is received) but Privacy action is opened up in browser
Full Codes
1. Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o =>
o.ClientCertificateMode =
ClientCertificateMode.RequireCertificate);
});
});
2. Startup.cs
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices.GetService<MyCertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
context.Success();
}
else
{
context.Fail("invalid cert");
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Fail("invalid cert");
return Task.CompletedTask;
}
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
3. MyCertificateValidationService.cs
public class MyCertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate)
{
var cert = new X509Certificate2(Path.Combine("localhost_root_l1.pfx"), "1234");
if (clientCertificate.Thumbprint == cert.Thumbprint)
{
return true;
}
return false;
}
}
4. Action methods that are secured and unsecured
[Authorize]
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
Note: Index action method requires client authentication while Privacy does not require client certificate.
The Problems: The problems which I am getting are:
CertificateAuthenticationEvents
& OnAuthenticationFailed
located on ConfigureServices()
method of startup.cs file I not called. I checked them by placing breakpoints but breakpoint is not reached.
MyCertificateValidationService.cs class ValidateCertificate()
method is also not called up. I have also checked it with breakpoint
Please help me to implement Certificate Authorization.
Update
I created 2 certificates in C# as explained in this tutorial. These are:
I did 2 things with these certificates:
a. I added root_localhost.pfx to the Trusted Root Certification Authorities (on Windows) for the Local Computer (using CertManager).
b. I imported the Client certificate on by chrome browser.
Next, I selected the project in VS 2019 (console) instead of 'IIS Express' and run my project. I opened the website url in incognito window, the URL happens to be - https://localhost:5001
Chrome asks to select the certificate, see image below:
On selecting it i get This site can’t be reached - ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, see below image:
Why it is happening????
Open “Power Shell” as an administrator and run the below command: New-SelfSignedCertificate -DnsName "localhost", "localhost" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date). AddYears(10) -FriendlyName "CAlocalhost" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature.
On the taskbar, click Start, and then click Control Panel. In Control Panel, click Programs and Features, and then click Turn Windows Features on or off. Expand Internet Information Services, then select Client Certificate Mapping Authentication, and then click OK.
HTTPS Redirection Middleware (UseHttpsRedirection) to redirect HTTP requests to HTTPS. HSTS Middleware (UseHsts) to send HTTP Strict Transport Security Protocol (HSTS) headers to clients.
At the moment your application is not configured to use client certificates. The reason is that you start (host) your application in IIS Express. There're 2 options:
1) The simplest one, switch to run in Project mode (the app will be run in the console window). You can run it manually in the console as well.
2) A little more complex method is to configure your IIS Express to work with client certificates. Following this steps: 2.1) edit \config\applicationhost.config file and change the section below (changes - Deny to Allow).
<sectionGroup name="security">
<section name="access" overrideModeDefault="**Allow**" />
<section name="applicationDependencies" overrideModeDefault="Deny" />
<sectionGroup name="authentication">
<section name="anonymousAuthentication" overrideModeDefault="**Allow**" />
2.2) in you project add the following file web.config
<configuration>
<system.webServer>
<security>
<access sslFlags="Ssl,SslNegotiateCert,SslRequireCert" />
<authentication>
<anonymousAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
</configuration>
Next:
The prerequisites for the client authentication to work is to have a client certificate. You can create self signed one using the following commands or any other methods for generating client certificates:
#create key
openssl req -newkey rsa:4096 -keyout key.pem -out csr.pem -nodes -days 365 -subj "/CN=Your name"
#create certificate
openssl x509 -req -in csr.pem -signkey key.pem -out cert.pem -days 365
#self sign it
openssl pkcs12 -export -in cert.pem -inkey key.pem -out your_cert.p12
As this certificate is self signed, you have to add it to the Trusted Root Certification Authorities (on Windows) for the Local Computer (using CertManager).
After it you need to install it (import) to you Personal Certificates Storage using the same CertManager but only for current user. Alternative methods is to use Chrome settings ("Manage certificates"). This is required for Chrome to be able to send the certificate to the server.
Also in you application you might change this option that allows self signed certificates.
services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
**options.AllowedCertificateTypes = CertificateTypes.All**;
After all these changes it should ask to choose the certificate when you access your site.
Tip: You may not be asked to choose which certificated to use if you are visiting the same page again until you close all chrome instances. if you want it to ask to choose which certificate to use, open a new incognito window.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With