作法:使用個別 X.509 憑證簽署與加密
本主題說明如何設定 Windows Communication Foundation (WCF),以使用不同的憑證來簽署和加密用戶端和服務上的訊息。
若要使用個別憑證進行簽署和加密,必須建立自訂用戶端和 (或) 服務憑證,因為 WCF 並未提供可以設定多個用戶端或服務憑證的 API。 此外,必須提供安全性權杖管理員才能運用多份憑證的資訊,以及建立可用於特定金鑰使用方式和訊息方向的適當安全性權杖提供者。
下方圖表會顯示所使用的主要類別、它們繼承的來源類別 (以上指標示),以及特定方向和屬性所傳回的類別。
MyClientCredentials
為 ClientCredentials 的自訂實作。它在圖表中所顯示的屬性都會傳回 X509Certificate2 的執行個體。
它的方法 CreateSecurityTokenManager 會傳回
MyClientCredentialsSecurityTokenManager
的執行個體。
MyClientCredentialsSecurityTokenManager
為 ClientCredentialsSecurityTokenManager 的自訂實作。- 它的方法 CreateSecurityTokenProvider 會傳回 X509SecurityTokenProvider 的執行個體。
如需自訂認證的詳細資訊,請參閱逐步解說:建立自訂用戶端和服務認證。
此外,您必須建立自訂身分識別驗證器,並將驗證器以自訂繫結連結到一個安全性繫結項目。 同時,不要使用預設認證,而必須使用自訂認證。
以下的圖表會顯示自訂繫結中使用的類別,以及自訂身分識別驗證器所連結的方法。 其中包含幾個繫結項目,全部都是繼承自 BindingElement。 AsymmetricSecurityBindingElement 有 LocalClientSecuritySettingsIdentityVerifier屬性,會傳回 MyIdentityVerifier
(便是自訂 的來源)的執行個體。
如需建立自訂身分識別驗證器的詳細資訊,請參閱操作說明:建立自訂用戶端身分識別驗證器。
使用個別憑證簽署與加密
定義從 ClientCredentials 類別繼承的新用戶端認證類別。 建立四個新屬性即可允許使用多個憑證規格:
ClientSigningCertificate
、ClientEncryptingCertificate
、ServiceSigningCertificate
ServiceEncryptingCertificate
及 。 此外,覆寫 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
及 。 此外,覆寫 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。 服務會擁有多個識別,因為此時會使用不同的憑證來加密要求及簽署回應。
注意
在下列範例中所提供的自訂識別驗證器,並不會示範執行任何的端點識別檢查。 但是在實際執行程式碼 (Production Code) 中不建議這樣做。
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