Search code examples
asp.net-core.net-corecertificateclient-certificates

Certificate based authentication failed with ERR_BAD_SSL_CLIENT_AUTH_CERT


I'm trying to implement Certificate based authentication with ASP.NET Core 9 following the official documentation: Configure certificate authentication in ASP.NET Core

I've come up with this basic implementation:

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.ConfigureHttpsDefaults(options =>
    {
        options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
    });
});

builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.AllowedCertificateTypes = CertificateTypes.All;

        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                Console.WriteLine($"Validating: {context.ClientCertificate.Subject}");

                if (context.ClientCertificate.Subject == "Pragmateek")
                    context.Success();
                else
                    context.Fail("Invalid certificate.");

                return Task.CompletedTask;
            }
        };
    });

var app = builder.Build();

app.UseAuthentication();

app.MapGet("/", () => "Hello World!");

app.Run();

When requesting the resource from Chrome I get a popup asking me to choose a certificate, I choose one generated with New-SelfSignedCertificate -Subject "CN=Pragmateek" -CertStoreLocation cert:\CurrentUser, but I get a ERR_BAD_SSL_CLIENT_AUTH_CERT error.

And the validation code in OnCertificateValidated is never called.

If it matters the project was created with the basic template dotnet new web --name ... and is run with dotnet run --launch-profile https (and running it with Visual Studio does not change anything).

What plumbing is missing?


Solution

  • The issue is that Kestrel seems to use a default client certificate validation which rejects the certificates, probably because they are self-signed.

    The solution was to allow any certificate with options.AllowAnyClientCertificate(); which overwrites ClientCertificateValidation with a truthy callback:

    builder.Services.Configure<KestrelServerOptions>(options =>
    {
        options.ConfigureHttpsDefaults(options =>
        {
            options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
            options.AllowAnyClientCertificate();
        });
    });
    

    EDIT: another way of having the certificate accepted is to trust it by adding it to the Windows Trust Store "Trusted Root Certification Authorities":

    Move-Item cert:\CurrentUser\My\B45606C0ACC9365D12172AF3E0C54BAAAAA27833 cert:\CurrentUser\Root