2

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.

Community
  • 1
  • 1
  • I don't see the security information in the WSDL, are you sure that's the correct URL? WSDL should have a or (for ws-*). Also, in your home-rolled example you have the closing getAdvertisements tag defined as >, is the second closing bracket a typo? Also your second example has an AcctivityId node and your home-rolled example does not. –  Sep 30 '14 at 17:29
  • It certainly is - stupid question - I presume that this would be quite a big issue? Updated the home-rolled example request XML. I've just tried pushing our SOAP XML manually to Lumesse without the ActivityId node, doesn't affect the results ('wsse:FailedCheck') - though it might later on, of course! – milquetoastable Sep 30 '14 at 17:35
  • Update - just tried entering the Nonce and Created nodes into the UsernameToken node as well. No joy, same error. – milquetoastable Sep 30 '14 at 17:42
  • 1
    The missing Nonce element will indeed cause the issue, I missed it at first. Unfortunately, adding the Nonce to that isn't as simple as changing a setting. You'll need to use a custom binding and likely have to manually build the soap packet. At least that is the only way I know of. You can find more information here http://stackoverflow.com/questions/896901/wcf-adding-nonce-to-usernametoken and here http://weblog.west-wind.com/posts/2012/Nov/24/WCF-WSSecurity-and-WSE-Nonce-Authentication. Hopefully that will solve your issue. –  Sep 30 '14 at 18:14
  • Thanks matey. Crazily enough, I've removed both the Nonce and Created nodes from the Security node from the hacked-together XML and it actually still works. This web service is bonkers. – milquetoastable Sep 30 '14 at 18:20
  • 1
    Yeah, it is odd... If you import the WSDL via add service reference, it sets it as basic binding which will never work with WSSE or WS-*. From what I've seen you'll need to manually create the packet which is unfortunate. Also, from your post I assume you know about SoapUI, if not, get it. Another good tool for these things is Fiddler. Although, Fiddler is more for HTTP requests, it can come in handy. –  Sep 30 '14 at 18:28
  • Yeah, that's the among the things we've been hacking around, as well as that, the endpoint it sets is completely wrong. I don't know if that's a VS issue since WSSE has been made obsolete (apparently only works with VS2005). SoapUI has been a whole other story! Spent a fair whack of time trying to sort peer validation errors. Fiddler's been a god-send so far, cheers again. – milquetoastable Sep 30 '14 at 18:32
  • Actually WSSE can be used in VS 2008/10/12. [You just need to install Web Service Enhancements (WSE) 3.0](http://www.microsoft.com/en-us/download/details.aspx?id=6545) WSE is not supported by default after VS 2008, but you can get it to work. [WSE 3.0 with VS 2010](http://digantakumar.com/2010/06/04/wse-3-in-visual-studio-2008-and-2010/). –  Sep 30 '14 at 18:47

1 Answers1

1

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.

Ben
  • 890
  • 4
  • 10