方法: 署名および暗号化に個別の X.509 証明書を使用する
このトピックでは、クライアントとサービスの両方でメッセージの署名と暗号化に異なる証明書を使用するように Windows Communication Foundation (WCF) を構成する方法を示します。
WCF では複数のクライアントまたはサービスの証明書を設定する API が提供されていないため、署名と暗号化で別の証明書を使用できるようにするには、カスタム クライアントまたはサービスの資格情報 (あるいはその両方) を作成する必要があります。 さらに、複数の証明書の情報を利用し、指定されたキーの使用方法やメッセージの方向について適切なセキュリティ トークン プロバイダーを作成するために、セキュリティ トークン マネージャーを用意する必要があります。
使用される主要なクラス、そのクラスの継承元のクラス (上向きの矢印で表示)、および特定のメソッドおよびプロパティの戻り値の型を次の図に示します。
MyClientCredentials
は、ClientCredentials のカスタム実装です。図に示されたすべてのプロパティは、すべて X509Certificate2 のインスタンスを返します。
メソッド CreateSecurityTokenManager は、
MyClientCredentialsSecurityTokenManager
のインスタンスを返します。
MyClientCredentialsSecurityTokenManager
は、ClientCredentialsSecurityTokenManager のカスタム実装です。- メソッド CreateSecurityTokenProvider は、X509SecurityTokenProvider のインスタンスを返します。
カスタム資格情報の詳細については、「チュートリアル: カスタム クライアントおよびサービスの資格情報を作成する」を参照してください。
また、カスタム ID 検証機能を作成し、カスタム バインドのセキュリティ バインド要素にリンクする必要があります。 さらに、既定の資格情報の代わりにカスタム資格情報を使用する必要があります。
カスタム バインドに関連したクラス、およびカスタム ID 検証機能のリンク方法を次の図に示します。 関連するバインド要素はいくつかありますが、そのすべてが BindingElement から継承されています。 AsymmetricSecurityBindingElement には、LocalClientSecuritySettings プロパティがあります。このプロパティは、IdentityVerifier のカスタマイズの元となる MyIdentityVerifier
のインスタンスを返します。
カスタム ID 検証機能の作成の詳細については、「方法: カスタム クライアント ID 検証機能を作成する」を参照してください。
署名と暗号化に別個の証明書を使用するには
ClientCredentials クラスを継承する新しいクライアント資格情報クラスを定義します。 複数の証明書を指定できるようにする、
ClientSigningCertificate
、ClientEncryptingCertificate
、ServiceSigningCertificate
、およびServiceEncryptingCertificate
の 4 つの新しいプロパティを実装します。 また、CreateSecurityTokenManager メソッドをオーバーライドして、次のステップで定義するカスタマイズ済みの ClientCredentialsSecurityTokenManager クラスのインスタンスを返すように設定します。public class MyClientCredentials : ClientCredentials { X509Certificate2 clientSigningCert; X509Certificate2 clientEncryptingCert; X509Certificate2 serviceSigningCert; X509Certificate2 serviceEncryptingCert; public MyClientCredentials() { } protected MyClientCredentials(MyClientCredentials other) : base(other) { this.clientEncryptingCert = other.clientEncryptingCert; this.clientSigningCert = other.clientSigningCert; this.serviceEncryptingCert = other.serviceEncryptingCert; this.serviceSigningCert = other.serviceSigningCert; } public X509Certificate2 ClientSigningCertificate { get { return this.clientSigningCert; } set { this.clientSigningCert = value; } } public X509Certificate2 ClientEncryptingCertificate { get { return this.clientEncryptingCert; } set { this.clientEncryptingCert = value; } } public X509Certificate2 ServiceSigningCertificate { get { return this.serviceSigningCert; } set { this.serviceSigningCert = value; } } public X509Certificate2 ServiceEncryptingCertificate { get { return this.serviceEncryptingCert; } set { this.serviceEncryptingCert = value; } } public override SecurityTokenManager CreateSecurityTokenManager() { return new MyClientCredentialsSecurityTokenManager(this); } protected override ClientCredentials CloneCore() { return new MyClientCredentials(this); } }
Public Class MyClientCredentials Inherits ClientCredentials Private clientSigningCert As X509Certificate2 Private clientEncryptingCert As X509Certificate2 Private serviceSigningCert As X509Certificate2 Private serviceEncryptingCert As X509Certificate2 Public Sub New() End Sub Protected Sub New(ByVal other As MyClientCredentials) MyBase.New(other) Me.clientEncryptingCert = other.clientEncryptingCert Me.clientSigningCert = other.clientSigningCert Me.serviceEncryptingCert = other.serviceEncryptingCert Me.serviceSigningCert = other.serviceSigningCert End Sub Public Property ClientSigningCertificate() As X509Certificate2 Get Return Me.clientSigningCert End Get Set(ByVal value As X509Certificate2) Me.clientSigningCert = value End Set End Property Public Property ClientEncryptingCertificate() As X509Certificate2 Get Return Me.clientEncryptingCert End Get Set(ByVal value As X509Certificate2) Me.clientEncryptingCert = value End Set End Property Public Property ServiceSigningCertificate() As X509Certificate2 Get Return Me.serviceSigningCert End Get Set(ByVal value As X509Certificate2) Me.serviceSigningCert = value End Set End Property Public Property ServiceEncryptingCertificate() As X509Certificate2 Get Return Me.serviceEncryptingCert End Get Set(ByVal value As X509Certificate2) Me.serviceEncryptingCert = value End Set End Property Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager Return New MyClientCredentialsSecurityTokenManager(Me) End Function Protected Overrides Function CloneCore() As ClientCredentials Return New MyClientCredentials(Me) End Function End Class
ClientCredentialsSecurityTokenManager クラスを継承する新しいクライアント セキュリティ トークン マネージャーを定義します。 適切なセキュリティ トークン プロバイダーを作成するために、CreateSecurityTokenProvider メソッドをオーバーライドします。 メッセージの方向とキーの使用方法は、
requirement
パラメーター (SecurityTokenRequirement) で提供されます。internal class MyClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager { MyClientCredentials credentials; public MyClientCredentialsSecurityTokenManager( MyClientCredentials credentials): base(credentials) { this.credentials = credentials; } public override SecurityTokenProvider CreateSecurityTokenProvider( SecurityTokenRequirement requirement) { SecurityTokenProvider result = null; if (requirement.TokenType == SecurityTokenTypes.X509Certificate) { MessageDirection direction = requirement.GetProperty <MessageDirection>(ServiceModelSecurityTokenRequirement. MessageDirectionProperty); if (direction == MessageDirection.Output) { if (requirement.KeyUsage == SecurityKeyUsage.Signature) { result = new X509SecurityTokenProvider( this.credentials.ClientSigningCertificate); } else { result = new X509SecurityTokenProvider(this.credentials. ServiceEncryptingCertificate); } } else { if (requirement.KeyUsage == SecurityKeyUsage.Signature) { result = new X509SecurityTokenProvider(this. credentials.ServiceSigningCertificate); } else { result = new X509SecurityTokenProvider(credentials. ClientEncryptingCertificate); } } } else { result = base.CreateSecurityTokenProvider(requirement); } return result; } public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver) { return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver); } }
Friend Class MyClientCredentialsSecurityTokenManager Inherits ClientCredentialsSecurityTokenManager Private credentials As MyClientCredentials Public Sub New(ByVal credentials As MyClientCredentials) MyBase.New(credentials) Me.credentials = credentials End Sub Public Overrides Function CreateSecurityTokenProvider(ByVal requirement As SecurityTokenRequirement) As SecurityTokenProvider Dim result As SecurityTokenProvider = Nothing If requirement.TokenType = SecurityTokenTypes.X509Certificate Then Dim direction = requirement.GetProperty(Of MessageDirection)(ServiceModelSecurityTokenRequirement.MessageDirectionProperty) If direction = MessageDirection.Output Then If requirement.KeyUsage = SecurityKeyUsage.Signature Then result = New X509SecurityTokenProvider(Me.credentials.ClientSigningCertificate) Else result = New X509SecurityTokenProvider(Me.credentials.ServiceEncryptingCertificate) End If Else If requirement.KeyUsage = SecurityKeyUsage.Signature Then result = New X509SecurityTokenProvider(Me.credentials.ServiceSigningCertificate) Else result = New X509SecurityTokenProvider(credentials.ClientEncryptingCertificate) End If End If Else result = MyBase.CreateSecurityTokenProvider(requirement) End If Return result End Function Public Overrides Function CreateSecurityTokenAuthenticator(ByVal tokenRequirement As SecurityTokenRequirement, _ <System.Runtime.InteropServices.Out()> ByRef outOfBandTokenResolver As SecurityTokenResolver) As SecurityTokenAuthenticator Return MyBase.CreateSecurityTokenAuthenticator(tokenRequirement, _ outOfBandTokenResolver) End Function End Class
ServiceCredentials クラスを継承する新しいサービス資格情報クラスを定義します。 複数の証明書を指定できるようにする、
ClientSigningCertificate
、ClientEncryptingCertificate
、ServiceSigningCertificate
、およびServiceEncryptingCertificate
の 4 つの新しいプロパティを実装します。 また、CreateSecurityTokenManager メソッドをオーバーライドして、次のステップで定義するカスタマイズ済みの ServiceCredentialsSecurityTokenManager クラスのインスタンスを返すように設定します。public class MyServiceCredentials : ServiceCredentials { X509Certificate2 clientSigningCert; X509Certificate2 clientEncryptingCert; X509Certificate2 serviceSigningCert; X509Certificate2 serviceEncryptingCert; public MyServiceCredentials() { } protected MyServiceCredentials(MyServiceCredentials other) : base(other) { this.clientEncryptingCert = other.clientEncryptingCert; this.clientSigningCert = other.clientSigningCert; this.serviceEncryptingCert = other.serviceEncryptingCert; this.serviceSigningCert = other.serviceSigningCert; } public X509Certificate2 ClientSigningCertificate { get { return this.clientSigningCert; } set { this.clientSigningCert = value; } } public X509Certificate2 ClientEncryptingCertificate { get { return this.clientEncryptingCert; } set { this.clientEncryptingCert = value; } } public X509Certificate2 ServiceSigningCertificate { get { return this.serviceSigningCert; } set { this.serviceSigningCert = value; } } public X509Certificate2 ServiceEncryptingCertificate { get { return this.serviceEncryptingCert; } set { this.serviceEncryptingCert = value; } } public override SecurityTokenManager CreateSecurityTokenManager() { return new MyServiceCredentialsSecurityTokenManager(this); } protected override ServiceCredentials CloneCore() { return new MyServiceCredentials(this); } }
Public Class MyServiceCredentials Inherits ServiceCredentials Private clientSigningCert As X509Certificate2 Private clientEncryptingCert As X509Certificate2 Private serviceSigningCert As X509Certificate2 Private serviceEncryptingCert As X509Certificate2 Public Sub New() End Sub Protected Sub New(ByVal other As MyServiceCredentials) MyBase.New(other) Me.clientEncryptingCert = other.clientEncryptingCert Me.clientSigningCert = other.clientSigningCert Me.serviceEncryptingCert = other.serviceEncryptingCert Me.serviceSigningCert = other.serviceSigningCert End Sub Public Property ClientSigningCertificate() As X509Certificate2 Get Return Me.clientSigningCert End Get Set(ByVal value As X509Certificate2) Me.clientSigningCert = value End Set End Property Public Property ClientEncryptingCertificate() As X509Certificate2 Get Return Me.clientEncryptingCert End Get Set(ByVal value As X509Certificate2) Me.clientEncryptingCert = value End Set End Property Public Property ServiceSigningCertificate() As X509Certificate2 Get Return Me.serviceSigningCert End Get Set(ByVal value As X509Certificate2) Me.serviceSigningCert = value End Set End Property Public Property ServiceEncryptingCertificate() As X509Certificate2 Get Return Me.serviceEncryptingCert End Get Set(ByVal value As X509Certificate2) Me.serviceEncryptingCert = value End Set End Property Public Overrides Function CreateSecurityTokenManager() As SecurityTokenManager Return New MyServiceCredentialsSecurityTokenManager(Me) End Function Protected Overrides Function CloneCore() As ServiceCredentials Return New MyServiceCredentials(Me) End Function End Class
ServiceCredentialsSecurityTokenManager クラスを継承する新しいサービス セキュリティ トークン マネージャーを定義します。 渡されたメッセージの方向とキーの使用方法について適切なセキュリティ トークン プロバイダーを作成するために、CreateSecurityTokenProvider メソッドをオーバーライドします。
internal class MyServiceCredentialsSecurityTokenManager : ServiceCredentialsSecurityTokenManager { MyServiceCredentials credentials; public MyServiceCredentialsSecurityTokenManager( MyServiceCredentials credentials) : base(credentials) { this.credentials = credentials; } public override SecurityTokenProvider CreateSecurityTokenProvider( SecurityTokenRequirement requirement) { SecurityTokenProvider result = null; if (requirement.TokenType == SecurityTokenTypes.X509Certificate) { MessageDirection direction = requirement. GetProperty<MessageDirection>( ServiceModelSecurityTokenRequirement. MessageDirectionProperty); if (direction == MessageDirection.Input) { if (requirement.KeyUsage == SecurityKeyUsage.Exchange) { result = new X509SecurityTokenProvider( credentials.ServiceEncryptingCertificate); } else { result = new X509SecurityTokenProvider( credentials.ClientSigningCertificate); } } else { if (requirement.KeyUsage == SecurityKeyUsage.Signature) { result = new X509SecurityTokenProvider( credentials.ServiceSigningCertificate); } else { result = new X509SecurityTokenProvider( credentials.ClientEncryptingCertificate); } } } else { result = base.CreateSecurityTokenProvider(requirement); } return result; } }
Friend Class MyServiceCredentialsSecurityTokenManager Inherits ServiceCredentialsSecurityTokenManager Private credentials As MyServiceCredentials Public Sub New(ByVal credentials As MyServiceCredentials) MyBase.New(credentials) Me.credentials = credentials End Sub Public Overrides Function CreateSecurityTokenProvider(ByVal requirement As SecurityTokenRequirement) As SecurityTokenProvider Dim result As SecurityTokenProvider = Nothing If requirement.TokenType = SecurityTokenTypes.X509Certificate Then Dim direction = requirement.GetProperty(Of MessageDirection)(ServiceModelSecurityTokenRequirement.MessageDirectionProperty) If direction = MessageDirection.Input Then If requirement.KeyUsage = SecurityKeyUsage.Exchange Then result = New X509SecurityTokenProvider(credentials.ServiceEncryptingCertificate) Else result = New X509SecurityTokenProvider(credentials.ClientSigningCertificate) End If Else If requirement.KeyUsage = SecurityKeyUsage.Signature Then result = New X509SecurityTokenProvider(credentials.ServiceSigningCertificate) Else result = New X509SecurityTokenProvider(credentials.ClientEncryptingCertificate) End If End If Else result = MyBase.CreateSecurityTokenProvider(requirement) End If Return result End Function End Class
クライアントで複数の証明書を使用するには
カスタム バインドの作成 セキュリティ バインド要素は、要求と応答について異なるセキュリティ トークン プロバイダーが存在することを可能にする二重モードで動作する必要があります。 これを行うには、二重モード対応のトランスポートを使用するか、次のコードで示すように CompositeDuplexBindingElement を使用します。 次のステップで定義するカスタマイズ済みの IdentityVerifier をセキュリティ バインド要素にリンクします。 既定のクライアント資格情報を、事前に作成したカスタマイズ済みのクライアント資格情報に置き換えます。
EndpointAddress serviceEndpoint = new EndpointAddress(new Uri("http://localhost:6060/service")); CustomBinding binding = new CustomBinding(); AsymmetricSecurityBindingElement securityBE = SecurityBindingElement.CreateMutualCertificateDuplexBindingElement( MessageSecurityVersion. WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10); // Add a custom IdentityVerifier because the service uses two certificates // (one for signing and one for encryption) and an endpoint identity that // contains a single identity claim. securityBE.LocalClientSettings.IdentityVerifier = new MyIdentityVerifier(); binding.Elements.Add(securityBE); CompositeDuplexBindingElement compositeDuplex = new CompositeDuplexBindingElement(); compositeDuplex.ClientBaseAddress = new Uri("http://localhost:6061/client"); binding.Elements.Add(compositeDuplex); binding.Elements.Add(new OneWayBindingElement()); binding.Elements.Add(new HttpTransportBindingElement()); using (ChannelFactory<IMyServiceChannel> factory = new ChannelFactory<IMyServiceChannel>(binding, serviceEndpoint)) { MyClientCredentials credentials = new MyClientCredentials(); SetupCertificates(credentials); factory.Endpoint.Behaviors.Remove(typeof(ClientCredentials)); factory.Endpoint.Behaviors.Add(credentials); IMyServiceChannel channel = factory.CreateChannel(); Console.WriteLine(channel.Hello("world")); channel.Close(); }
Dim serviceEndpoint As New EndpointAddress(New Uri("http://localhost:6060/service")) Dim binding As New CustomBinding() Dim securityBE = SecurityBindingElement.CreateMutualCertificateDuplexBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10) ' Add a custom IdentityVerifier because the service uses two certificates ' (one for signing and one for encryption) and an endpoint identity that ' contains a single identity claim. securityBE.LocalClientSettings.IdentityVerifier = New MyIdentityVerifier() binding.Elements.Add(securityBE) Dim compositeDuplex As New CompositeDuplexBindingElement() compositeDuplex.ClientBaseAddress = New Uri("http://localhost:6061/client") With binding.Elements .Add(compositeDuplex) .Add(New OneWayBindingElement()) .Add(New HttpTransportBindingElement()) End With Using factory As New ChannelFactory(Of IMyServiceChannel)(binding, serviceEndpoint) Dim credentials As New MyClientCredentials() SetupCertificates(credentials) With factory.Endpoint.Behaviors .Remove(GetType(ClientCredentials)) .Add(credentials) End With Dim channel = factory.CreateChannel() Console.WriteLine(channel.Hello("world")) channel.Close() End Using
カスタム IdentityVerifier を定義します。 要求の暗号化と応答への署名で異なる証明書が使用されるため、サービスには複数の ID があります。
Note
次のサンプルで用意されたカスタム ID 検証方法では、デモンストレーション用であるため、エンドポイント ID 検査がまったく行われません。 これは製品版のコードでは、お勧めしません。
class MyIdentityVerifier : IdentityVerifier { IdentityVerifier defaultVerifier; public MyIdentityVerifier() { this.defaultVerifier = IdentityVerifier.CreateDefault(); } public override bool CheckAccess(EndpointIdentity identity, AuthorizationContext authContext) { // The following implementation is for demonstration only, and // does not perform any checks regarding EndpointIdentity. // Do not use this for production code. return true; } public override bool TryGetIdentity(EndpointAddress reference, out EndpointIdentity identity) { return this.defaultVerifier.TryGetIdentity(reference, out identity); } }
Friend Class MyIdentityVerifier Inherits IdentityVerifier Private defaultVerifier As IdentityVerifier Public Sub New() Me.defaultVerifier = IdentityVerifier.CreateDefault() End Sub Public Overrides Function CheckAccess(ByVal identity As EndpointIdentity, ByVal authContext As AuthorizationContext) As Boolean ' The following implementation is for demonstration only, and ' does not perform any checks regarding EndpointIdentity. ' Do not use this for production code. Return True End Function Public Overrides Function TryGetIdentity(ByVal reference As EndpointAddress, <System.Runtime.InteropServices.Out()> ByRef identity As EndpointIdentity) As Boolean Return Me.defaultVerifier.TryGetIdentity(reference, identity) End Function End Class
サービスで複数の証明書を使用するには
カスタム バインドの作成 セキュリティ バインド要素は、要求と応答について異なるセキュリティ トークン プロバイダーが存在することを可能にする二重モードで動作する必要があります。 クライアントの場合と同様に、二重モード対応のトランスポートを使用するか、次のコードで示すように CompositeDuplexBindingElement を使用します。 既定のサービス資格情報を、事前に作成したカスタマイズ済みのサービス資格情報に置き換えます。
Uri serviceEndpoint = new Uri("http://localhost:6060/service"); using (ServiceHost host = new ServiceHost(typeof(Service), serviceEndpoint)) { CustomBinding binding = new CustomBinding(); binding.Elements.Add(SecurityBindingElement. CreateMutualCertificateDuplexBindingElement( MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10)); binding.Elements.Add(new CompositeDuplexBindingElement()); binding.Elements.Add(new OneWayBindingElement()); binding.Elements.Add(new HttpTransportBindingElement()); MyServiceCredentials credentials = new MyServiceCredentials(); SetupCertificates(credentials); host.Description.Behaviors.Remove(typeof(ServiceCredentials)); host.Description.Behaviors.Add(credentials); ServiceEndpoint endpoint = host.AddServiceEndpoint( typeof(IMyService), binding, ""); host.Open(); Console.WriteLine("Service started, press ENTER to stop..."); Console.ReadLine(); }
Dim serviceEndpoint As New Uri("http://localhost:6060/service") Using host As New ServiceHost(GetType(Service), serviceEndpoint) Dim binding As New CustomBinding() With binding.Elements .Add(SecurityBindingElement.CreateMutualCertificateDuplexBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10)) .Add(New CompositeDuplexBindingElement()) .Add(New OneWayBindingElement()) .Add(New HttpTransportBindingElement()) End With Dim credentials As New MyServiceCredentials() SetupCertificates(credentials) With host.Description.Behaviors .Remove(GetType(ServiceCredentials)) .Add(credentials) End With Dim endpoint = host.AddServiceEndpoint(GetType(IMyService), binding, "") host.Open() Console.WriteLine("Service started, press ENTER to stop...") Console.ReadLine() End Using