Procedimiento para crear un servicio que emplee un validador de certificado personalizado

En este tema se muestra cómo implementar un validador del certificado personalizado y cómo configurar el cliente o credenciales del servicio para reemplazar la lógica de validación de certificado predeterminada por el validador del certificado personalizado.

Si se usa el certificado X.509 para autenticar un cliente o servicio, Windows Communication Foundation (WCF) emplea de forma predeterminada el almacén de certificados de Windows y Crypto API para validar el certificado y asegurarse de que es de confianza. La funcionalidad integrada de validación del certificado no es suficiente y a veces se debe cambiar. WCF proporciona una manera sencilla de cambiar la lógica de validación permitiendo a los usuarios agregar un validador de certificado personalizado. Si se especifica un validador de certificado personalizado, WCF no usa la lógica de validación de certificado integrada, sino que se basa en el validador personalizado.

Procedimientos

Crear un validador de certificado personalizado

  1. Defina una clase nueva derivada a partir de X509CertificateValidator.

  2. Implemente el método Validate abstracto. El certificado que se debe validar se pasa como un argumento al método. Si el certificado pasado no es válido según la lógica de la validación, este método inicia SecurityTokenValidationException. Si el certificado es válido, el método vuelve al autor de la llamada.

    Nota

    Para devolver errores de autenticación al cliente, genere una FaultException en el método Validate.

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");
        }
    }
}
Public Class MyX509CertificateValidator
    Inherits X509CertificateValidator
    Private allowedIssuerName As String

    Public Sub New(ByVal allowedIssuerName As String)
        If allowedIssuerName Is Nothing Then
            Throw New ArgumentNullException("allowedIssuerName")
        End If

        Me.allowedIssuerName = allowedIssuerName

    End Sub

    Public Overrides Sub Validate(ByVal certificate As X509Certificate2)
        ' Check that there is a certificate.
        If certificate Is Nothing Then
            Throw New ArgumentNullException("certificate")
        End If

        ' Check that the certificate issuer matches the configured issuer.
        If allowedIssuerName <> certificate.IssuerName.Name Then
            Throw New SecurityTokenValidationException _
              ("Certificate was not issued by a trusted issuer")
        End If

    End Sub
End Class

Especificar un validador de certificado personalizado en configuración de servicio

  1. Agregue un elemento <behaviors> y un elemento <serviceBehaviors> al elemento <system.serviceModel>.

  2. Agregue un elemento <behavior> y establezca el atributo name en un valor adecuado.

  3. Agregue un elemento <serviceCredentials> al elemento <behavior>.

  4. Agregue un elemento <clientCertificate> al elemento <serviceCredentials>.

  5. Agregue un elemento <autenticación> al elemento <clientCertificate>.

  6. Defina el atributo customCertificateValidatorType en el tipo de validador. El ejemplo siguiente define el atributo en el espacio de nombres y nombre del tipo.

  7. Defina el atributo certificateValidationMode a 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>  
    

Especificar un validador de certificado personalizado mediante la configuración del cliente

  1. Agregue un elemento <behaviors> y un elemento <serviceBehaviors> al elemento <system.serviceModel>.

  2. Agregue un elemento <endpointBehaviors>.

  3. Agregue un elemento y establezca el atributo en un valor adecuado.

  4. Agregue un elemento <clientCredentials>.

  5. Agregue un elemento <serviceCertificate>.

  6. Agregue un elemento <authentication> como se muestra en el ejemplo siguiente.

  7. Defina el atributo customCertificateValidatorType en el tipo de validador.

  8. Defina el atributo certificateValidationMode a Custom. El ejemplo siguiente define el atributo en el espacio de nombres y nombre del tipo.

    <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>  
    

Especificar un validador de certificado personalizado mediante el código en el servicio

  1. Especifique el validador de certificado personalizado en la propiedad ClientCertificate. Puede obtener acceso a las credenciales de servicio mediante la propiedad Credentials.

  2. Establezca la propiedad CertificateValidationMode en Custom.

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

Especificar un validador de certificado personalizado mediante el código en el cliente

  1. Especifique el validador de certificado personalizado mediante la propiedad CustomCertificateValidator. Puede obtener acceso a las credenciales de cliente mediante la propiedad Credentials. (La clase de cliente generada por serviceModel Metadata Utility Tool (Svcutil.exe) siempre se obtiene de la clase ClientBase<TChannel>).

  2. Establezca la propiedad CertificateValidationMode en Custom.

Ejemplo

Descripción

El ejemplo siguiente muestra una implementación de un validador de certificado personalizado y su uso en el servicio.

Código

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

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");
            }
        }
    }
}
Imports System.IdentityModel.Selectors
Imports System.Security.Cryptography.X509Certificates
Imports System.ServiceModel
Imports System.ServiceModel.Security
Imports System.IdentityModel.Tokens
Imports System.Security.Permissions

<ServiceContract([Namespace]:="http://Microsoft.ServiceModel.Samples")> _
Public Interface ICalculator
    <OperationContract()> _
    Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
End Interface


Public Class CalculatorService
    Implements ICalculator

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double _
       Implements ICalculator.Add
        Dim result As Double = n1 + n2
        Return result
    End Function
End Class


Class Program

    Shared Sub Main()
        Dim serviceHost As New ServiceHost(GetType(CalculatorService))
        Try
            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()
        Finally
            serviceHost.Close()
        End Try

    End Sub
End Class

Public Class MyX509CertificateValidator
    Inherits X509CertificateValidator
    Private allowedIssuerName As String

    Public Sub New(ByVal allowedIssuerName As String)
        If allowedIssuerName Is Nothing Then
            Throw New ArgumentNullException("allowedIssuerName")
        End If

        Me.allowedIssuerName = allowedIssuerName

    End Sub

    Public Overrides Sub Validate(ByVal certificate As X509Certificate2)
        ' Check that there is a certificate.
        If certificate Is Nothing Then
            Throw New ArgumentNullException("certificate")
        End If

        ' Check that the certificate issuer matches the configured issuer.
        If allowedIssuerName <> certificate.IssuerName.Name Then
            Throw New SecurityTokenValidationException _
              ("Certificate was not issued by a trusted issuer")
        End If

    End Sub
End Class

Consulte también