Comment : créer un service qui utilise un validateur de certificat personnalisé
Cette rubrique décrit comment implémenter un validateur de certificat personnalisé et comment configurer les informations d'identification du service ou du client pour remplacer la logique de validation de certificat par défaut par le validateur de certificat personnalisé.
Si le certificat X.509 est utilisé pour authentifier un client ou un service, Windows Communication Foundation (WCF) utilise par défaut le magasin de certificats Windows et API Crypto pour valider le certificat et garantir qu'il est approuvé. Les fonctionnalités intégrées de validation du certificat sont parfois insuffisantes et doivent être changées. WCF offre un moyen facile pour modifier la logique de validation en permettant aux utilisateurs d'ajouter un validateur de certificat personnalisé. Si un validateur de certificat personnalisé est spécifié, WCF n'utilise pas la logique intégrée de validation de certificat mais fait appel au validateur personnalisé.
Procédures
Pour créer un validateur de certificat personnalisé
Définissez une nouvelle classe dérivée de X509CertificateValidator.
Implémentez la méthode abstraite Validate. Le certificat qui doit être validé est passé sous la forme d'un argument à la méthode. Si le certificat passé n'est pas valide selon la logique de validation, cette méthode lève une SecurityTokenValidationException. Si le certificat est valide, la méthode est retournée à l'appelant.
Remarque : Pour renvoyer des erreurs d'authentification au client, levez une exception FaultException dans la méthode 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");
}
}
}
Pour spécifier un validateur de certificat personnalisé dans la configuration du service
Ajoutez un élément <behaviors> et serviceBehaviors section à l'élément <system.ServiceModel>.
Ajoutez un Behavior element et affectez une valeur appropriée à l'attribut name.
Ajoutez un <serviceCredentials> Element à l'élément <behavior>.
Ajoutez un élément <clientCertificate> à l'élément <serviceCredentials>.
Ajoutez un <authentication> of <clientCertificate> Element à l'élément <clientCertificate>.
Affectez à l'attribut customCertificateValidatorType le type de validateur. L'exemple suivant affecte à l'attribut l'espace de noms et le nom du type.
Affectez à l'attribut certificateValidationMode la valeur 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>
Pour spécifier un validateur de certificat personnalisé à l'aide de la configuration sur le client
Ajoutez un élément <behaviors> et serviceBehaviors section à l'élément <system.ServiceModel>.
Ajoutez un élément <endpointBehaviors>.
Ajoutez un élément <behavior>, puis affectez à l'attribut name une valeur appropriée.
Ajoutez un élément <clientCredentials>.
Ajoutez un <serviceCertificate> of <clientCredentials> Element
Ajoutez un <authentication> of <serviceCertificate> Element comme illustré dans l'exemple suivant.
Affectez à l'attribut customCertificateValidatorType le type de validateur.
Affectez à l'attribut certificateValidationMode la valeur Custom. L'exemple suivant affecte à l'attribut l'espace de noms et le nom du 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>
Pour spécifier un validateur de certificat personnalisé à l'aide du code sur le service
Spécifiez le validateur de certificat personnalisé sur la propriété ClientCertificate. Vous pouvez accéder aux informations d'identification de service à l'aide de la propriété Credentials.
Affectez la valeur Custom à la propriété CertificateValidationMode.
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");
Pour spécifier un validateur de certificat personnalisé à l'aide du code sur le client
Spécifiez le validateur de certificat personnalisé à l'aide de la propriété CustomCertificateValidator. Vous pouvez accéder aux informations d'identification du client à l'aide de la propriété Credentials. (La classe de client générée par Outil Service Model Metadata Tool (Svcutil.exe) dérive toujours de la classe ClientBase.)
Affectez la valeur Custom à la propriété CertificateValidationMode.
Exemple
Description
L'exemple suivant montre une implémentation d'un validateur de certificat personnalisé et son utilisation sur le service.
Code
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");
}
}
}
}