Comment : accéder à un service WSE 3.0 avec un client WCF
Les clients Windows Communication Foundation (WCF) sont compatibles au niveau câble avec les services WSE (Web Services Enhancements) 3.0 for Microsoft .NET lorsque les clients WCF sont configurés pour utiliser la version d'août 2004 de la spécification WS-Addressing. Toutefois, les services WSE 3.0 ne prennent pas en charge le protocole d'échange de métadonnées (MEX) ; par conséquent, lorsque vous utilisez l'Outil Service Model Metadata Tool (Svcutil.exe) pour créer une classe de client WCF, les paramètres de sécurité ne sont pas appliqués au client WCF généré. Par conséquent, vous devez spécifier les paramètres de sécurité que le service WSE 3.0 requiert une fois le client WCF généré.
Vous pouvez appliquer ces paramètres de sécurité en utilisant une liaison personnalisée pour prendre en compte les spécifications du service WSE 3.0 et les spécifications interopérables entre un service WSE 3.0 et un client WCF. Ces spécifications d'interopérabilité incluent l'utilisation précédemment mentionnée de la version d'août 2004 de la spécification WS-Addressing et la protection des messages par défaut WSE 3.0 de SignBeforeEncrypt. La protection des messages par défaut pour WCF est SignBeforeEncryptAndEncryptSignature. Cette rubrique explique comment créer une liaison WCF qui interagit avec un service WSE 3.0. WCF fournit également un exemple qui incorpore cette liaison. Pour plus d'informations sur le sujet suivant cet exemple, consultez Interoperating with WSE.
Pour accéder à un service Web WSE 3.0 avec un client WCF
Exécutez Outil Service Model Metadata Tool (Svcutil.exe) pour créer un client WCF pour le service Web WSE 3.0.
Pour un service Web WSE 3.0, un client WCF est créé. WSE 3.0 ne prenant pas en charge le protocole MEX, vous ne pouvez pas utiliser l'outil pour récupérer les conditions de sécurité pour le service Web. Le développeur d'applications doit ajouter les paramètres de sécurité pour le client.
Pour plus d'informations sur le sujet suivant la création d'un client WCF, consultez Comment : créer un client Windows Communication Foundation.
Créez une classe qui représente une liaison pouvant communiquer avec les services Web WSE 3.0.
La classe suivante fait partie de l'exemple Interoperating with WSE :
Créez une classe qui dérive de la classe Binding.
L'exemple de code suivant crée une classe nommée
WseHttpBinding
qui dérive de la classe Binding.Public Class WseHttpBinding Inherits Binding
public class WseHttpBinding : Binding {
Ajoutez à la classe des propriétés spécifiant l'assertion clé en main WSE utilisée par le service WSE, si des clés dérivées sont requises, si des sessions sécurisées sont utilisées, si des confirmations de signature sont requises, ainsi que les paramètres de protection des messages. Dans WSE 3.0, une assertion clé en main spécifie les conditions de sécurité pour un client ou un service Web (similaire au mode d'authentification d'une liaison dans WCF).
L'exemple de code suivant définit les propriétés
SecurityAssertion
,RequireDerivedKeys
,EstablishSecurityContext
etMessageProtectionOrder
qui spécifient l'assertion clé en main WSE, si des clés dérivées sont requises, si des sessions sécurisées sont utilisées, si des confirmations de signature sont requises, ainsi que les paramètres de protection des messages, respectivement.Public Property SecurityAssertion() As WseSecurityAssertion Get Return assertion End Get Set(ByVal value As WseSecurityAssertion) assertion = value End Set End Property Private m_requireDerivedKeys As Boolean Public Property RequireDerivedKeys() As Boolean Get Return m_requireDerivedKeys End Get Set(ByVal value As Boolean) m_requireDerivedKeys = value End Set End Property Private m_establishSecurityContext As Boolean Public Property EstablishSecurityContext() As Boolean Get Return m_establishSecurityContext End Get Set(ByVal value As Boolean) m_establishSecurityContext = value End Set End Property Private m_requireSignatureConfirmation As Boolean Public Property RequireSignatureConfirmation() As Boolean Get Return m_requireSignatureConfirmation End Get Set(ByVal value As Boolean) m_requireSignatureConfirmation = value End Set End Property Private m_messageProtectionOrder As MessageProtectionOrder Public Property MessageProtectionOrder() As MessageProtectionOrder Get Return m_messageProtectionOrder End Get Set(ByVal value As MessageProtectionOrder) m_messageProtectionOrder = value End Set End Property
private WseSecurityAssertion assertion; public WseSecurityAssertion SecurityAssertion { get { return assertion; } set { assertion = value; } } private bool requireDerivedKeys; public bool RequireDerivedKeys { get { return requireDerivedKeys; } set { requireDerivedKeys = value; } } private bool establishSecurityContext; public bool EstablishSecurityContext { get { return establishSecurityContext; } set { establishSecurityContext = value; } } private bool requireSignatureConfirmation; public bool RequireSignatureConfirmation { get { return requireSignatureConfirmation; } set { requireSignatureConfirmation = value; } } private MessageProtectionOrder messageProtectionOrder; public MessageProtectionOrder MessageProtectionOrder { get { return messageProtectionOrder; } set { messageProtectionOrder = value; } }
Substituez la méthode CreateBindingElements pour définir les propriétés de la liaison.
L'exemple de code suivant spécifie le transport, l'encodage de message et les paramètres de protection des messages en obtenant les valeurs des propriétés
SecurityAssertion
etMessageProtectionOrder
.Public Overloads Overrides Function CreateBindingElements() As BindingElementCollection 'SecurityBindingElement sbe = bec.Find<SecurityBindingElement>(); Dim bec As New BindingElementCollection() ' By default http transport is used Dim securityBinding As SecurityBindingElement Dim transport As BindingElement Select Case assertion Case WseSecurityAssertion.UsernameOverTransport transport = New HttpsTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateUserNameOverTransportBindingElement(), TransportSecurityBindingElement) If m_establishSecurityContext = True Then Throw New InvalidOperationException("Secure Conversation is not supported for this Security Assertion Type") End If If m_requireSignatureConfirmation = True Then Throw New InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type") End If Exit Select Case WseSecurityAssertion.MutualCertificate10 transport = New HttpTransportBindingElement() securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10) If m_requireSignatureConfirmation = True Then Throw New InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type") End If DirectCast(securityBinding, AsymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.UsernameForCertificate transport = New HttpTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateUserNameForCertificateBindingElement(), SymmetricSecurityBindingElement) ' We want signatureconfirmation on the bootstrap process ' either for the application messages or for the RST/RSTR DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.AnonymousForCertificate transport = New HttpTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateAnonymousForCertificateBindingElement(), SymmetricSecurityBindingElement) DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.MutualCertificate11 transport = New HttpTransportBindingElement() securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11) DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.Kerberos transport = New HttpTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateKerberosBindingElement(), SymmetricSecurityBindingElement) DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case Else Throw New NotSupportedException("This supplied Wse security assertion is not supported") End Select 'Set defaults for the security binding securityBinding.IncludeTimestamp = True ' Derived Keys ' Set the preference for derived keys before creating the binding for SecureConversation. securityBinding.SetKeyDerivation(m_requireDerivedKeys) 'Secure Conversation If m_establishSecurityContext = True Then Dim secureconversation As SymmetricSecurityBindingElement = DirectCast(SymmetricSecurityBindingElement.CreateSecureConversationBindingElement(securityBinding, False), SymmetricSecurityBindingElement) ' This is the default 'secureconversation.DefaultProtectionLevel = ProtectionLevel.EncryptAndSign; 'Set defaults for the secure conversation binding secureconversation.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256 ' We do not want signature confirmation on the application level messages ' when secure conversation is enabled. secureconversation.RequireSignatureConfirmation = False secureconversation.MessageProtectionOrder = m_messageProtectionOrder secureconversation.SetKeyDerivation(m_requireDerivedKeys) securityBinding = secureconversation End If ' Add the security binding to the binding collection bec.Add(securityBinding) ' Add the message encoder. Dim textelement As New TextMessageEncodingBindingElement() textelement.MessageVersion = System.ServiceModel.Channels.MessageVersion.Soap11WSAddressingAugust2004 'These are the defaults required for WSE 'textelement.MessageVersion = MessageVersion.Soap11Addressing1; 'textelement.WriteEncoding = System.Text.Encoding.UTF8; bec.Add(textelement) ' Add the transport bec.Add(transport) ' return the binding elements Return bec End Function
public override BindingElementCollection CreateBindingElements() { //SecurityBindingElement sbe = bec.Find<SecurityBindingElement>(); BindingElementCollection bec = new BindingElementCollection(); // By default http transport is used SecurityBindingElement securityBinding; BindingElement transport; switch (assertion) { case WseSecurityAssertion.UsernameOverTransport: transport = new HttpsTransportBindingElement(); securityBinding = (TransportSecurityBindingElement)SecurityBindingElement.CreateUserNameOverTransportBindingElement(); if (establishSecurityContext == true) throw new InvalidOperationException("Secure Conversation is not supported for this Security Assertion Type"); if (requireSignatureConfirmation == true) throw new InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type"); break; case WseSecurityAssertion.MutualCertificate10: transport = new HttpTransportBindingElement(); securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10); if (requireSignatureConfirmation == true) throw new InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type"); ((AsymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.UsernameForCertificate: transport = new HttpTransportBindingElement(); securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateUserNameForCertificateBindingElement(); // We want signatureconfirmation on the bootstrap process // either for the application messages or for the RST/RSTR ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.AnonymousForCertificate: transport = new HttpTransportBindingElement(); securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateAnonymousForCertificateBindingElement(); ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.MutualCertificate11: transport = new HttpTransportBindingElement(); securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11); ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.Kerberos: transport = new HttpTransportBindingElement(); securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateKerberosBindingElement(); ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; default: throw new NotSupportedException("This supplied Wse security assertion is not supported"); } //Set defaults for the security binding securityBinding.IncludeTimestamp = true; // Derived Keys // set the preference for derived keys before creating SecureConversationBindingElement securityBinding.SetKeyDerivation(requireDerivedKeys); //Secure Conversation if (establishSecurityContext == true) { SymmetricSecurityBindingElement secureconversation = (SymmetricSecurityBindingElement)SymmetricSecurityBindingElement.CreateSecureConversationBindingElement( securityBinding, false); // This is the default //secureconversation.DefaultProtectionLevel = ProtectionLevel.EncryptAndSign; //Set defaults for the secure conversation binding secureconversation.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256; // We do not want signature confirmation on the application level messages // when secure conversation is enabled. secureconversation.RequireSignatureConfirmation = false; secureconversation.MessageProtectionOrder = messageProtectionOrder; secureconversation.SetKeyDerivation(requireDerivedKeys); securityBinding = secureconversation; } // Add the security binding to the binding collection bec.Add(securityBinding); // Add the message encoder. TextMessageEncodingBindingElement textelement = new TextMessageEncodingBindingElement(); textelement.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004; //These are the defaults required for WSE //textelement.MessageVersion = MessageVersion.Soap11Addressing1; //textelement.WriteEncoding = System.Text.Encoding.UTF8; bec.Add(textelement); // Add the transport bec.Add(transport); // return the binding elements return bec; }
Dans le code d'application cliente, ajoutez le code pour définir les propriétés de la liaison.
L'exemple de code suivant spécifie que le client WCF doit utiliser l'authentification et la protection des messages telles que définies par l'assertion de sécurité clé en main AnonymousForCertificate WSE 3.0. En outre, des sessions sécurisées et des clés dérivées sont requises.
Private Shared Sub CallWseService(ByVal usePolicyFile As Boolean) Dim address As New EndpointAddress(New Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"), EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer")) Dim binding As New WseHttpBinding() If Not usePolicyFile Then binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate binding.EstablishSecurityContext = True binding.RequireDerivedKeys = True binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt Else binding.LoadPolicy("..\wse3policyCache.config", "ServerPolicy") End If Dim client As New WSSecurityAnonymousServiceSoapClient(binding, address)
static void CallWseService(bool usePolicyFile) { EndpointAddress address = new EndpointAddress(new Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"), EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer")); WseHttpBinding binding = new WseHttpBinding(); if (!usePolicyFile) { binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate; binding.EstablishSecurityContext = true; binding.RequireDerivedKeys = true; binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt; } else binding.LoadPolicy("..\\wse3policyCache.config", "ServerPolicy"); WSSecurityAnonymousServiceSoapClient client = new WSSecurityAnonymousServiceSoapClient(binding, address);
Exemple
L'exemple de code suivant définit une liaison personnalisée qui expose des propriétés correspondant à celles d'une assertion de sécurité clé en main WSE 3.0. Cette liaison personnalisée, qui est appelée WseHttpBinding
, est utilisée pour spécifier les propriétés de liaison d'un client WCF qui communique avec l'exemple de Démarrage rapide WSE 3.0 WSSecurityAnonymous.
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Security.Cryptography.X509Certificates
Imports System.ServiceModel
Imports System.ServiceModel.Security
Imports System.ServiceModel.Channels
Imports Microsoft.VisualBasic
Namespace Microsoft.ServiceModel.Samples
' The service contract is defined in generatedClient.vb, generated from the service by
' the svcutil tool.
Class Program
Public Shared Sub Main(ByVal args As String())
CallWseService(True)
End Sub
Private Shared Sub CallWseService(ByVal usePolicyFile As Boolean)
Dim address As New EndpointAddress(New Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"), EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer"))
Dim binding As New WseHttpBinding()
If Not usePolicyFile Then
binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate
binding.EstablishSecurityContext = True
binding.RequireDerivedKeys = True
binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt
Else
binding.LoadPolicy("..\wse3policyCache.config", "ServerPolicy")
End If
Dim client As New WSSecurityAnonymousServiceSoapClient(binding, address)
' Need to supply the credentials depending on the type of WseSecurityAssertion used.
' Anonymous only requires server certificate. UsernameForCertificate would also require
' a username and password to be supplied.
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectDistinguishedName, "CN=WSE2QuickStartServer")
Dim symbols As String() = New String() {"FABRIKAM", "CONTOSO"}
Dim quotes As StockQuote() = client.StockQuoteRequest(symbols)
client.Close()
' Success!
For Each quote As StockQuote In quotes
Console.WriteLine("")
Console.WriteLine("Symbol: " + quote.Symbol)
Console.WriteLine("" & Chr(9) & "Name:" & Chr(9) & "" & Chr(9) & "" & Chr(9) & "" + quote.Name)
Console.WriteLine("" & Chr(9) & "Last Price:" & Chr(9) & "" & Chr(9) & "" & quote.Last)
Console.WriteLine("" & Chr(9) & "Previous Change:" & Chr(9) & "" & quote.PreviousChange & "%")
Next
Console.WriteLine("Press <ENTER> to terminate client.")
Console.ReadLine()
End Sub
End Class
End Namespace
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.ServiceModel.Channels;
namespace Microsoft.ServiceModel.Samples
{
// The service contract is defined in generatedClient.cs, generated from the service by
// the svcutil tool.
class Program
{
static void Main(string[] args)
{
CallWseService(true);
}
static void CallWseService(bool usePolicyFile)
{
EndpointAddress address = new EndpointAddress(new Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"),
EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer"));
WseHttpBinding binding = new WseHttpBinding();
if (!usePolicyFile)
{
binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate;
binding.EstablishSecurityContext = true;
binding.RequireDerivedKeys = true;
binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
}
else
binding.LoadPolicy("..\\wse3policyCache.config", "ServerPolicy");
WSSecurityAnonymousServiceSoapClient client = new WSSecurityAnonymousServiceSoapClient(binding, address);
// Need to supply the credentials depending on the type of WseSecurityAssertion used.
// Anonymous only requires server certificate. UsernameForCertificate would also require
// a username and password to be supplied.
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectDistinguishedName,
"CN=WSE2QuickStartServer");
string[] symbols = new string[] { "FABRIKAM", "CONTOSO" };
StockQuote[] quotes = client.StockQuoteRequest(symbols);
client.Close();
// Success!
foreach (StockQuote quote in quotes)
{
Console.WriteLine("");
Console.WriteLine("Symbol: " + quote.Symbol);
Console.WriteLine("\tName:\t\t\t" + quote.Name);
Console.WriteLine("\tLast Price:\t\t" + quote.Last);
Console.WriteLine("\tPrevious Change:\t" + quote.PreviousChange + "%");
}
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}