How to: Access a WSE 3.0 Service with a WCF Client


Windows Communication Foundation (WCF) clients are wire-level compatible with Web Services Enhancements (WSE) 3.0 for Microsoft .NET services when WCF clients are configured to use the August 2004 version of the WS-Addressing specification. However, WSE 3.0 services do not support the metadata exchange (MEX) protocol, so when you use the ServiceModel Metadata Utility Tool (Svcutil.exe) to create a WCF client class, the security settings are not applied to the generated WCF client. Therefore, you must specify the security settings that the WSE 3.0 service requires after the WCF client is generated.

You can apply these security settings by using a custom binding to take into account the WSE 3.0 service's requirements and the interoperable requirements between a WSE 3.0 service and a WCF client. These interoperability requirements include the aforementioned use of the August 2004 WS-Addressing specification and the WSE 3.0default message protection of SignBeforeEncrypt. The default message protection for WCF is SignBeforeEncryptAndEncryptSignature. This topic details how to create a WCF binding that interoperates with a WSE 3.0 service. WCF also provides a sample that incorporates this binding. For more information about this sample, see Interoperating with ASMX Web Services.

To access a WSE 3.0 Web service with a WCF client

  1. Run the ServiceModel Metadata Utility Tool (Svcutil.exe) to create a WCF client for the WSE 3.0 Web service.

    For a WSE 3.0 Web service, a WCF client is created. Because WSE 3.0 does not support the MEX protocol, you cannot use the tool to retrieve the security requirements for the Web service. The application developer must add the security settings for the client.

    For more information about creating a WCF client, see the How to: Create a Client.

  2. 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:

    1. 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 : Binding

    2. Add properties to the class that specify the WSE turnkey assertion used by the WSE service, whether derived keys are required, whether secure sessions are used, whether signature confirmations are required, and the message protection settings. In WSE 3.0, a turnkey assertion specifies the security requirements for a client or Web service—similar to the authentication mode of a binding in WCF.

      The following code example defines the SecurityAssertion, RequireDerivedKeys, EstablishSecurityContext, and 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.

              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; }

    3. 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 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");
                      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;
                      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;
                      case WseSecurityAssertion.AnonymousForCertificate:
                          transport = new HttpTransportBindingElement();
                          securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateAnonymousForCertificateBindingElement();
                          ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation;
                          ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder;
                      case WseSecurityAssertion.MutualCertificate11:
                          transport = new HttpTransportBindingElement();
                          securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11);
                          ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation;
                          ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder;
                      case WseSecurityAssertion.Kerberos:
                          transport = new HttpTransportBindingElement();
                          securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateKerberosBindingElement();
                          ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation;
                          ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder;
                          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
                  //Secure Conversation 
                  if (establishSecurityContext == true)
                      SymmetricSecurityBindingElement secureconversation =
                                                          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;
                      securityBinding = secureconversation;
                  // Add the security binding to the binding collection
                  // 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;
                  // Add the transport
                  // return the binding elements
                  return bec;

  3. 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.

            static void CallWseService(bool usePolicyFile)
                EndpointAddress address = new EndpointAddress(new Uri("http://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"),
                WseHttpBinding binding = new WseHttpBinding();
                if (!usePolicyFile)
                    binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate;
                    binding.EstablishSecurityContext = true;
                    binding.RequireDerivedKeys = true;
                    binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
                    binding.LoadPolicy("..\\wse3policyCache.config", "ServerPolicy");
                WSSecurityAnonymousServiceSoapClient client = new WSSecurityAnonymousServiceSoapClient(binding, address);

The following code example defines a custom binding that exposes properties that correspond to the properties of a WSE 3.0 turnkey security assertion. That custom binding, which is named WseHttpBinding, is then used to specify the binding properties for a WCF client that communicates with the WSSecurityAnonymous WSE 3.0 QuickStart sample.

Interoperating with WSE