Windows Communication Foundation (WCF) clients are wire-level compatible with Web Services Enhancements 3.0 for Microsoft .NET (WSE) services when WCF clients are configured to use the August 2004 version of the WS-Addressing specification.
To configure a WCF client to interoperate with a WSE 3.0 Web service
-
Run the Service Metadata Utility Tool (SvcUtil.exe) to create a WCF client for the WSE 3.0 Web service.
For a WSE Web service, a WCF client class is created.
For details about creating a WCF client, see the How to: Create a Windows Communication Foundation Client.
-
Create a class that represents a binding that can communicate with WSE 3.0 Web services.
The following class is part of the Interoperating with WSE sample.
-
Create a class that derives from the Binding class.
The following code example creates a class named WseHttpBinding that derives from the Binding class.
Public Class WseHttpBinding
Inherits Binding
public class WseHttpBinding : Binding
{
-
Add properties to the class that specify the WSE turnkey assertion, whether derived keys are required, whether secure sessions are used, whether signature confirmations are required, and the message protection settings.
The following code example defines SecurityAssertion, RequireDerivedKeys, EstablishSecurityContext, MessageProtectionOrder properties that specify the WSE turnkey assertion, whether derived keys are required, whether secure sessions are used, whether signature confirmations are required, and the message protection settings, respectively.
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; }
}
-
Override the CreateBindingElements method to set the binding properties.
The following code example specifies the transport, message encoding, and message protection settings by getting the values of the SecurityAssertion and MessageProtectionOrder properties.
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;
}
-
In the client application code, add code to set the binding properties.
The following code example specifies that the WCF client must use message protection and authentication as defined by the WSE 3.0 AnonymousForCertificate turnkey security assertion. Additionally, secure sessions and derived keys are required.
Private Shared Sub CallWseService(ByVal usePolicyFile As Boolean)
Dim address As New EndpointAddress(New Uri("http://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("http://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);
Example
The following code example defines a custom binding that exposes properties that correspond to the properties of a WSE 3.0 turnkey security assertion. The custom binding, which is named WseHttpBinding, is then used to specify the binding properties for a WCF client.
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("http://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("http://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();
}
}
}
See Also
© 2007 Microsoft Corporation. All rights reserved.