Reputation: 6292
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????
Upvotes: 11
Views: 9676
Reputation: 41
I was stuck on the same issue recently and resolved it successfully. There are two issues with the test project you're running on.
{
options.RevocationMode = X509RevocationMode.NoCheck;
services.AddTransient<MyCertificateValidationService>();
As soon as you make these changes, your breakpoint should hit MyCertificateValidationService.ValidateCertificate()
public class MyCertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate)
{
var cert = new X509Certificate2(Path.Combine("localhost_root_l1.pfx"), "1234");
return clientCertificate.Thumbprint == cert.Thumbprint;
}
}
Upvotes: 4
Reputation: 1
Following is the only thing that was required, just to get around the certificate error when calling on localhost self signed. Note that, I am constructor injecting the HttpClient (as described in here) In StartUp class,
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddHttpClient().ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (request, certificate, certificateChain, policy) => true
};
});
}
I got the above validation override from this article: Custom certificate validation in .NET.
Upvotes: -1
Reputation: 961
I had to deal with this as well recently and documented my steps here. They are quite lengthy so I recommend checking out the README and follow the instructions.
I'll post theme here anyway for those interested:
certcrt.cmd
and follow the instructions to create .cer, .pfx and .crl files.Current User\Personal
. This is usually done during creation.
.cer
client fileLocal Computer\Trusted Root Certitificates Authorities
.cer
file.crl
fileRun file iis.cmd
to update relevant configuration sections. The section iisClientCertificateMappingAuthentication
must be enabled and the section access
should have sslFlags
set to "Ssl, SslNegotiateCert, SslRequireCert"`.
Add Microsoft.AspNetCore.Authentication.Certificate
nuget package
Configure authentication protocol
services
.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
// Do validation on context.Certificate here
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
return Task.CompletedTask;
}
};
});
Add app.UseAuthentication()
before app.UseAuthorization()
to hook into CertificateAuthenticationEvents.OnCertificateValidated
. This is never called otherwise, leaving open for any certificate.
HTTP Error 403.16 - Forbidden Your client certificate is either not trusted or is invalid.
See certificates step 2.
Warning: Certificate validation failed, subject was CN=ancc_client. RevocationStatusUnknown The revocation function was unable to check revocation for the certificate.
See certificates step 3 or disable recovation check options.RevocationMode = X509RevocationMode.NoCheck
Upvotes: 0
Reputation: 39
options.RevocationMode = X509RevocationMode.NoCheck
setting this worked for me
Upvotes: 0
Reputation: 1492
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.
Upvotes: 2