Written by John DeVight on 2011-May-18
Download Source Code
Overview
Last year I needed to implement PKI authentication on a project. Initially I was worried that this would be a major task to accomplish, but was pleased to discover how easy it was to do. I created an HttpModule called AuthenticationModule that subscribed to the HttpApplication.AuthenticationRequest event. The event handler checked to see if the 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 AuthenticationModule
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.Security.Cryptography.X509Certificates;
namespace WebFormApp.HttpModules
{
public class AuthenticationModule : IHttpModule
{
private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(typeof(AuthenticationModule));
public void Dispose()
{
}
public void Init(HttpApplication context)
{
if (_logger.IsInfoEnabled) _logger.Info("Init");
// Add handler for the Authenticate event
context.AuthenticateRequest += AuthenticateRequestHandler;
}
void AuthenticateRequestHandler(object sender, EventArgs e)
{
if (_logger.IsInfoEnabled) _logger.Info("AuthenticateRequestHandler");
HttpApplication app = (HttpApplication)sender;
HttpContext context = app.Context;
X509Certificate2 userCert = null;
// If client certificate is presented...
if (context.Request.ClientCertificate.IsPresent)
{
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");
}
}
}
}
Web.config Configuration to Register the HttpModule
Now that we have our AuthenticationModule, it needs to be registered in the web.config. Settings are added in two places in the web.config file, /configuration/system.web/httpModules and /configuration/system.webServer/modules.
Here are excerpts from the web.config:
<configuration>
<system.web>
<httpModules>
<add name="PkiAuthentication" type="WebFormApp.HttpModules.AuthenticationModule, WebFormApp" />
</httpModules>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="PkiAuthentication" type="WebFormApp.HttpModules.AuthenticationModule, WebFormApp" />
</modules>
</system.webServer>
References
- Configuring IIS 7 with Self-Signed Server and Client Certificate
- ASP.NET MVC 3 : Implementing PKI Authentication
Support ASP.NET Wiki
If you like this page, click on the "Share on" links in the wikidot toolbar at the top of the page to share it with your friends.