WWSAPI to WCF interop 5: WSFederationHttpBinding with mixed mode security

WCF endpoints with WSFederationHttpBinding accept SAML tokens issued by trusted Security Token Services (STS, the Issuing Party, or IP). The first thing that should be noted with WSFederationHttpBinding is that secure conversation (specifically, the February 2005 version) is turned on and no knob is provided to turn it off (in fact, this is the case for WS2007FederationHttpBinding in .NET 3.5 as well). WSFederationHttpBinding has three security modes: None, Message, TransportWithMessageCredential. In Win7, WWSAPI will only provide support compatible to the TransportWithMessageCredential mode.

 

First of all, in order to send a SAML token, the client has to obtain such a token. For a WCF client using WSFederationHttpBinding, this happens automatically as the client will follow the STS endpoint specified in the binding to retrieve the SAML token. When there are multiple STS’s, the WCF client actually automagically unwinds the trust chain. In contrast, WWSAPI intentionally does not provide such magic. Instead, an API named WsRequestSecurityToken is provided for the client to call to get the token from the STS. Details of how to use WsRequestSecurityToken will be discussed in a later post. For the purpose of this post, we just need to know that the client has to call WsRequestSecurityToken (possibly multiple times) to get a token before building a proxy or channel to communicate with the WCF service endpoint (the Relying Party, or RP). The token returned from WsRequestSecurityToken is an opaque WWSAPI handle, WS_SECURITY_TOKEN.

 

The WS_SECURITY_TOKEN will be used in the WS_XML_TOKEN_MESSAGE_SECURITY_BINDING to represent the message credential. In addition to WS_XML_TOKEN_MESSAGE_SECURITY_BINDING, we need WS_SSL_TRANSPORT_SECURITY_BINDING in order to build the security description that matches the TransportWithMessageCredential mode in WSFederationHttpBinding. The following code shows how to do this.

    WS_SECURITY_TOKEN* securityToken = NULL;

    // call WsRequestSecurityToken to retrieve the security token

    // e.g. WsRequestSecurityToken(channel, NULL, 0, &securityToken, NULL, error);

    // declare and initialize an XML security token message security binding

    WS_XML_TOKEN_MESSAGE_SECURITY_BINDING xmlTokenBinding = {}; // zero out the struct

    xmlTokenBinding.binding.bindingType = WS_XML_TOKEN_MESSAGE_SECURITY_BINDING_TYPE; // set the binding type

    xmlTokenBinding.bindingUsage = WS_SUPPORTING_MESSAGE_SECURITY_USAGE; // set the binding usage

    xmlTokenBinding.xmlToken = securityToken; // securityToken returned from WsRequestSecurityToken

   

    // declare and initialize an SSL transport security binding

    WS_SSL_TRANSPORT_SECURITY_BINDING sslBinding = {}; // zero out the struct

    sslBinding.binding.bindingType = WS_SSL_TRANSPORT_SECURITY_BINDING_TYPE; // set the binding type

   

    // declare and initialize the array of all security bindings

    WS_SECURITY_BINDING* bootstrapSecurityBindings[2] = { &sslBinding.binding, &xmlTokenBinding.binding };

   

    // declare and initialize the security description

    WS_SECURITY_DESCRIPTION bootstrapSecurityDescription = {}; // zero out the struct

    bootstrapSecurityDescription.securityBindings = bootstrapSecurityBindings;

    bootstrapSecurityDescription.securityBindingCount = WsCountOf(bootstrapSecurityBindings);

 

Now this security description won’t work with a WCF endpoint using WsFederationHttpBinding. Why? We need secure conversation. As noted earlier, secure conversation is required in WSFederationHttpBinding, so the WWSAPI client has to use the WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING to describe how to use secure conversation (version, entropy mode, etc.). If you are familiar with the WS-SecureConversation spec, you know that the original client credential is used to negotiate a session key, which is used for future client authentication. In the WWSAPI design, the initial negotiation using the client credential (the SAML token here) is called bootstrapping. A separate channel is created to do this negotiation, which needs to be described in a different security description. This is exactly what the code above gives you (notice the word “bootstrap” in the variable name?). Now if you look into the WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING, you will know how to hook this up.

 

    // declare and initialize a secure conversation message security binding

    WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING contextBinding = {}; // zero out the struct

    contextBinding.binding.bindingType = WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING_TYPE;

    contextBinding.bindingUsage = WS_SUPPORTING_MESSAGE_SECURITY_USAGE;

    contextBinding.bootstrapSecurityDescription = &bootstrapSecurityDescription;

    // declare and initialize the array of all security bindings

    WS_SECURITY_BINDING* securityBindings[2] = {&contextBinding.binding, &sslBinding.binding};

    // declare and initialize the security description

    WS_SECURITY_DESCRIPTION securityDescription = {}; // zero out the struct

    securityDescription.securityBindings = securityBindings;

    securityDescription.securityBindingCount = WsCountOf(securityBindings);

Please note that in the code above we reuse the sslBinding used in the bootstrap security description. This is necessary because WWSAPI requires the transport security used for protecting the secure conversation to be same as the one initially used to negotiate the session key. This requirement simplifies the design as well as the usage pattern. Of course you don’t have to use the same variable. As long as the binding type and settings match, it is ok.

 

Note: the corresponding WSFederationHttpBinding object can be created by new WSFederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential). It is represented in the following element in config:

      <wsFederationHttpBinding>

        <binding name="federation">

          <security mode="TransportWithMessageCredential">

            <message>

              <issuer address="https://localhost:8443/FederationSample/BookStoreSTS/wsHttps"

                binding="wsHttpBinding" bindingConfiguration="UPOverHttps" />

            </message>

       </security>

        </binding>

      </wsFederationHttpBinding>