Rune Jacobsen
Rune Jacobsen

Reputation: 10081

Can WCF access a SOAP 1.1 service using https?

I have a SOAP service I need to talk to from a C# application. I have a PHP test application that is using the same SOAP service today, using the standard SoapClient.

It is used similar to this:

$options = array(
    'login' => $username,
    'password' => $password,
    'location' => "https://$serveruri/soap",
);

$service = new SOAPClient($wsdl, $options);

$retval = $service->SomeMethod($parameter);

And this works just fine. As far as I understand (I am useless at PHP), since we're not setting an authentication option, it should go with Basic authentication.

I am trying to talk to the same endpoint in C#, and it keeps prompting me for authentication. I am not sure how to recreate the same authentication. I believe some of the problems come from using https - I have been able to talk to a similar system in the past using http and the BasicHttpBinding, but since I have to talk to this one across https, that is no good.

So I have generated a client proxy using SvcUtil, and I am trying to talk to it. Here is my current iteration of desperation:

var endpoint = new EndpointAddress(SoapUrl);

var binding = new WSHttpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.SendTimeout = new TimeSpan(0, 0, 10);

ServicePointManager.ServerCertificateValidationCallback +=
                (sender, cert, chain, sslPolicyErrors) => true;

var client = new soapServiceClient(binding, endpoint);

if (client.ClientCredentials != null)
{
   client.ClientCredentials.UserName.UserName = Settings.AuthenticationUsername;
   client.ClientCredentials.UserName.Password = Settings.AuthenticationPassword;
}

var retval = client.SomeMethod(parameter);

And to address Jons comment below: client.ClientCredentials really is not null, ever, when I run this. ReSharper put that there to stop itself from nagging.

When I run this, I get a 401 Unauthorized from the server. I get the following in Visual Studio 2013:

Additional information: The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="bla bla service"'.

How can I get WCF to behave like PHPs SOAPClient in this case?

Is there another binding I should use? I have tried different SecurityMode values, I have tried Digest authentication, but they get me nowhere. Since this happens over https I guess I can't Wireshark it either..

Thankful for any insights!

EDIT: Jon suggested to give Fiddler2 a try. I turned on https decryption and gave it a go.

Two things jump out at me:

1) When I run the PHP script locally (using EasyPHP running on my machine), it contacts the SOAP server and gets data. However, Fiddler2 does not see this traffic in any way.

2) When I run my app in Visual Studio 2013, Fiddler2 does see the traffic, and it decrypts it. I see two attempts to contact the SOAP endpoint; Both get a 401 reply. The first contains no auth information (looking at the Inspectors -> Auth part of Fiddler2) and just says:

No Proxy-Authorization Header is present.

No Authorization Header is present.

Then the second request tries to fix that with an Authorization header that looks kinda like this:

Authorization Header is present: Basic [some hash data] Decoded Username:Password= [correct username]:[correct password]

But as mentioned - this still provokes a 401 from the other side.

I have no idea why the PHP traffic doesn't show up in Fiddler2, but it very clearly receives live data from the other side. Perhaps PHP doesn't use the network stack in a way that Fiddler2 can pick up, I have no idea.

EDIT 2: After a bunch of filthy debugging on the PHP side, I finally got to compare the request/response cycle of the PHP app with the WCF one. That got me a lot closer (somehow I had gotten a sub character into the auth username, which caused the authentication issue), but now I am struggling with a ProtocolException, caused by my binding being set to Content-Type application/soap+xml, and the external service (I believe this to be Linux based) returning text/xml.

My understanding from abusing Google on this is that text/xml is common for SOAP 1.1, while application/soap+xml is what is used with SOAP 1.2.

Also, I understand that in WCF BasicHttpBinding supports SOAP 1.1, while WsHttpBinding supports SOAP 1.2.

Since this service is on the public internet, it requires https for security. I believe that the original SOAP service on the system itself uses HTTP, but it is behind a gateway that requires HTTPS and then passes it on as HTTP to the actual box serving the requests.

The core of the question then becomes: Is there a way for me to access a SOAP 1.1 service using HTTPS with WCF?

Upvotes: 2

Views: 3210

Answers (1)

Rune Jacobsen
Rune Jacobsen

Reputation: 10081

So I've come full circle on this. I started out this morning as a WCF newbie, and while I won't even claim to be a competent user yet, I understand a lot more about the bindings.

By using shotgun debugging I had actually done it correctly a few times, but I was tricked by an invisible sub character (^Z I believe) in the username I copied into my code.

Also, this was compounded by me seeing a lot of information about BasicHttpBinding not supporting https - it does, if you create it like this:

var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);

Credit to this relatively old article for educating me to the point of figuring it out:

https://msdn.microsoft.com/en-us/magazine/cc163394.aspx

From this article I also came to the understanding that had BasicHttpBinding not supported https, I could have built my own CustomBinding that worked the same with relatively little effort.

Also thanks to our overlord Jon Skeet for pushing me in the right direction of figuring out what was actually being sent across the wire!

Upvotes: 3

Related Questions