ASP.NET MVC 3 : Implementing PKI Authentication

This website is no longer actively supported

Written by John DeVightJohn DeVight on 13 Jun 2011 18:10

Download RAZOR Source Code
Download ASPX Source Code

Overview

I am working on a project where PKI authentication is required for the application. I've implemented this in ASP.NET Web Form Applications using an HttpModule, but not in ASP.NET MVC. I tried using an HttpModule, but wasn't able to get it to work properly. I searched on the internet I read that it is recommended that a global ActionFilter is used instead of an HttpModule. I created a class called AuthorizationFilter that implements IAuthorizationFilter interface. The interface has one method, OnAuthorization. I was able to implement code very similar to what I had done in an HttpModule to check that a client certificate is present, validate the certificate's Distinguished Name (DN) and allow access to the website. If the certificate was missing or invalid, then I redirected the user to an unauthorized page.

Implementing the AuthorizationFilter

In the sample application, the verification of the certificate DN is simply checking a hard-coded DN in the source code. Normally I would query the database to see if the certificate DN is registered, return the user information and set the HttpContext.Current.User to an instance of a model that implements the IPrincipal interface. Also, to keep the sample application simple and straight forward, I throw an HttpException in the sample application if the certificate DN is invalid or if the client certificate is missing (which shouldn't happen if IIS is configured to require client certificates.

Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Security.Cryptography.X509Certificates;

namespace MvcRazorApp.ActionFilters
{
    public class AuthorizationFilter : IAuthorizationFilter
    {
        private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(typeof(AuthorizationFilter));

        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (_logger.IsInfoEnabled) _logger.Info("OnAuthorization");

            HttpContext context = HttpContext.Current;

            if (context.Request.ClientCertificate.IsPresent)
            {
                X509Certificate2 userCert = new X509Certificate2(context.Request.ClientCertificate.Certificate);
                X500DistinguishedName dn = userCert.SubjectName;
                string userDn = dn.Name.Trim().ToUpper();

                if (_logger.IsInfoEnabled) _logger.Info(string.Format("OnAuthorization : UserDn = {0}", userDn));

                if (userDn != "CN=JOHN DOE")
                {
                    throw new HttpException(401, "Invalid Client Certificate");
                }
            }
            else
            {
                throw new HttpException(401, "Client Certificate Missing");
            }
        }
    }
}

Instantiating the AuthorizationFilter in Global.asax

Now that we have our AuthorizationFilter class, it needs to be instantiated and added to the GlobalFilters in the Global.asax, Application_Start method.

Here is the code:

protected void Application_Start()
{
    log4net.Config.XmlConfigurator.Configure();

    AreaRegistration.RegisterAllAreas();

    GlobalFilters.Filters.Add(new MvcAspxApp.ActionFilters.AuthorizationFilter());

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

References

Comments

Add a New Comment

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License