Export (0) Print
Expand All

How to: Create a Service that Employs a Custom Certificate Validator

This topic shows how to implement a custom certificate validator and how to configure client or service credentials to replace the default certificate validation logic with the custom certificate validator.

If the X.509 certificate is used to authenticate a client or service, Windows Communication Foundation (WCF) by default uses the Windows certificate store and Crypto API to validate the certificate and to ensure that it is trusted. Sometimes the built-in certificate validation functionality is not enough and must be changed. WCF provides an easy way to change the validation logic by allowing users to add a custom certificate validator. If a custom certificate validator is specified, WCF does not use the built-in certificate validation logic but relies on the custom validator instead.

Procedures

To create a custom certificate validator

  1. Define a new class derived from X509CertificateValidator.

  2. Implement the abstract Validate method. The certificate that must be validated is passed as an argument to the method. If the passed certificate is not valid according to the validation logic, this method throws a SecurityTokenValidationException. If the certificate is valid, the method returns to the caller.

    ms733806.note(en-us,VS.90).gifNote:
    To return authentication errors back to the client, throw a FaultException in the Validate method.

public class MyX509CertificateValidator : X509CertificateValidator
{
    string allowedIssuerName;

    public MyX509CertificateValidator(string allowedIssuerName)
    {
        if (allowedIssuerName == null)
        {
            throw new ArgumentNullException("allowedIssuerName");
        }

        this.allowedIssuerName = allowedIssuerName;
    }

    public override void Validate(X509Certificate2 certificate)
    {
        // Check that there is a certificate.
        if (certificate == null)
        {
            throw new ArgumentNullException("certificate");
        }

        // Check that the certificate issuer matches the configured issuer.
        if (allowedIssuerName != certificate.IssuerName.Name)
        {
            throw new SecurityTokenValidationException
              ("Certificate was not issued by a trusted issuer");
        }
    }
}

To specify a custom certificate validator in service configuration

  1. Add a <behaviors> element and a serviceBehaviors section to the <system.ServiceModel> element.

  2. Add a Behavior element and set the name attribute to an appropriate value.

  3. Add a <serviceCredentials> Element to the <behavior> element.

  4. Add a <clientCertificate> element to the <serviceCredentials> element.

  5. Add an <authentication> of <clientCertificate> Element to the <clientCertificate> element.

  6. Set the customCertificateValidatorType attribute to the validator type. The following example sets the attribute to the namespace and name of the type.

  7. Set the certificateValidationMode attribute to Custom.

    <configuration>
     <system.serviceModel>
      <behaviors>
       <serviceBehaviors>
        <behavior name="ServiceBehavior">
         <serviceCredentials>
          <clientCertificate>
          <authentication certificateValidationMode="Custom" customCertificateValidatorType="Samples.MyValidator, service" />
          </clientCertificate>
         </serviceCredentials>
        </behavior>
       </serviceBehaviors>
      </behaviors>
    </system.serviceModel>
    </configuration>
    

To specify a custom certificate validator using configuration on the client

  1. Add a <behaviors> element and a serviceBehaviors section to the <system.ServiceModel> element.

  2. Add an <endpointBehaviors> element.

  3. Add a <behavior> element and set the name attribute to an appropriate value.

  4. Add a <clientCredentials> element.

  5. Add a <serviceCertificate> of <clientCredentials> Element.

  6. Add an <authentication> of <serviceCertificate> Element as shown on the following example.

  7. Set the customCertificateValidatorType attribute to the validator type.

  8. Set the certificateValidationMode attribute to Custom. The following example sets the attribute to the namespace and name of the type.

    <configuration>
     <system.serviceModel>
      <behaviors>
       <endpointBehaviors>
        <behavior name="clientBehavior">
         <clientCredentials>
          <serviceCertificate>
           <authentication certificateValidationMode="Custom" 
                  customCertificateValidatorType=
             "Samples.CustomX509CertificateValidator, client"/>
          </serviceCertificate>
         </clientCredentials>
        </behavior>
       </endpointBehaviors>
      </behaviors>
     </system.serviceModel>
    </configuration>
    

To specify a custom certificate validator using code on the service

  1. Specify the custom certificate validator on the ClientCertificate property. You can access the service credentials using the Credentials property.

  2. Set the CertificateValidationMode property to Custom.

serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = 
    X509CertificateValidationMode.Custom;
serviceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = 
    new MyX509CertificateValidator("CN=Contoso.com");

To specify a custom certificate validator using code on the client

  1. Specify the custom certificate validator using the CustomCertificateValidator property. You can access the client credentials using the Credentials property. (The client class generated by ServiceModel Metadata Utility Tool (Svcutil.exe) always derives from the ClientBase class.)

  2. Set the CertificateValidationMode property to Custom.

Example

Description

The following sample shows an implementation of a custom certificate validator and its usage on the service.

Code

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

using System.Security.Permissions;

[assembly: SecurityPermission(
   SecurityAction.RequestMinimum, Execution = true)]
namespace Microsoft.ServiceModel.Samples
{ 
    [ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
    }

    public class CalculatorService : ICalculator
    {
        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            return result;
        }
    }

    class Program
    {
        static void Main()
        {
            using (ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService)))
            {
                serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = 
                    X509CertificateValidationMode.Custom;
                serviceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = 
                    new MyX509CertificateValidator("CN=Contoso.com");

                serviceHost.Open();
                Console.WriteLine("Service started, press ENTER to stop ...");
                Console.ReadLine();

                serviceHost.Close();
            }
        }
    }

    public class MyX509CertificateValidator : X509CertificateValidator
    {
        string allowedIssuerName;

        public MyX509CertificateValidator(string allowedIssuerName)
        {
            if (allowedIssuerName == null)
            {
                throw new ArgumentNullException("allowedIssuerName");
            }

            this.allowedIssuerName = allowedIssuerName;
        }

        public override void Validate(X509Certificate2 certificate)
        {
            // Check that there is a certificate.
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }

            // Check that the certificate issuer matches the configured issuer.
            if (allowedIssuerName != certificate.IssuerName.Name)
            {
                throw new SecurityTokenValidationException
                  ("Certificate was not issued by a trusted issuer");
            }
        }
    }
}

See Also

Community Additions

ADD
Show:
© 2014 Microsoft