Implementing Client Certificate Authentication with IIS and ASP.NET

Arseni Rynkevich
8 min readApr 28, 2020

--

Client certificate authentication is the part of a two-way TLS/SSL cryptographic protocol. “Two-way” means that a server and a client perform mutual certificate checks during the authentication process. Client certificate, just like server certificate, provides some information about the client’s identity, provides its public key and is digitally signed by a Certification Authority (aka CA) to verify the authenticity of the information it contains. It can be used to restrict access to the server on the session layer (in terms of the OSI model), making only the company’s customers or partners operate with its system.

Though this concept is described in many articles, I have not found much information on the two-way TLS/SSL implementation with ASP.NET and the nuances you need to know to make it work. This is why I decided to share my experience here. But first, let’s have a quick look at some general aspects.

High-Level Setup

In a production environment, the interaction between all three parties of the protocol may look like this:

In part a) the client creates CSR, or Certificate Signing Request — a special message to Certification Authority. CA, in its turn, responds with a valid signed certificate.

In part b) of the communication, CA exposes the certificate it uses to issue client certificates. The server saves it in a trusted CA certificate store to verify signatures on client certificates, signed by this particular CA.

When both a) and b) parts are finished, the client can access the server using the issued client certificate.

Getting a client or CA certificate is usually a manual process, as the certificate most likely won’t change often. Its expiration time could be a few years or more.

All certificates involved in the protocol have X.509 compliant format. .cer and .pvk files or a .pfx file can represent this kind of a certificate and a corresponding private key. Using two files allows to transfer public and private information separately. The following picture shows how this information is shared between protocol parties.

Client and CA private keys, generated on their sides, are never shared with anyone else. They are secrets, allowing anyone who has access to them to identify himself as the original owner of the key. CA’s certificate is public, but only the server needs it to verify CA’s signature on the client certificate. The latter is issued by CA and provided by the client to the server during the authentication. Therefore it’s shared between all three parties.

We’ll omit the details of the authentication algorithm as its description is not the purpose of this post. Instead, we’ll take a closer look at the configuration process.

Configuration

The following configuration algorithm is described for Windows 10 and IIS 10. However, it’s almost the same on other Windows and IIS versions.

1. Generation and installation of a CA certificate on a server

First, we need a CA certificate and a private key. To create those for testing purposes we can choose makecert utility from Windows SDK or New-SelfSignedCertificate PowerShell cmdlet. Let’s use makecert here as we’ll need it later to create a certificate chain.

makecert -r -n "CN=TestCA" -sv TestCA.pvk TestCA.cer

This command generates a self-signed certificate (-r) issued to TestCA (-n), provides a private key file (-sv) saved as TestCA.pvk, and a certificate file TestCA.cer. A small dialog box will ask for a password. As it’s a test certificate, we’ll omit the password by selecting the “None” option.

Windows has special certificate stores for machine and each user. We’ll have to register the CA certificate in the server’s local machine store. In “Start” menu look for “Manage computer certificates”. In opened manager import created certificate to “Trusted Root Certification Authorities”.

Follow the Certificate Import Wizard navigation until it asks for a certificate, then choose the TestCA.cer file and continue. After finishing import you will see the CA certificate in a store viewer.

Note: if there is a certificate in “Trusted Root Certification Authorities” store that is not self-signed, the authentication won’t work. For example, this could happen if you import a client certificate to a wrong store. You can found such certificates using PowerShell command

Get-ChildItem cert:\LocalMachine\root -Recurse | Where-Object {$_.Issuer -ne $_.Subject}

Then delete it in certificate manager.

Note: if a client certificate is signed with CA certificate, which is not a root certificate, i.e. not the first certificate in the chain (which we will discuss in the next section), the root should be imported to “Trusted Root Certification Authorities” and other certificates between root and issuer should be imported to “Intermediate Certification Authorities” store.

2. Generation and export of a client certificate

With CA’s certificate and private key we can emulate CA actions and issue a new client certificate.

At this point we should understand that we’re creating a certificate chain (certificate path).

The certificate chain starts with root, can include multiple intermediate CA certificates, and ends with an end-entity certificate. Chain elements are linked by signatures: each element is signed by its predecessor and contains its name to indicate it. We’ll have only the root CA certificate and the client certificate in our chain.

makecert -iv TestCA.pvk -n "CN=TestClient" -ic TestCA.cer -ss my -pe

Another makecert command issues a client certificate to TestClient (-n) using TestCA.pvk private key and TestCA.cer certificate of CA (-iv, -ic) with a generated exportable private key (-pe) and saves it to the current user’s “Personal” certificate store (-ss). To find and export it, in “Start” menu look for “Manage user certificates”, navigate to the corresponding store, and select the “Export” task.

Follow the Certificate Export Wizard navigation, confirm that the private key should also be exported.

On the “Export File Format” step it’s possible to include the whole certificate path in the exported file by choosing “Include all certificates in the certification path if possible”. We’ll only export the client certificate.

This time it’s obligatory to use password.

After finishing all steps the certificate is exported in .pfx format. This way it could be installed on another machine the same way the CA certificate was installed. The only difference in the import process is choosing a “Personal” store.

3. Web server configuration

IIS has three types of behavior for client certificates handling:

  • Ignore. The certificate is not required and won’t be accessible in an application even if provided by the client.
  • Accept. The certificate is not required but will be verified if provided by the client and passed to the application to perform additional optional checks.
  • Require. The certificate is required and will be verified on the web server level and passed to the application to perform additional optional checks.

For IIS Express this behavior could be specified in the applicationhost.json configuration file. For sample HelloWorldApp it is located in <projectDirectory>/.vs/HelloWorldApp/config.

There is an <access> tag in <security> section. SSL is configured with sslFlags attribute. None disables SSL, Ssl value enables it, added SslNegotiateCert enables “Accept” behavior and two previous values with SslRequireCert enable “Require” behavior.

This way to choose “Require” applicationhost.json should contain

<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />

Also, inside <authentication> section of <security> the following tag is required:

<iisClientCertificateMappingAuthentication enabled=”true”></iisClientCertificateMappingAuthentication>

And that’s it. For standard IIS the same configuration is done via GUI:

4. Testing client authentication

Now it’s time to test authentication. First, let’s try internet browser. If we navigate to any URL of the configured web application, we’ll be asked to choose a certificate from the “Personal” store. As we’ve created a TestClient certificate there, it’ll appear in a dialog window. In Google Chrome the window looks like this:

Clicking on “Cancel” or closing the window will send nothing as a certificate and with “Require” behavior an error will appear.

Selecting the TestClient certificate will send a valid certificate and we’ll see an application page.

Let’s try the same thing with Postman. Without the client certificate the application returns an error page, as expected.

To use the certificate, we navigate to the “Certificates” tab in Settings.

In “Add certificate” section we choose the TestClient.pfx file and specify the password that was used during the export.

When the certificate is added, the application will respond with a page source to the same request.

It’s required to provide either a .pfx or a pair of .cer and .pvk files to authenticate. You may ask: “Why should I share my private key? Isn’t it a secret?”. In fact, the private key is not shared with anyone. It’s is used during a mutually-authenticated TLS handshake to encrypt arbitrary value, which could be decrypted by the server with the public key to verify that request sender is actually the owner of the certificate. See RFC 5246 for more information on the TLS handshake protocol.

5. Inspecting the certificate on application level

When it’s not enough to check if the certificate’s CA is in a trusted list or only some of the endpoints have to be protected with client certificate authentication, we can always extend verification logic in the application.

HttpClientCertificate object exists on current HttpRequest. The following example shows a way to check the common name of a certificate subject using ASP.NET infrastructure (HttpModule). In ASP.NET Web API or MVC the same thing could be done with an authentication filter.

And that’s the way of implementing client certificate authentication with IIS and ASP.NET.

Thanks for reading this post! Feel free to leave your questions in the comments below.

--

--

Arseni Rynkevich

Proactive Software Engineer, currently living in Minsk, Belarus.