Attention: We are retiring the ASP.NET Community Blogs. Learn more >

X509 Certificate validation

Some time ago I was looking for some way to validate an X509 certificate and for my surprise I couldn't find any good method to do this pretty common task. I know that the X509Certificate2 class has a Verify method but is rather limited in terms of validation options and scope. I also know that the X509Chain class can give some good validation but basically scoped to the certificates in the chain and the many options it has may take some time to get them right. Then I remembered that WCF already provides a good and configurable certificate validation API in the form of "<authentication>" element like the configuration section below:

<serviceCertificate>

     <authentication certificateValidationMode="ChainTrust" revocationMode="Online"/>

</serviceCertificate>

This was exactly what I was looking for, some code where I can specify different validation modes and also the kind of revocation checks to apply in my certificate validation. As you can see, the "certificateValidationMode" attribute provides an enum like X509CertificateValidationMode that covers some common scenarios described in the last link. The same goes to "revocationMode" and its enum X509RevocationMode.

After some brief research with Reflector I could finally isolate the key class that handle these settings and provide access to all these rich certificate validation and best of all, most of these classes were public. The only drawback was that this key class "System.ServiceModel.Security.X509ServiceCertificateAuthentication" has all the constructors private so we can't use it out of the box. So all that we need is to get a "clone" that simply expose all the public interface and we should be done.

Leverage X509ServiceCertificateAuthentication class

The key property of this class that will give as the certificate validation with all the heavy work handled by the native WSF API is "GetCertificateValidator()".

As you can see, this method returns an X509CertificateValidator class that can be derived to implement any kind of certificate validation. Another good thing is that WCF already provides all the validators according to the validation mode selected so we don't need to write any single line of code for this. Let's see a class that will handle all the validation settings and how we can use it.

Notice that I omitted all the properties and other constructors for simplicity.

 

public class X509CertificateValidation

{

      public X509CertificateValidation(

            X509CertificateValidationMode certificateValidationMode,

            X509RevocationMode revocationMode,

            StoreLocation trustedStoreLocation,

            X509CertificateValidator customCertificateValidator)

      {

            this.certificateValidationMode = certificateValidationMode;

            this.revocationMode = revocationMode;

            this.trustedStoreLocation = trustedStoreLocation;

            // you can add you own custom validator as in WCF config

            this.customCertificateValidator = customCertificateValidator;

      }

      public X509CertificateValidator GetCertificateValidator()

      {

            X509CertificateValidator validator = null;

            switch (this.certificateValidationMode)

            {

                  case X509CertificateValidationMode.None:

                        validator = X509CertificateValidator.None;

                        break;

                  case X509CertificateValidationMode.PeerTrust:

                        validator = X509CertificateValidator.PeerTrust;

                        break;

                  case X509CertificateValidationMode.Custom:

                        validator = this.customCertificateValidator;

                        break;

                  case X509CertificateValidationMode.ChainTrust:

                        bool useMachineContext =

                                 this.trustedStoreLocation == StoreLocation.LocalMachine;

                        X509ChainPolicy chainPolicy = new X509ChainPolicy();

                        chainPolicy.RevocationMode = this.revocationMode;

                        if (this.certificateValidationMode == 

                                                X509CertificateValidationMode.ChainTrust)

                        {

                              validator = X509CertificateValidator.

                               CreateChainTrustValidator(useMachineContext, chainPolicy);

                        }

                        else

                        {

                              validator = X509CertificateValidator.

                         CreatePeerOrChainTrustValidator(useMachineContext, chainPolicy);

                        }

                        break;

            }

            if (validator == null)

            {

                  // throw if not validation found

                  throw new InvalidOperationException("MissingCertificateValidator");

            }

            return validator;

      }

}

Now we can simply use this class like the below sample setting the required constructor parameters and passing our X509Certificate2 instance to the Validate method exposed by the returned validator instance according to the parameters passed. 

 

X509CertificateValidation validation = new X509CertificateAuthentication([set params]);

Validation.GetCertificateValidator().Validate(yourCertificate);

 

In case you want to set the validation parameters like in WCF config, you can also make use of the X509ServiceCertificateAuthenticationElement class and add a property to your configuration section class like this:

 

public class MyConfigurationSection : ConfigurationSection

{

  // add your properties here

  [ConfigurationProperty("myCertificate")]

  public X509ServiceCertificateAuthenticationElement MyCertificateAuthentication

  {

      Get

      {

          return X509ServiceCertificateAuthenticationElement)this["myCertificate"];

      }

  }

}

And your config element will look like:

<myCertificate certificateValidationMode="ChainTrust" revocationMode="Online"/>

You can also add a new constructor to the validation class that use the config parameters like this:

 

public X509CertificateAuthentication(X509ServiceCertificateAuthenticationElement element)

: this(element.CertificateValidationMode,

       element.RevocationMode, 

       element.TrustedStoreLocation,                

       CreateCustomCertificateValidator(element.CustomCertificateValidatorType))

{              

}

Notice that in case of an invalid certificate, you will get a pretty good error description thanks to the WCF built in validators.

No Comments