Avoiding X.509 chain policy caching when using WCF with certificate security

A while ago my customer came to me with an issue that was a bit tricky to find a solution to.

Their system uses a Windows forms client and a WCF service running on IIS7 on a Windows 2008 R2 server. The users are provided with personal certificates stored on smart cards. Those certificates are then used to authenticate the users with the WCF service using their AD accounts.

The issue my customer was facing was:
1. New employees could not log on to the system until the application pool of the web site running the WCF service was recycled.
2. Employees who got their certificates revokeed COULD log on to the system the application pool of the web site running the WCF service was recycled.

Obviously a (totally unwanted) certificate cache of some sort was causing this behavior, so after digging around a bit I found out that System.IdentityModel was caching the X.509 chain policy in memory.

The relevant web.config settings for the WCF service looked like this:

<clientCertificate>
	<authentication mapClientCertificateToWindowsAccount="true" certificateValidationMode="ChainTrust" revocationMode="Online"/>
</clientCertificate>

To solve this issue I decided to implement a custom X.509 validator that builds a X509Chain manually to prevent the policy from being cached.

I changed the above web.config lines to

<clientCertificate>
	<authentication mapClientCertificateToWindowsAccount="true" certificateValidationMode="Custom" customCertificateValidatorType="My.IdentityModel.MyX509Validator, My.IdentityModel" />
</clientCertificate>

and implemented the custom validator like this:

using System;
using System.IO;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace My.IdentityModel
{
    /// <summary>
    /// Custom x509 certificate validator
    /// Richard Ginzburg - richard@ginzburgconsulting.com
    /// </summary>
    public class MyX509Validator : X509CertificateValidator
    {
        public override void Validate(X509Certificate2 certificate)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate", "Certificate validation failed, no certificate provided");
            }

            X509ChainPolicy myChainPolicy = new X509ChainPolicy
                                                {
                                                    RevocationMode = X509RevocationMode.Online,
                                                    RevocationFlag = X509RevocationFlag.EntireChain,
                                                    VerificationFlags = X509VerificationFlags.NoFlag,
                                                    UrlRetrievalTimeout = new TimeSpan(0, 0, 10),
                                                    VerificationTime = DateTime.Now
                                                };
            X509Chain chain = new X509Chain(true) {ChainPolicy = myChainPolicy};

            try
            {
                bool ok = chain.Build(certificate);
                if(!ok)
                {
                    foreach (var status in chain.ChainStatus)
                    {
                        Logging.Log("MyX509Validator: Validation failed - " + status.StatusInformation);
                    }
                    throw new SecurityTokenValidationException("Certificate validation failed when building chain");
                }
            }
            catch (CryptographicException e)
            {
                throw new SecurityTokenValidationException("Certificate validation failed when building chain, " + e);
            }
        }
    }
}

This resolved the issue, now the changes in certificate validity take effect immediately.

Some documentation on certificate validity checking can be found here: http://technet.microsoft.com/en-us/library/bb457027.aspx .

This entry was posted in Work and tagged , , , . Bookmark the permalink.

3 Responses to Avoiding X.509 chain policy caching when using WCF with certificate security

  1. Pingback: Real-time CRL validation for X.509 certificates in .NET | Ginzburg Consulting blog

  2. Tim Huntley says:

    Thanks. Looks good. I am going to try a custom validator to solve an unrelated problem. Should you use DateTime.UtcNow instead of DateTime.Now to timestamp your chain policy?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>