This website is no longer actively supported
Written by John 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
- Configuring IIS 7 with Self-Signed Server and Client Certificate
- http://aspnet.wikidot.com/asp-net-webforms:implementing-pki-authentication
Hello,
It's a great article.
I made all of this steps listed by you here, but I have a problem: when I browse the site "Client Certificate Missing" exception is throw, even if I had certificate installed and configured to work with IIS 7. Can you help me to figure out what is the problem.?
Thank you very much.
Hi Marina,
I'm really sorry I missed this comment / question. Are you still having problems? Let me know if I can still help in any way.
Regards,
John DeVight
Telerik MVP
I figured out the problem, thank you very much anyway. You helped me with this article :)
I am also seeing a problem where context.Request.ClientCertificate.IsPresent is always false despite being prompted for and selecting a certificate. Any suggestions?
Hi
I am also seeing the same problem . Can you advise what was done to overcome it
Hi John,
Thank you for the great article!
I'm implementing PKI authentication in an MVC3 application running on Windows Azure. The problem is that, I don't know how to set up my iis on windows azure to ask clients certificates.
Do you have any idea?
Thanks!
Hi Michel,
Unfortunately I haven't had the opportunity to work with Windows Azure and am unfamiliar with what issues would come up.
Regards,
John DeVight
Telerik MVP
Hi
I am using a similar filter for my MVC3 application , I have written controllers which return a xml . We are using this as an Api . I had problems using WebApi with Autofac and Nhibernate hence i didnt pursue it . But now i have another problem at hand ,
My detailed post is here
I am trying to test my MVC3 Api which we build without using WebApi , We use normal MVC3 controllers to return an Xml data. Below is the code for the client . The certpath contains a abc.crt file .
// create certificate
var cert = new X509Certificate2(_settings.ApiSslCertPath);
ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };
var request = (HttpWebRequest)WebRequest.Create(_settings.AppUrl + "/api/GetContractsById/" + contractId);q
request.ClientCertificates.Add(cert);
request.Method = "GET";
var response = (HttpWebResponse)request.GetResponse();
Now when this request hits the Api , I am checking if the context.Request.ClientCertificate.IsPresent but its always false.
HttpContext context = System.Web.HttpContext.Current;
if (context.Request.ClientCertificate.IsPresent)
My WebConfig has
<security>
<access sslFlags="SslNegotiateCert"/>
<authentication >
<iisClientCertificateMappingAuthentication enabled="true" oneToOneCertificateMappingsEnabled="true">
</iisClientCertificateMappingAuthentication>
</authentication>
</security>
and the IIS has ClientCertificate Settings to Accept. And the Applicationhost.config has Allow for access section.
Can you please advise if I am doing anything wrong
Regards