Практическое руководство. Создание службы, используюющей настраиваемый проверяющий сертификат

В этом разделе показано, как реализовать настраиваемый проверяющий элемент сертификата и как настроить учетные данные клиента или службы для замены логики проверки сертификатов по умолчанию пользовательским проверятелем сертификатов.

Если сертификат X.509 используется для проверки подлинности клиента или службы, Windows Communication Foundation (WCF) по умолчанию использует хранилище сертификатов Windows и API шифрования для проверки сертификата и обеспечения доверия. Иногда встроенные функции проверки сертификатов недостаточно и должны быть изменены. WCF предоставляет простой способ изменить логику проверки, позволяя пользователям добавлять настраиваемый валидатор сертификатов. Если указан настраиваемый проверяющий элемент сертификата, WCF не использует встроенную логику проверки сертификатов, а использует настраиваемый проверяющий элемент.

Procedures

Создание настраиваемого средства проверки сертификатов

  1. Определите новый производный класс на основе класса X509CertificateValidator.

  2. Реализуйте абстрактный Validate метод. Сертификат, который необходимо проверить, передается в качестве аргумента методу. Если переданный сертификат не соответствует критериям проверки, этот метод генерирует исключение SecurityTokenValidationException. Если сертификат действителен, метод возвращается вызывающему объекту.

    Замечание

    Чтобы вернуть ошибки проверки подлинности обратно клиенту, вызовите FaultExceptionValidate метод.

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

Указание пользовательского валидатора сертификатов в конфигурации службы

  1. Добавьте элемент <поведения> и элемент <serviceBehaviors> в элемент <system.serviceModel>.

  2. <Добавьте поведение> и задайте name атрибут соответствующим значением.

  3. Добавьте <serviceCredentials> к элементу <behavior>.

  4. Добавьте элемент <clientCertificate> в элемент <serviceCredentials>.

  5. <Добавьте аутентификацию> в элемент <clientCertificate>.

  6. customCertificateValidatorType Задайте атрибут типу проверяющего элемента. В следующем примере атрибут присваивается пространству имен и имени типа.

  7. Задайте атрибуту certificateValidationMode значение 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>
    

Указание настраиваемого проверяющего элемента сертификата с помощью конфигурации на клиенте

  1. Добавьте элемент <поведения> и <serviceBehaviors> в элемент <system.serviceModel>.

  2. <Добавьте элемент endpointBehaviors.>

  3. Добавьте элемент <behavior> и задайте атрибут name на соответствующее значение.

  4. <Добавьте элемент clientCredentials.>

  5. <Добавьте serviceCertificate>.

  6. <Добавьте проверку подлинности>, как показано в следующем примере.

  7. customCertificateValidatorType Задайте атрибут типу валидатора.

  8. Установите атрибут certificateValidationMode на Custom. В следующем примере атрибут присваивается пространству имен и имени типа.

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

Указание пользовательского валидатора сертификата с использованием кода на сервере

  1. Укажите в свойстве ClientCertificate настраиваемый проверяющий сертификат. Вы можете получить доступ к учетным данным службы с помощью Credentials свойства.

  2. Задайте для свойства CertificateValidationMode значение 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")

Назначение пользовательского средства проверки сертификатов с помощью кода на стороне клиента

  1. Укажите кастомизированный валидатор сертификата, используя свойство CustomCertificateValidator. Вы можете получить доступ к учетным данным клиента с помощью Credentials свойства. (Клиентский класс, созданный средством служебной программы метаданных ServiceModel (Svcutil.exe) всегда является производным от ClientBase<TChannel> класса.)

  2. Задайте для свойства CertificateValidationMode значение Custom.

Пример

Описание

В следующем примере показана реализация клиентской валидации сертификатов и его использование в службе.

Код

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

См. также