While playing around with IdentityServer4 and mTLS client authentication, I was recommended mkcert for generating trusted development certificates.
I found this tool to be super simple to use and it saved me from having to use OpenSSL or the PowerShell replacement for MakeCert (New-SelfSignedCertificate
).
So, I thought I would document how to use mkcert on Windows and how to use it for some ASP.NET Core development tasks such as client authentication and pfx generation.
Installation and mkcert CA
mkcert is written in Go, and you can run it with a Go run
command, but if you’re like me, you’ll want to install it.
I did this on Windows using Chocolatey, but you can find alternatives listed in the documentation.
Once you have mkcert installed, you will need to run the following command to install a new Certificate Authority (CA) on your machine.
mkcert -install
IIS TLS Certificates
With mkcert ready to go, let’s use the example of wanting a certificate for Transport Layer Security (TLS) for the test site on our machine. The cool thing about this approach is that you can issue certificates for reserved top-level domains, including localhost. So, let’s generate a certificate for scottbrady91.test. I’ll leave it to you to set up a hostname record for your chosen domain.
By default, mkcert will two .pem
files, one for the certificate, the other for the private key.
Windows & IIS like their .p12
and .pfx
files, so you will need to use the pkcs12
command to generate that instead:
mkcert -pkcs12 scottbrady91.test
This command should give you the file scottbrady91.test.p12
with a default password of “changeit”.
It is a 2048-bit RSA key, valid for 10 years, suitable for Server Authentication, and Digital Signature & Key Encipherment.
In my case has a subject of:
CN = scottbrady91.test
OU = DESKTOP-ITA7HLB\Scott@DESKTOP-ITA7HLB (Scott Brady)
O = mkcert development certificate
You can now import this into your Windows certificate store (local machine), and bind it to your IIS site:
Which should then let you navigate to that URL and see a valid certificate (I’m using Chrome, which uses my machine’s CAs).
ECDSA IIS TLS
You can even generate a certificate with an ECDSA key using:
mkcert -ecdsa -pkcs12 scottbrady91.test
This certificate has a 256-bit ECC public key and public key parameters of ECDH_P256. This also works nicely with the Windows certificate store and IIS.
Client Certificate Authentication (mTLS)
You can also use certificates generated with mkcert for client authentication with mutual TLS (mTLS). mTLS is where the client uses an X.509 certificate to authenticate themselves with the server that they are calling. This way, both sides of the connection are verified using TLS. mTLS is a popular approach for machine-to-machine authentication with microservices, gRPC, and even for basic proof-of-possession in OAuth.
To generate a client certificate for your server, you can use the client flag in combination with pkcs12:
mkcert -client -pkcs12 scottbrady91.test
This should give you the file scottbrady91.test-client.p12
, and a certificate that is suitable for both Client Authentication and Server Authentication.
mTLS in Kestrel
Let’s take a quick look at how to use this client certificate to authenticate access to an ASP.NET Core 3 website. We’re going to run this using Kestrel as this lets us run the site from Visual Studio. If you want to use IIS, you would have to publish the site; as far as I know, you can’t use client certificate authentication in IIS Express.
First, spin up an ASP.NET Core application:
dotnet new mvc
You can then configure Kestrel to require a client certificate to connect to the server.
This is done in the KestrelServerOptions
, accessible in your web host builder.
This enforces client authentication applies to your whole website.
If you want to apply it only to a specific path, consider using IIS instead.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
options.ConfigureHttpsDefaults(opt =>
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});
webBuilder.UseStartup<Startup>();
});
}
We now require a client certificate, but we’re not yet validating it within our application. To do so, you’ll to bring in the following nuget package:
install-package Microsoft.AspNetCore.Authentication.Certificate
Which you can then use to add client certificate authentication to your Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication("mTLS")
.AddCertificate("mTLS", options
=> options.RevocationMode = X509RevocationMode.NoCheck);
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
}
}
I was unable to get mkcert to work with the authentication handler’s revocation check, so I had to disable it. Don’t let that one slip into production.
You can then trigger the authentication handler using an AuthorizeAttribute on one of your endpoints.
[Authorize]
public IActionResult Index()
{
return View();
}
You can test this using chrome and get challenged for a certificate. The UX isn’t great, but we don’t typically use client certificate authentication via a browser.
You should now get a user loaded in (or, if it went wrong, a 403). Mine had the following claims:
- issuer: CN=mkcert DESKTOP-ITA7HLB\Scott@DESKTOP-ITA7HLB, OU=DESKTOP-ITA7HLB\Scott@DESKTOP-ITA7HLB, O=mkcert development CA
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/thumbprint: 96AF4D125E580B99B2F51E54DC5B5703A164AC39
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/x500distinguishedname: CN=scottbrady91.test, OU=DESKTOP-ITA7HLB\Scott@DESKTOP-ITA7HLB (Scott Brady), O=mkcert development certificate
- http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber: 1EF3FA2F9790C4B6B8635179718326F0
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dns: scottbrady91.test
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: scottbrady91.test
Source Code
You can find the source code for this ASP.NET Core website on GitHub. You’ll need to create your certs for testing though. It’s a good thing you now know how 😀