Как создать службу, использующую пользовательский проверяющий элемент управления для сертификатов
В данном разделе показано, как реализовать пользовательский проверяющий элемент управления для сертификата и как настроить учетные данные клиента или службы, чтобы заменить логику проверки сертификата по умолчанию пользовательским проверяющим элементом управления для сертификата.
Если для проверки подлинности клиента или службы используется сертификат X.509, Windows Communication Foundation (WCF) по умолчанию использует хранилище сертификатов Windows и Crypto API, чтобы проверить сертификат и гарантировать его надежность. Иногда встроенных функциональных возможностей проверки сертификатов недостаточно, и их необходимо изменить. WCF предоставляет простой способ изменения логики проверки, позволяя пользователям добавлять пользовательский проверяющий элемент управления для сертификата. Если пользовательский проверяющий элемент управления для сертификата указан, WCF вместо встроенной логики проверки сертификатов использует пользовательский проверяющий элемент управления.
Процедуры
Создание пользовательского проверяющего элемента управления для сертификата
Определите новый класс, унаследованный от класса X509CertificateValidator.
Реализуйте абстрактный метод Validate. Сертификат, который необходимо проверить, передается методу как аргумент. Если переданный сертификат недопустим согласно логике проверки, этот метод создает исключение SecurityTokenValidationException. Если сертификат действителен, метод возвращает его вызывающей стороне.
Примечание Чтобы вернуть ошибки проверки подлинности клиенту, создайте исключение FaultException в методе Validate.
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
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");
}
}
}
Задание пользовательского проверяющего элемента управления для сертификата в конфигурации службы
Добавьте элемент <behaviors> и serviceBehaviors section в элемент <system.ServiceModel>.
Добавьте элемент Behavior element и задайте соответствующее значение для атрибута name.
Добавьте элемент <serviceCredentials> Element в элемент <behavior>.
Добавьте элемент <clientCertificate> в элемент <serviceCredentials>.
Добавьте элемент <authentication> of <clientCertificate> Element в элемент <clientCertificate>.
Задайте для атрибута customCertificateValidatorType тип проверяющего элемента управления. В следующем примере для атрибута задано значение пространства имен и имени типа.
Задайте для атрибута 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>
Задание пользовательского проверяющего элемента управления для сертификата с использованием конфигурации на стороне клиента
Добавьте элемент <behaviors> и serviceBehaviors section в элемент <system.ServiceModel>.
Добавьте элемент <endpointBehaviors>.
Добавьте элемент <behavior> и присвойте атрибуту name соответствующее значение.
Добавьте элемент <clientCredentials>.
Добавьте элемент <serviceCertificate> of <clientCredentials> Element.
Добавьте элемент <authentication> of <serviceCertificate> Element, как показано в следующем примере.
Задайте для атрибута customCertificateValidatorType тип проверяющего элемента управления.
Задайте для атрибута 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>
Задание пользовательского проверяющего элемента управления для сертификата с использованием кода на стороне службы
Задайте пользовательский проверяющий элемент управления для сертификата в свойстве ClientCertificate. Получить доступ к учетным данным службы можно с помощью свойства Credentials.
Задайте для свойства 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");
Задание пользовательского проверяющего элемента управления для сертификата с использованием кода на стороне клиента
Задайте пользовательский проверяющий элемент управления для сертификата в свойстве CustomCertificateValidator. Получить доступ к учетным данным клиента можно с помощью свойства Credentials. (Класс клиента, созданный с помощью средства Служебное средство ServiceModel Metadata Utility Tool (Svcutil.exe), всегда наследуется от класса ClientBase.)
Задайте для свойства CertificateValidationMode значение Custom.
Пример
Описание
В следующем примере показана реализация пользовательского проверяющего элемента управления для сертификата и его использование на стороне службы.
Код
Imports System
Imports System.IdentityModel.Selectors
Imports System.Security.Cryptography.X509Certificates
Imports System.ServiceModel
Imports System.ServiceModel.Security
Imports System.IdentityModel.Tokens
Imports System.Security.Permissions
<assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution := True)>
<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
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");
}
}
}
}