方法 : カスタム証明書検証を使用するサービスを作成する
このトピックでは、カスタム証明書検証を実装する方法、クライアントまたはサービスの資格情報の設定により、既定の証明書検証機能を、カスタム証明書検証で置き換える方法について解説します。
X.509 証明書を使ってクライアントやサービスを認証する場合、Windows Communication Foundation (WCF) の既定では、Windows 証明書ストアと Crypto API を使用して証明書が検証され、信頼できるかどうかが確認されます。ところが、組み込みの検証機能では不充分で、処理内容を変更しなければならない場合もあります。WCF では、ユーザーがカスタムの証明書検証機能を追加して、検証ロジックを簡単に変更できます。カスタムの証明書検証機能が指定されている場合、WCF では、組み込みの検証機能ではなく、カスタムの検証処理のみが使用されます。
手順
カスタムの証明書検証機能を作成するには
X509CertificateValidator から派生する新しいクラスを定義します。
抽象メソッドである Validate を実装します。検証対象の証明書が、引数としてこのメソッドに渡されます。検証処理の結果、証明書が有効ではないと判断されると、このメソッドは、SecurityTokenValidationException という例外をスローします。有効であればそのまま呼び出し元に制御を返します。
注 : クライアントに認証エラーを返すには、Validate メソッドで FaultException をスローします。
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");
}
}
}
サービス構成でカスタム検証処理を指定するには
<system.ServiceModel> 要素に <behaviors> 要素と serviceBehaviors section を追加します。
Behavior elementを追加し、name 属性に適切な値を設定します。
<behavior> 要素に <serviceCredentials> Elementを追加します。
<serviceCredentials> 要素に <clientCertificate> 要素を追加します。
<clientCertificate> 要素に <authentication> of <clientCertificate> Element を追加します。
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>
クライアント側の設定でカスタム証明書検証を指定するには
<system.ServiceModel> 要素に <behaviors> 要素と serviceBehaviors section を追加します。
<endpointBehaviors> 要素を追加します。
<behavior> 要素を追加し、name 属性に適切な値を設定します。
<clientCredentials> 要素を追加します。
次の例に示すように、<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 メタデータ ユーティリティ ツール (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");
}
}
}
}