WCF client passing username token with mustUnderstand set to true
Asked Answered
F

3

7

I'm tasked with creating a WCF service that will be consumed by an external client. The client is using WSSE security, specifically, they're passing a username token via a SOAP header.

The WCF service is hosted on an IIS server with SSL enabled.

At this point, I have a semi-working prototype. The issue I'm dealing with now is that the SOAP header has the mustUnderstand attribute set to 1, and this causes the process to fail.

I'd like some advice (or better yet, a code example smiles) on how to handle the username token in such a fashion as to return the proper response when the mustUnderstand attribute is true.

Here's a sample of the SOAP request that's failing:

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
       <soapenv:Header>
          <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
             <wsse:UsernameToken>
                <wsse:Username>TestUser</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">TestPWD</wsse:Password>
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">NzU3MjFhN2YtYTlmYS00ZWZjLTkxNjktY2ExZjlkZDEwNzE5</wsse:Nonce>
                <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2011-10-26T03:04:39Z</wsu:Created>
             </wsse:UsernameToken>
          </wsse:Security>
       </soapenv:Header>
       <soapenv:Body>
          <tem:Getstuff>
             <tem:Arg1>Arg1</tem:Arg1>
             <tem:Arg2>Arg2</tem:Arg2>
          </tem:Getstuff>
       </soapenv:Body>
    </soapenv:Envelope>

If soapenv:mustUnderstand="1" is changed to soapenv:mustUnderstand="0", then the process works.


PS: Here's a revised sample request the client sent:

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
      <s:Header>
        <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/WService/Getstuff</Action>
        <Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
          <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="removed" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:Username>TestUser</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">TestPass</wsse:Password>
            <wsse:Nonce>2Udx78sh2y2xRJYJpZZ9+w==</wsse:Nonce>
            <wsu:Created>2011-09-26T19:12:48Z</wsu:Created>
          </wsse:UsernameToken>
        </Security>
      </s:Header>
      <s:Body>
        <Getstuff xmlns="http://tempuri.org/">
         <Arg1>Arg1</Arg1>
         <Arg2>Arg2</Arg2>
        </Getstuff>
      </s:Body>
    </s:Envelope>

I receive the following response to the above requests:

    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
       <s:Body>
          <s:Fault>
             <faultcode>s:MustUnderstand</faultcode>
             <faultstring xml:lang="en-US">The header 'Security' from the namespace 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' was not understood by the recipient of this message, causing the message to not be processed.  This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process.  Please ensure that the configuration of the client's binding is consistent with the service's binding.</faultstring>
          </s:Fault>
       </s:Body>
    </s:Envelope>

Here's the binding:

<bindings>
  <basicHttpBinding>
    <binding name="TransportBind" maxBufferSize="2147483647" maxBufferPoolSize="2147483647"
      maxReceivedMessageSize="2147483647">
      <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
        maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
      <security mode="Transport">
        <transport clientCredentialType="None" />
      </security>
    </binding>
    <binding name="basic" maxBufferSize="2147483647" maxBufferPoolSize="2147483647"
      maxReceivedMessageSize="2147483647">
      <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
        maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
    </binding>
  </basicHttpBinding>
</bindings>
Finnish answered 7/11, 2011 at 14:42 Comment(6)
Are you sure it is using WSE (Web Service Extensions)?Ossify
No, I'm not positive. I've added to the above sample a revised sample request the client sent.Finnish
Exactly what is the "failure". Exactly what made you believe that the failure was caused by mustUnderstand?Ossify
Added the error response to the above (not enough characters to fit in a comment).Finnish
Can you share with us the binding you're using on your service? Is it using wsHttpBinding? As an experiment, can you create a .NET client program to consume your service? Does your .NET client also send mustUnderstand?Ossify
Added binding config above. The .Net client doesn't send mustUnderstand.Finnish
O
2

Your binding is basicHttpBinding. You need to use wsHttpBinding.

Ossify answered 8/11, 2011 at 9:2 Comment(0)
O
1

I solved this issue by not using the generated WS-Security header (when you add the Service Reference) in the WCF configuration, but rather commenting this out and instead letting .NET generate the header itself by using Client Credentials and specifying a Security Mode of "TransportWithMessageCredential":

client.ClientCredentials.UserName.UserName = "UserName";
client.ClientCredentials.UserName.Password = "Password";

<basicHttpBinding>
   <binding name="Binding">
      <security mode="TransportWithMessageCredential">
          <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
          <message clientCredentialType="UserName" algorithmSuite="Default" />
      </security>
   </binding>
</basicHttpBinding>

(We are using SSL, so hence this security setting).

Commented-out generated header:

 <client>
        <endpoint ...>
            <!--<headers>
                <wsse:Security...>
                </wsse:Security>
            </headers>-->
        </endpoint>
 </client>

Unfortunately I don't know enough WCF to capture the raw soap request/response to compare the difference and see why ClientCredentials doesn't cause the "was not understood" fault, while the generated header does.

As an aside, according to the MSDN documentation, if you only use "Transport" it won't know to use WS-Security: "Leave this property at its default value, which is System.ServiceModel.SecurityMode.Transport to not use WS-Security."

Od answered 15/10, 2012 at 1:18 Comment(0)
B
0

+1 @JohnSaunders because he is most likely barking up the right tree here.

Is your client .NET/WCF? If not, it may not be implementing WS-Security, or at least not in the way WCF wants it to.

If the client is .NET, this is just an mismatched binding on the client side.

The mustUnderstand flag says that the WS-Security header must be acknowledged and processed. A non-WS-Security client, whether it's because it doesn't speak WS-Security or isn't configured to, will ignore the header, try to use the message anyway and the server will punt.

Your other option is to turn of repudiation on the server. It'll stop sending WS-Security headers. Of course, then you don't get repudiation.

Bayne answered 7/11, 2011 at 23:30 Comment(1)
I'm fairly certain it's a java client, but I don't know which flavor.Finnish

© 2022 - 2024 — McMap. All rights reserved.