Anvisningar: Använda separata X.509-certifikat för signering och kryptering
Det här avsnittet visar hur du konfigurerar Windows Communication Foundation (WCF) för att använda olika certifikat för meddelandesignering och kryptering på både klienten och tjänsten.
För att separata certifikat ska kunna användas för signering och kryptering måste en anpassad klient eller tjänstautentiseringsuppgifter (eller båda) skapas eftersom WCF inte tillhandahåller något API för att ange flera klient- eller tjänstcertifikat. Dessutom måste en säkerhetstokenhanterare tillhandahållas för att utnyttja information om flera certifikat och för att skapa en lämplig säkerhetstokenprovider för angiven nyckelanvändning och meddelanderiktning.
Följande diagram visar de huvudklasser som används, de klasser som de ärver från (visas med en uppåtriktad pil) och returtyperna för vissa metoder och egenskaper.
MyClientCredentials
är en anpassad implementering av ClientCredentials.Dess egenskaper som visas i diagrammet returnerar alla instanser av X509Certificate2.
Dess metod CreateSecurityTokenManager returnerar en instans av
MyClientCredentialsSecurityTokenManager
.
MyClientCredentialsSecurityTokenManager
är en anpassad implementering av ClientCredentialsSecurityTokenManager.- Dess metod CreateSecurityTokenProvider returnerar en instans av X509SecurityTokenProvider.
Mer information om anpassade autentiseringsuppgifter finns i Genomgång: Skapa anpassade klient- och tjänstautentiseringsuppgifter.
Dessutom måste du skapa en anpassad identitetsverifierare och länka den till ett säkerhetsbindningselement i en anpassad bindning. Du måste också använda de anpassade autentiseringsuppgifterna i stället för standardautentiseringsuppgifterna.
Följande diagram visar de klasser som ingår i den anpassade bindningen och hur den anpassade identitetsverifieraren är länkad. Det finns flera bindningselement som alla ärver från BindingElement. AsymmetricSecurityBindingElement har LocalClientSecuritySettings egenskapen, som returnerar en instans av IdentityVerifier, som MyIdentityVerifier
är anpassad från.
Mer information om hur du skapar en anpassad identitetsverifierare finns i How to: How to: Create a Custom Client Identity Verifier (Så här skapar du en anpassad klientidentitetsverifierare).
Så här använder du separata certifikat för signering och kryptering
Definiera en ny klass för klientautentiseringsuppgifter som ärver från ClientCredentials klassen. Implementera fyra nya egenskaper för att tillåta flera certifikatspecifikationer:
ClientSigningCertificate
,ClientEncryptingCertificate
,ServiceSigningCertificate
ochServiceEncryptingCertificate
. Åsidosätt CreateSecurityTokenManager även metoden för att returnera en instans av den anpassade ClientCredentialsSecurityTokenManager klassen som definieras i nästa steg.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
Definiera en ny klientsäkerhetstokenhanterare som ärver från ClientCredentialsSecurityTokenManager klassen. Åsidosätt CreateSecurityTokenProvider metoden för att skapa en lämplig säkerhetstokenprovider. Parametern
requirement
(a SecurityTokenRequirement) innehåller meddelanderiktningen och nyckelanvändningen.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
Definiera en ny klass för tjänstautentiseringsuppgifter som ärver från ServiceCredentials klassen. Implementera fyra nya egenskaper för att tillåta flera certifikatspecifikationer:
ClientSigningCertificate
,ClientEncryptingCertificate
,ServiceSigningCertificate
ochServiceEncryptingCertificate
. Åsidosätt CreateSecurityTokenManager även metoden för att returnera en instans av den anpassade ServiceCredentialsSecurityTokenManager klassen som definieras i nästa steg.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
Definiera en ny tjänstsäkerhetstokenhanterare som ärver från ServiceCredentialsSecurityTokenManager klassen. Åsidosätt CreateSecurityTokenProvider metoden för att skapa en lämplig säkerhetstokenprovider med tanke på den skickade meddelanderiktningen och nyckelanvändningen.
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
Så här använder du flera certifikat på klienten
Skapa en anpassad bindning. Säkerhetsbindningselementet måste fungera i duplexläge för att olika säkerhetstokenproviders ska kunna finnas för begäranden och svar. Ett sätt att göra detta är att använda en duplex-kompatibel transport eller att använda som CompositeDuplexBindingElement visas i följande kod. Länka den anpassade som definieras i nästa steg till säkerhetsbindningselementet IdentityVerifier . Ersätt standardklientautentiseringsuppgifterna med de anpassade klientautentiseringsuppgifterna som skapades tidigare.
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
Definiera en anpassad IdentityVerifier. Tjänsten har flera identiteter eftersom olika certifikat används för att kryptera begäran och signera svaret.
Kommentar
I följande exempel utför den angivna anpassade identitetsverifieraren inte någon slutpunktsidentitetskontroll i demonstrationssyfte. Detta rekommenderas inte för produktionskod.
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
Så här använder du flera certifikat i tjänsten
Skapa en anpassad bindning. Säkerhetsbindningselementet måste fungera i duplexläge för att olika säkerhetstokenproviders ska kunna finnas för begäranden och svar. Precis som med klienten använder du en duplex-kompatibel transport eller användning CompositeDuplexBindingElement som visas i följande kod. Ersätt standardautentiseringsuppgifterna för tjänsten med de anpassade autentiseringsuppgifterna för tjänsten som skapades tidigare.
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