views:

931

answers:

2

I have an application that connects via https to a SOAP-based web service that implements WS-Security. The web service is written in Java, expects a plain text password as well as a properly set timestamp.

After a great deal of googling and experimentation, I can't figure out how to configure my WCF client to interact with this service. In addition to a correct answer, I would also appreciate a link to a tutorial that explains WCF and SOAP well.

My current client's app.config looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="MyServiceSoapBinding" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                  <security mode="TransportWithMessageCredential">
                    <transport clientCredentialType="None" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="UserName" algorithmSuite="Default" />
                  </security>
                  <!--security mode="None">
                    <transport clientCredentialType="None" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="UserName" algorithmSuite="Default" />
                  </security-->
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://p1.my.com/tx/services/MyService"
                binding="basicHttpBinding" bindingConfiguration="MyServiceSoapBinding"
                contract="My.IMyService" name="MyServiceEndpointPort" />
        </client>
    </system.serviceModel>
</configuration>

and the client code looks like this:

string response;

try
{
    MyService.MyServiceClient svc = new WcfExample.MyService.MyServiceClient();

    svc.ClientCredentials.UserName.UserName = "myUser";
    svc.ClientCredentials.UserName.Password = "myPass";

    response = svc.ping();

    lblPingResponse.Text = response;
}
catch (System.ServiceModel.Security.MessageSecurityException mse)
{
    lblPingResponse.Text = "MessageSecurityException: " + mse.Message;
}
catch (Exception ex)
{
    lblPingResponse.Text = "Exception: " + ex.Message;
}

This code is throwing this exception:

MessageSecurityException "Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security."

The WSE 3 version simply requires the following to work:

ServiceUsernameTokenManager.AddUser(userName, password);

UsernameToken token = new UsernameToken(userName, password,
                PasswordOption.SendPlainText);

proxy = new _MyServiceWse();

Policy policy = new Policy();

policy.Assertions.Add(new UsernameOverTransportAssertion());
policy.Assertions.Add(new RequireActionHeaderAssertion());
proxy.SetPolicy(policy);

proxy.SetClientCredential(token);

UPDATE:

The request now reaches the server and a response is sent back from the server using this configuration in app.config:

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

The client then throws an Exception

"Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security."

This seems to be because the client sends a Timestamp header, but the service does not return a Timestamp header. This is probably "the right thing to do", but it's not very helpful because there are many web services deployed out there that expect a Timestamp but do not return one.

If there is a way to convince the client to accept this situation I would love to know about it. In the mean time, I'll look into whether I can have the web service changed to return a Timestamp.

+2  A: 

As for your concrete problem, have a look at a few similar questions on Stackoverflow and elsewhere:

Links to useful tutorials and screen casts explaining WCF in great detail: there's the MSDN WCF Developer Center which has everything from beginner's tutorials to articles and sample code.

Also, I would recommend you have a look at the Pluralsight screen casts on WCF - it's an excellent series going from "Creating your first WCF service" and "Creating your first WCF client" all the way to rather advanced topics. Aaron Skonnard very nicely explains everything in 10-15 minutes screencasts - highly recommended!

marc_s
@marc_s: Thanks for the response. However, the SO question appears to imply that you have to custom-craft the SOAP header by hand. Is that really the only solution? The Wordpress link discusses configuring the server, not the client. The Plurlasight videos require a subscription/payment and seem to cover general WCF, not the specific issue as far as I can see.
Eric J.
@Eric J: the Pluralsight screencasts definitely do not require any subscription - those are all totally free, and worth your time! As for SOAP headers - if you need to send custom SOAP headers, you can either use a Message Contract which defines the headers and the body elements of your messages, or you'll have to create some logic to inject those SOAP headers into the messages
marc_s
@marc_s: Looks like some of them are free for a while "We are offering a limited time guest pass that will allow you to access a few clips from each module in the Pluralsight On-Demand! library." I'll have a look. I also updated the question with additional findings. I do appreciate your help!
Eric J.
@Eric J: ah ok - well yes, there's the screen casts (which are free) but of course, Pluralsight also has their WCF On-Demand library which is for a fee - they might offer some bits as teaser :-) But the screencasts alone are extremely useful already
marc_s
A: 

Although I realise that you have already marked the answer from Mark_S as correct, you may find the following resolves your issue.

Your error message is addressed in Microsoft Hotfix KB971493: A hotfix that enables WCF to send secured messages and to receive unsecured responses, and to send unsecured messages and to receive secured responses, is available for the .NET Framework 3.5 SP1.

Windows Communication Foundation (WCF) does not have the functionality to send secured messages and then receive unsecured responses, or to send unsecured messages and receive secured responses. The hotfix that is described in this article adds a new enableUnsecuredResponse attribute.

I would be interested to know if this solves your problem.

Junto