Using client certificates in .NET part 8: working with client certificates in OWIN/Katana II

Introduction

In the previous post we started adding the necessary OWIN-related libraries to our Web API project: a couple of NuGet libraries and the Startup class. We publish our application to the local IIS and it doesn’t allow us to break the code within Startup.cs. We’ll soon see that we can still debug the OWIN components further down the call stack.

In this post we’ll add the necessary elements to a client certificate based authentication in .NET MVC with OWIN.

Preparations

Open the demo Web API project in Visual Studio as an administrator. Install the Microsoft.Owin.Security DLL from NuGet to the web project:

Install microsoft owin security DLL from NuGet

This package includes the security related classes in OWIN without which we cannot build the custom authentication mechanism.

Let’s remove all the security related demo code from CustomersController to as the next step. Normally your controllers should have no security related logic. Here’s the revised controller:

public class CustomersController : ApiController
{
	[Authorize]
	public IHttpActionResult Get()
	{
		IList<Customer> customers = new List<Customer>();
		customers.Add(new Customer() { Name = "Nice customer", Address = "USA", Telephone = "123345456" });
		customers.Add(new Customer() { Name = "Good customer", Address = "UK", Telephone = "9878757654" });
		customers.Add(new Customer() { Name = "Awesome customer", Address = "France", Telephone = "34546456" });
		return Ok<IList<Customer>>(customers);
	}
}

Note, however, that we’ve decorated the Get method with the familiar Authorize attribute. We only want people with a valid client certificate to be able to view the customers list.

Then insert a folder called Authentication to the Web API project. We’ll put all our authentication related classes there.

AuthenticationOptions

The OWIN namespace has a number of security related classes that we’ll see in this post. The first one is called AuthenticationOptions. It is a base class for all types of authentication options, such as CookieAuthenticationOptions, GoogleAuthenticationOptions or OpenIdConnectAuthenticationOptions. If you’d like to build your own OWIN authentication then this is the starting point.

The minimum requirement here is to derive from the AuthenticationOptions which has no virtual methods. You only need to have a constructor which accepts a string parameter which describes the authentication type. However, you are free to insert any properties and constructors as you wish. Here we’ll keep it at a minimum.

Add a class called ClientCertificateAuthenticationOptionsinto the Authentication folder:

public class ClientCertificateAuthenticationOptions : AuthenticationOptions
{
	public ClientCertificateAuthenticationOptions() : base("X.509")
	{}
}

We simply declare our authentication type in line with X509 certificates.

The authentication handler

The next class that we need to derive from is AuthenticationHandler. It is the base class for the work carried out by OWIN authentication middleware. We’ll see shortly that the implemented authentication middleware must return an authentication handler that derives from AuthenticationHandler.

AuthenticationHandler is a generic class with a type parameter. The type parameter must derive from AuthenticationOptions we discussed above. The minimum requirement is to implement the authentication logic in the AuthenticateCoreAsync method. It returns a Task of AuthenticationTicket object, i.e. it is implicitly assumed that the authentication logic will be called asynchronously in some resource intensive operation such as a database lookup.

An AuthenticationTicket, as its name implies “contains user identity information as well as additional authentication state” according to the MSDN documentation. The constructor of this object requires a claims identity – represented by the ClaimsIdentity object – and the authentication properties, which are contained by the AuthenticationProperties object.

Claims-based authentication is a large topic and there are multiple posts dedicated to it on this blog. If you don’t know what claims are then you can start here. You can also check out the Security and Cryptography index page referred to at the end of this post to find claims-related posts. Basically a claim in the world of authentication and authorisation can be defined as a statement about an entity, typically a user.

Add a class called ClientCertificateAuthenticationHandler into the Authentication folder:

public class ClientCertificateAuthenticationHandler : AuthenticationHandler<ClientCertificateAuthenticationOptions>
{
	private readonly IClientCertificateValidator _clientCertificateValidator;
	private readonly string _owinClientCertKey = "ssl.ClientCertificate";

	public ClientCertificateAuthenticationHandler(IClientCertificateValidator clientCertificateValidator)
	{
		if (clientCertificateValidator == null) throw new ArgumentNullException("ClientCertificateValidator");
		_clientCertificateValidator = clientCertificateValidator;
	}

	protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
	{
		ClientCertificateValidationResult validationResult = await Task<ClientCertificateValidationResult>.Run(() => ValidateCertificate(Request.Environment));
		if (validationResult.CertificateValid)
		{
			AuthenticationProperties authProperties = new AuthenticationProperties();
			authProperties.IssuedUtc = DateTime.UtcNow;
			authProperties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
			authProperties.AllowRefresh = true;
			authProperties.IsPersistent = true;
			IList<Claim> claimCollection = new List<Claim>
			{
				new Claim(ClaimTypes.Name, "Andras")
				, new Claim(ClaimTypes.Country, "Sweden")
				, new Claim(ClaimTypes.Gender, "M")
				, new Claim(ClaimTypes.Surname, "Nemes")
				, new Claim(ClaimTypes.Email, "hello@me.com")
				, new Claim(ClaimTypes.Role, "IT")
				, new Claim("HasValidClientCertificate", "true")
			};
			ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection, "X.509");
			AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, authProperties);
			return ticket;
		}
		return await Task.FromResult<AuthenticationTicket>(null);
	}

	private ClientCertificateValidationResult ValidateCertificate(IDictionary<string, object> owinEnvironment)
	{
		if (owinEnvironment.ContainsKey(_owinClientCertKey))
		{
			X509Certificate2 clientCert = Context.Get<X509Certificate2>(_owinClientCertKey);
			return _clientCertificateValidator.Validate(clientCert);
		}

		ClientCertificateValidationResult invalid = new ClientCertificateValidationResult(false);
		invalid.AddValidationException("There's no client certificate attached to the request.");
		return invalid;
	}
}

…where we have a couple of new elements. The client certificate validation logic is abstracted away to an interface for loose coupling:

public interface IClientCertificateValidator
{
	ClientCertificateValidationResult Validate(X509Certificate2 certificate);
}

The ClientCertificateValidationResult is a simple object to store whether the certificate is valid or not and a list of validation exceptions:

public class ClientCertificateValidationResult
{
	private readonly bool _certificateValid;
	private readonly IEnumerable<string> _validationExceptions;

	public ClientCertificateValidationResult(bool certificateValid)
	{
		_certificateValid = certificateValid;
		_validationExceptions = new List<string>();
	}

	public void AddValidationExceptions(IEnumerable<string> validationExceptions)
	{
		_validationExceptions.Concat(validationExceptions);
	}

	public void AddValidationException(string validationException)
	{
		_validationExceptions.Concat(new[] { validationException });
	}

	public IEnumerable<string> ValidationExceptions
	{
		get
		{
			return _validationExceptions;
		}
	}

	public bool CertificateValid
	{
		get
		{
			return _certificateValid;
		}
	}
}

Let’s go through the ClientCertificateAuthenticationHandler class a little bit. It derives from AuthenticationHandler of T where T is ClientCertificateAuthenticationOptions, this is in line with what we wrote in the short introduction of this section. The class has a loose dependency on IClientCertificateValidator which will perform the actual client certificate validation. We also store a key called _owinClientCertKey with the value “ssl.ClientCertificate”. If you are new to OWIN then you probably won’t understand what this is all about.

You can read about the very basics of OWIN and Katana in an introductory series starting here. Basically, in the OWIN middleware chain there’s an object called the “environment”. It is a dictionary with a key of type string and a value of type object. It contains all sorts of entries about the incoming web request. In addition, each middleware in the OWIN chain can add their own objects with a certain key. Some elements have shortcuts available through the built-in Request property, e.g. the headers or the query string. You’ll see a call for “Request.” in the following bit:

ClientCertificateValidationResult validationResult = await Task<ClientCertificateValidationResult>.Run(() => ValidateCertificate(Request.Environment));

If you type “Request.” then you’ll see some shortcuts, like Request.Headers which are really only wrappers around the more direct key-based technique that we have to use for the client certificate.

So, the client certificate sent by the client is not available from some request property, you’ll need to look for it with a key, and that key is “ssl.ClientCertificate”. That’s what you’ll see in the ValidateCertificate method:

if (owinEnvironment.ContainsKey(_owinClientCertKey))
{
	X509Certificate2 clientCert = Context.Get<X509Certificate2>(_owinClientCertKey);
	return _clientCertificateValidator.Validate(clientCert);
}

Every value in the environment dictionary is an object so it needs to be cast to its “proper” type, in this case an X509Certificate2 object. If there’s a client certificate in the web request then it is sent for validation to the certificate validator. Otherwise we return a message saying that there was no client certificate.

As you can see we retrieve the client certificate from the web request differently from how we extracted it within an MVC – or Web API – controller.

If the user is authorised, i.e. the certificate has passed the validation phase, then we build the AuthenticationTicket object. The code shows an example of building an AuthenticationProperties object. Those are of course not meaningful in our demo specifically. We also build a list of claims and you’ll see that a claim consists of a key and a value as noted above. The ClaimTypes class contains a list of well-known claim types such as Name, Email, Role etc. Those are all strings, therefore you can specify your own claim category as you wish, such as “www.mycompany.com/claims/favourite-colour”.

We return null in case authentication fails.

How do we link up all these elements with OWIN? We’ll find it out in the next post which also finishes this series.

You can view the list of posts on Security and Cryptography here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

6 Responses to Using client certificates in .NET part 8: working with client certificates in OWIN/Katana II

  1. I am trying to do this running the OWIN app stand-alone without IIS, but the ssl.ClientCertificate is never populated in the environment. Is there something that is required in the App.config to allow or require the client certificate?

  2. korn3l says:

    You have hardcoded the claims collection, but how could you make this dynamic? Do you store the email in the certificate (how? where?) and get the user from a database or do you store the claims in the certificate itself ?

    • Reinhard Brongers says:

      You don’t (and can’t) modify a certificate. The certificate is send by the client to the server to prove his identity (instead of e.g. basic authentication). The point in this blog is to get the client certificate in the authentication pipeline. What happens after that to find a user and issue some claims is a separate concern (fair enough beyond the scope of this post). Obviously you would not hardcode a claimcollection, but probably read the user from a database (maybe based on the subject in the certificate if it’s a user cert or whatever) and create a claimscollection out of the result. Examples on how to do that (i.e. using a userdatabase for claims) can be found in several other internet blogs and posts. I can recommend looking at IdentityServer for a full-fledged and flexible solution. But I also recommend reading up on the use and role of certificates (e.g. PKI) before entering the playground šŸ™‚
      HTH

  3. suhassk100 says:

    There is a problem with ValidateCertificate() if i fire multiple requests. If i fire a single request from the client it works fine. If i fire multiple requests, ValidateCertificate() is entered but never exits. And after sometime I get this error “System.Net.Http.HttpRequestException: Error while copying content to a stream. —> System.IO.IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.”

  4. Martin says:

    When setting authProperties.ExpiresUtc to a specified time / date in the future, what uses this? I’ve tried setting this value and it still comes into this method for re-authentication. If I set this value to 5 minutes for example, how do I use this to delay the users authentication for 5 minutes? Thanks

  5. Ryan Lege says:

    I implemented this solution and tried using the AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
    However, the user is still authenticated. How do I force the Owin pipeline to reset?

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.