REST API (HTTPS) Client Certificate Authentication (.Net Core, C# )

Arun Kumar
6 min readNov 14, 2020

--

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 secure authentication for the clients:

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

Please note, digital certificates are commonly used for initiating the secure SSL connection with the webserver. The 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:

  1. Keep the server-side certificate in the application folder and then implement the below code in Program.cs .
  2. Program.cs
//Program.csvar 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 many times we face the problem where to store the client certificate and how to validate/authenticate cert too.

2- Implement the Client Certificate Authentication.

  1. How to create the self-sign certificate for testing purpose? (OR get the CA-signed certificate)
  2. Where to store the client certificates at the server for validation?
  3. 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 on 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 a 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 validation?

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.csp.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 : IMyCertValidationpublic 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 request 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:

Issues Noticed: During its implementation under a specific service user (E.g- svcSsshUser, we noticed that giving access to other users on the folder C:/Users/svcSsshUser/.ssh/authorized_key will holt the situation and will keep showing permissions issues while connecting from ssh client.

The easiest way to enable login at ssh server by changing the sshd_config file in C:/Prgram Data/SSH then came to know which sid is trying to access the authorized_key file and what exact error had with “bad permission …”.

If you face such issues please contact me.

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. The 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

--

--