Search code examples
c#web-servicessoapwsewsse

Issue with Lumesse Web Services and WSSE plain text security


We're currently re-developing a legacy project that makes use of a SOAP web service from Lumesse, called over HTTPS.

http://developer.lumesse.com/Getting_Started

This is being consumed in a ASP.NET application; the original developer (who has long since left) never took advantage of using the provided WSDL, preferring to manually construct the request and parse the response. Whilst this is utter madness, this is what Lumesse's documentation actually recommends when being consumed from .NET, as their service uses obsolete WSSE plain text security.

Whilst we would not usually go against the grain, we'd much prefer to use the in-built support for consuming SOAP web services as opposed to rolling our own solution as the previous developer has.

We've had a few issues already, such as the failure to generate temporary classes that we have hacked around.

Unfortunately, we are now stuck when it comes to sending a successful SOAP request.

The exception that is thrown when we make a request is:

The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

Digging deeper, the actual response is as follows:

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
  <env:Header /> 
    <env:Body>
      <env:Fault xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
        <faultcode xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">wsse:FailedCheck</faultcode> 
        <faultstring>Expired message.</faultstring> 
      </env:Fault>
   </env:Body>
</env:Envelope>

Which appears to be that 'the signature or decryption was invalid'. Using the other developer's hobbled together solution, with the same credentials works as expected.

Why would it be failing here and what can we do to fix it? Is it possible without resorting to rolling our own request and response (or even recommended?) service?

http://schemas.xmlsoap.org/specs/ws-security/ws-security.htm

The WSDL we have used, supplied from Lumesse:

https://api3.lumesse-talenthub.com/CareerPortal/SOAP/FoAdvert?WSDL

The endpoint we are hitting:

https://api3.lumesse-talenthub.com/CareerPortal/SOAP/FoAdvert?api_key=xxx

Here is our code so far, heavily based off With C#, WCF SOAP consumer that uses WSSE plain text authentication? - which looks like the same issue.

Correct way communicate WSSE Usernametoken for SOAP webservice has the same issue, but the answer has us storing the binding details in the web.config.

using (LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient client = new LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient())
{
    client.ClientCredentials.UserName.UserName = "xxxx";

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

    var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);

    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

    binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

    client.Endpoint.Binding = binding;

    var response = client.getAdvertisements(new LumesseSoapTest.FoAdvert.getAdvertisements());
}

Example request Lumesse expects, taken from the part of the previous developers home-rolled solution - works as expected:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.mrted.com/">
    <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" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:UsernameToken wsu:Id="UsernameToken-11">
                <wsse:Username>xxxx</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
                    xxxx
                </wsse:Password> 
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">5Xhsv3Yp2l1xGpL3pNYy6A==
                </wsse:Nonce>
                <wsu:Created>2012-06-22T09:07:26.631Z</wsu:Created>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <getAdvertisements xmlns="http://ws.mrted.com/">
            <firstResult>0</firstResult>
            <maxResults>0</maxResults>
        </getAdvertisements>
    </soapenv:Body>
</soapenv:Envelope>

Example request we are currently sending:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <s:Header>
        <ActivityId CorrelationId="d309ce44-ed91-4314-87ee-e3abee4f531e" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">dd9a8c26-e673-464d-87e4-5cb8b76989c3</ActivityId>
        <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <u:Timestamp u:Id="_0">
                <u:Created>2014-09-30T16:15:47.426Z</u:Created>
                <u:Expires>2014-09-30T16:20:47.426Z</u:Expires>
            </u:Timestamp>
            <o:UsernameToken u:Id="uuid-c3275c63-6d98-4ae3-a7a7-afe314d23d6c-3">
                <o:Username>xxxx</o:Username>
                <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxxx</o:Password>
            </o:UsernameToken>
        </o:Security>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <getAdvertisements xmlns="http://ws.mrted.com/">
            <firstResult>0</firstResult>
            <maxResults>0</maxResults>
        </getAdvertisements>
    </s:Body>
</s:Envelope>

Any help will be greatly appreciated.

Update

Right, using this hacked together XML, I can get a response from the API.

I just don't have a clue how to generate this from our request. Again any help will be greatly appreciated.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <o:UsernameToken u:Id="UsernameToken-11">
        <o:Username>xxx</o:Username>
        <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxx
        </o:Password>
        <o:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
          5Xhsv3Yp2l1xGpL3pNYy6A==
        </o:Nonce>
        <o:Created>2012-06-22T09:07:26.631Z</o:Created>
      </o:UsernameToken>
    </o:Security>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <getAdvertisements xmlns="http://ws.mrted.com/"><firstResult>0</firstResult><maxResults>10</maxResults>
    </getAdvertisements>
   </s:Body>
</s:Envelope>

Update 2 (got it working!)

Finally, after about 9 hours we've got somewhere. I'll leave this here in for anybody else who has the utter misfortune of working with Lumesse (or another similar Java web service).

The main issue with the XML we are sending above is the Timestamp node under the Security node. The extraneous Nonce node it'll handle, presumably because it's not the first node under the Security node? Who knows (this is actually my first experience with SOAP / WCF in any form haha!).

So, the Timestamp node needs to go. If you need to use a standard binding like basicHttpBinding or wsHttpBinding, you'll need to create a custom binding. Here's an example that mimics basicHttpBinding, apparently, taken from http://www.mikeobrien.net/blog/removing-wss-timestamp-from-wcf/

Example config:

<customBinding>
  <binding name="MyBinding">
    <security authenticationMode="UserNameOverTransport" includeTimestamp="false" />
    <textMessageEncoding messageVersion="Soap11" />
    <httpsTransport maxReceivedMessageSize="26214400" />
  </binding>
</customBinding>

Then just call the service as would, passing in the credentials (you can probably stores these in the web.config alongside the above, but I'm currently damned if I know how).

using (LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient client = new LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient())
{
  client.ClientCredentials.UserName.UserName = "xxxx";

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

  foreach (var ad in response.advertisementResult.advertisements)
  {
    @ad.jobTitle <br />
  }

}

Thanks again.


Solution

  • I too am working with the same api and had the same issue. If you scroll down to the bottom of this article: http://www.hanselman.com/blog/BreakingAllTheRulesWithWCF.aspx Scott Hanselman removes the time-stamp through code rather than through the config.