REST API (HTTPS) Client Certificate Authentication (.Net Core, C# )
By: Arun Kumar
Tags: Rest Api, C Sharp Programming, Httpscertificate, Net Core, Https

Client certificate authentication (Image)

Every organisation increasing engages with their partners, customers and employees through the web-based applications/REST API and secure the access of your API is most important. Security threats not only disrupt the applications but also impact the organisation’s market value and its revenue too.

The purpose of this story is to share the knowledge on how to make your web API (.net core) secure at the server level and also implement the secure authentication for the clients:

Implement the security at the server level (HTTPS:\\). Implement the Client Certificate Authentication.

Please note, digital certificates are commonly used for initiating the secure SSL connection with the webserver. Below image shows the standard client authentication — how it works between client and server using the certificate.

Client Handshake (source)

1- Implement security at the server level.

The server-level SSL authentication is simply covered in below few steps:

Keep the server certificate in the application folder and then implement the below code in Program.cs . Program.cs

//Program.cs var certName = config.GetValue<string>("CertName");

var certKey = config.GetValue<string>("CertKey"); var Digicertificate = new X509Certificate2(AppContext.BaseDirectory + certName, certKey);

webBuilder.ConfigureKestrel(p =>

{p.ConfigureEndpointDefaults(listenopts =>

{ listenopts.UseHttps(Digicertificate);

});

webBuilder.UseKestrel().UseUrls(new string[] { " https://localhost:8999 " });

My main focus was to cover the client authentication as may times we face the problem where to store the client certificate and how to validate/authenticate cert too.

2- Implement the Client Certificate Authentication.

How to create the self-sign certificate for testing purpose? (OR get the CA-signed certificate) Where to store the client certificates at the server for validation? Implementation at the code level in .net Core.

How to create the self-sign certificate for testing purpose?

For testing purpose, we can generate the self-sign certificate-based the below commands or help available in MS documents.

Execute the below command on Powershell:

New-SelfSignedCertificate -Type Custom -Subject “CN=TestCert,OU=UserAccounts,DC=test,DC=User,DC=com” -TextExtension @(“2.5.29.37={text}1.3.6.1.5.5.7.3.2”,”2.5.29.17={text}upn=testusername@gmail.com”) -KeyUsage DigitalSignature -FriendlyName “TestCleint1” -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation “Cert:\CurrentUser\My”

A self-sign certificate is created that you can check in Manage User Certificates by typing certificate in windows search.

You have test certificate with the name of TestCert that you can export as *.pfx file and assign a password too.

Once you have TestCert.pfx file that you need to import in the local computer certification store as in my program my code reads the certificates from local computer stores. It depends as few developers can use the drive or application folder to store the certificates.

Where to store the client certificates at the server for valdiation?

Press the Windows key + R to bring up the Run command, type certmgr and press enter.

When the Certificate Manager console opens, expand any certificates folder on the left. In the right pane, you’ll see details about your certificates. Right-click on them and you can import the certificate that exported TestCert.pfx

Note: The above step is important as in code for authentication it checks the Client certificate at the Local Computer Certificate store hence needs to import client’s certificate here otherwise Client authentication will be failed.

// MyCertValidation service class in which below statement is used to get the certificates stored from LocalMachine. var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);

Implementation at the code level in .net Core:

Update the ClientCertificateMode.RequireCertificate; that ensure the certificate is required while the client is accessing the API URI.

//Program.cs p.ConfigureHttpsDefaults(q =>

{

q.ClientCertificateMode = ClientCertificateMode.RequireCertificate;

q.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;



});

});

Certification Validation Service that contains the validation method is ingested as a service first then call the method ‘ValidateCertificate()’ for validating the client certificate which came with the request to the stored client certificate.

//Worker.cs

public void ConfigureServices(IServiceCollection services)

{

//Add Certification Validation Service

services.AddSingleton<IMyCertValidation, MyCertificateValidationService>();

services.AddControllers(); services.AddAuthentication(

CertificateAuthenticationDefaults.AuthenticationScheme)

.AddCertificate(options =>

{

options.AllowedCertificateTypes = CertificateTypes.All;

options.Events = new CertificateAuthenticationEvents

{

OnCertificateValidated = context =>

{

//Leveraging the DP injected single instance of the CertificateValidation Service

var validationService = context.HttpContext.RequestServices.GetService<IMyCertValidation>(); // Here it validate and call the methode ValidateCertificate from MyCertValidation class. if (validationService.ValidateCertificate(context.ClientCertificate, Appconfig))

{

context.Success();

}

else

{

context.Fail("Invalid certificate");

}

return Task.CompletedTask;

},

OnAuthenticationFailed = context =>

{

context.Fail("Invalid certificate");

return Task.CompletedTask;

}

}; });

MyCertValidation Class has a validation method -public bool ValidateCertificate ().

Simple Approach: This method first search the stored/installed client certificates -> And then checks certificate’s expiry -> Also, notify in case certificate is going to expire in 30 days.

If no certificate exists in LocalMachine OR the certificate is expired then client authentication will be failed.

// Class MyCertValidation : IMyCertValidation public bool ValidateCertificate(X509Certificate2 clientCertificate)

{

try

{

// Reads the installed client certificates

var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);

store.Open(OpenFlags.ReadOnly);

var certficate = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == clientCertificate.Thumbprint);

store.Close(); // Maching certificate will authenticated with its expiry date too

if (certficate == null)

{

Logger.LogError("Install the client certificate first. Certificate- Issuer :{0}, Certificate Thumbprint: {1}", clientCertificate.Issuer, clientCertificate.Thumbprint);

} // Check the certificate expry

if ((certficate != null) && (clientCertificate.Thumbprint == certficate.Thumbprint) && (System.DateTime.Today <= System.Convert.ToDateTime(certficate.GetExpirationDateString())))

{

if(System.Convert.ToDateTime(certficate.GetExpirationDateString()).AddDays(-30) <= System.DateTime.Today)

{

Logger.LogWarning("Certificate is about to expire in {0} days. Issuer :{1}", (System.Convert.ToDateTime(certficate.GetExpirationDateString()) - System.DateTime.Today).Days, certficate.Issuer); }

return true;

}

return false;

}

catch (Exception error)

{

Logger.LogError("Failed to authenticate the cleint certificate {0}",error.Message);

return false;

}

Performed the tests by using the postman by installing the same client certificate (which was installed in the Local Machine store at the server) and then send the requested which authenticated successfully.

For a negative authentication test, you can uninstall/ remove the certificate from the server using the Certificate Manager.

NOTE: Install the client certificates at the local machine store, not in the user certificate store:

Summary

This article enables you to implement the client certificate authentication with the REST API. Also, help how to use a self-signed certificate for testing purpose. Most important aspect if you have a client certificate installed at the local machine certificate store (@Server), will not require many changes in the config file or at the code level in case, certificate get changes.

Please give your feedback to make better content for future usage.

Warm regards,

Arun Kumar