Using Certificates For API Authentication In .NET 5

Introduction

In today’s article, we will look at using certificates for protecting and providing authentication to our APIs in .NET 5. As we all know, security is particularly important for all applications especially APIs as these expose our business logic to be consumed by various clients over the web. By using certificates, we can ensure that whenever a call is made to our API, there is a certificate attached to it that confirms the identity of the caller, and only if we recognize this identity do, we allow the API to be processed and return data to be provided.

Creating the Certificates

The first step is to obtain a certificate from a certification authority company like VeriSign etc. that provides these certificates. These certificates will confirm the identity of the company and will ensure that calls making these certificates are genuine. For the purpose of this article, we will not be using a commercial certificate but instead, we will generate our own certificate.

The first step is to generate a CA (Certification Authority) certificate that will be installed on the server (Where our API will reside) and this certificate will be used to further generate client certificates that will be provided to various clients that will call the API. These clients will then attach this certificate to each call. Back on the server, we will validate this certificate and if it looks good, we will allow the operation to proceed.

Let us generate the CA certificate first. 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.

Once this command is executed, you will see the thumbprint for this certificate. Note this down as it will be used to generate client certificates. This is the unique identifier for the certificate.

Using Certificates for API authentication in .NET 5

Next, set a password as below:

$mypwd = ConvertTo-SecureString -String "Server123" -Force -AsPlainText

Using Certificates for API authentication in .NET 5

The final step is to generate a PFX file for the certificate. We do this using the below command,

Get-ChildItem -Path cert:\localMachine\my\05793D3304C6CE0F8C1A4B26E137BBFDDE2A81EE | Export-PfxCertificate -FilePath "C:\CloudItems\Learning\CSharpCorner\Article 62\cacert.pfx" -Password $mypwd

Using Certificates for API authentication in .NET 5

Now, the PFX file has been generated. Before we add the CA certificate to our machine certificates, let us generate the client certificate from it. We will then provide this certificate to the client to attach with each request.

First run the below command using the thumbprint of the CA certificate.

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\05793D3304C6CE0F8C1A4B26E137BBFDDE2A81EE )

Using Certificates for API authentication in .NET 5

Then follow with the below command,

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "localhost" -Signer $rootcert -NotAfter (Get-Date).AddYears(10) -FriendlyName "Clientlocalhost"

Using Certificates for API authentication in .NET 5

This will create the client certificate using the CA certificate. Note down the thumbprint for later use.

Next, create a password to be used with the client certificate as below,

$mypwd = ConvertTo-SecureString -String "Client123" -Force -AsPlainText,

Using Certificates for API authentication in .NET 5

Finally, create the PFX file for the client certificate as below using the thumbprint of the client certificate.

Get-ChildItem -Path cert:\localMachine\my\7ED035ABAB1B8CCDFF561935D3C55BE91EAB3DFB | Export-PfxCertificate -FilePath "C:\CloudItems\Learning\CSharpCorner\Article 62\clientcert.pfx" -Password $mypwd

Using Certificates for API authentication in .NET 5

Now, we have both the CA certificate and the client certificate.

Let us now add the CA certificate to our machine. Click Start and type “Manage Computer Certificates”. You will see the below,

Using Certificates for API authentication in .NET 5

Select “Trusted Root Certification Authorities- Certificates – All Tasks- Import” and add the “cacert.pfx” to it.

You will be asked to provide the password. Here provide the password used to generate the “cacert.pfx” file. After the import is done, you will see the below entry in the “Trusted Root Certification Authorities- Certificates”.

Using Certificates for API authentication in .NET 5

You are now ready to design your solution.

Create a new empty API controller project using C# in Visual Studio 2019. I use the free Community Edition.

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Next, add a new class called “CertificateValidation” and add the below code to it,

using System;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
namespace WebAppCerts {
    public class CertificateValidation {
        public bool ValidateCertificate(X509Certificate2 clientCertificate) {
            string[] allowedThumbprints = {
                "7ED035ABAB1B8CCDFF561935D3C55BE91EAB3DFB"
            };
            if (allowedThumbprints.Contains(clientCertificate.Thumbprint)) {
                return true;
            }
            return false;
        }
    }
}

You will need to replace the client certificate thumbnail in the string array called “allowedThumprints” above.

Next, add the below Nugget package.

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Next, update the Startup.cs file as below,

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebAppCerts {
    public class Startup {
        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.AddControllers();
            services.AddTransient < CertificateValidation > ();
            services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(options => {
                options.AllowedCertificateTypes = CertificateTypes.SelfSigned;
                options.Events = new CertificateAuthenticationEvents {
                    OnCertificateValidated = context => {
                            var validationService = context.HttpContext.RequestServices.GetService < CertificateValidation > ();
                            if (validationService.ValidateCertificate(context.ClientCertificate)) {
                                context.Success();
                            } else {
                                context.Fail("Invalid certificate");
                            }
                            return Task.CompletedTask;
                        },
                        OnAuthenticationFailed = context => {
                            context.Fail("Invalid certificate");
                            return Task.CompletedTask;
                        }
                };
            });
            services.AddSwaggerGen(c => {
                c.SwaggerDoc("v1", new OpenApiInfo {
                    Title = "WebAppCerts", Version = "v1"
                });
            });
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAppCerts v1"));
            }
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => {
                endpoints.MapControllers();
            });
        }
    }
}

As we will be using Kestrel to run this application, let us set it to use certificates. Update the Program.cs file as below,

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.Hosting;
namespace WebAppCerts {
    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.UseStartup < Startup > ();
            webBuilder.ConfigureKestrel(o => {
                o.ConfigureHttpsDefaults(o => o.ClientCertificateMode = ClientCertificateMode.AllowCertificate);
            });
        });
    }
}

Finally, let us create two Controller endpoints. One will require a certificate to work and the other will not. Create a new empty API controller called “TestController” as below,

Using Certificates for API authentication in .NET 5

Add the below code to the new controller,

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace WebAppCerts.Controllers {
    [Route("api/[controller]")]
    [ApiController]
    public class TestController: ControllerBase {
        [HttpGet]
        public string Get() => "The action works fine without a certificate";
        [Authorize]
        [HttpPost]
        public string Post() => "The action works fine only with a certificate";
    }
}

The code is now done, and we will test it using Postman. Run the application using Kestrel (The project name)

Using Certificates for API authentication in .NET 5

Using Certificates for API authentication in .NET 5

Let us now test using Postman,

Using Certificates for API authentication in .NET 5

The Get action works fine as this does not need a certificate.

However, the POST call will give a 403 Forbidden message as it requires a certificate.

Using Certificates for API authentication in .NET 5

Let us attach the client certificate to the POST request.

Using Certificates for API authentication in .NET 5

Select the grid on the top right corner and then select “Certificates” and add Certificate.

Here, add the client certificate as below:

Using Certificates for API authentication in .NET 5

The certificate is now added as below,

Using Certificates for API authentication in .NET 5

Now, when we make the POST request, all works fine as below,

Using Certificates for API authentication in .NET 5

Summary

In this article, we looked at how to generate a self-signed certificate for the Certification Authority and then how to generate a client certificate from it. Next, we saw how to protect our .NET 5 WEB API endpoints using these certificates. This provides a way to secure our endpoints. I tested it using Postman and in a future article, I will demonstrate how to attach the client certificate and make this call using a .NET 5 client. Happy coding!