WCF configuration for SOAP plain text password authentication over SSL

10,630

Solution 1

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.

Solution 2

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!

Share:
10,630
Eric J.
Author by

Eric J.

Eric Johannsen VP of Engineering at GrowFlow Email: my first name at my last name dot us Science Fiction author The Gods We Make The Gods We Seek Ji-min Co-author C# 8.0 in a Nutshell

Updated on June 27, 2022

Comments

  • Eric J.
    Eric J. almost 2 years

    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.