6

This is how I have currently managed to consume a particular Microsoft web service. Notice that it is located on an HTTPS server and that it requires a username, a password, and a .cer file to be installed in the operating system's "root certificate authorities".

WSHttpBinding binding = new WSHttpBinding();

binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.NegotiateServiceCredential = true;
binding.Security.Message.AlgorithmSuite
  = System.ServiceModel.Security.SecurityAlgorithmSuite.Default;
binding.Security.Message.EstablishSecurityContext = true;

EndpointAddress endpoint = new EndpointAddress("https://address.of.service");

//"GreatClient" was created for me automatically by running
//"svcutil.exe https://address.of.service?wsdl"
GreatClient client = new GreatClient(binding, endpoint);

//Username and password for the authentication. Notice that I have also installed
//the required .cer certificate into the system's "root certificate authorities".
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";

//Now I can start using the client as I wish.

My question is this: How can I obtain all the information necessary so that I can consume the web service with a direct POST to https://address.of.service, and how do I actually perform the POST with C#? I only want to use POST, where I can supply raw XML data using POST directly to https://address.of.service and get back the result as raw XML data. The question is, what is that raw XML data and how exactly should I send it using POST?

(The purpose of this question: The reason I ask is that I wish to consume this service using something other than C# and .NET (such as Ruby, or Cocoa on Mac OS X). I have no way of knowing how on earth to do that, since I don't have any easy-to-use "svcutil.exe" on other platforms to generate the required code for me. This is why I figured that just being able to consume the service using regular POST would allow me to more easily to consume the service on other platforms.)

Enchilada
  • 3,859
  • 1
  • 36
  • 69
  • 1
    Have you tried a tool like Fiddler to inspect what is going back and forth? If so, did you try just pulling the raw outgoing XML and posting that directly to the server? – McGarnagle Apr 01 '12 at 00:50
  • I believe I have managed to find out the raw XML being sent and received from the service using the instructions here: http://thecoffman.com/2011/01/25/please-drop-the-soap/ However, the problem is that this is just the XML data. It doesn't contain any HTTPS addresses in it -- only plain HTTP ones -- and it doesn't have any username/password things. It therefore seems to me that the XML data being sent and received is a separate issue from actually connecting securely to the service. I'm not sure how to do that. – Enchilada Apr 01 '12 at 00:54
  • 1
    no worries, it still appeared in my inbox. As far as the article linked, did you use the code, or or did you notice that at the end he dropped all that and just went with Wireshark? If you go with the latter, then you're guaranteed to see everything (maybe too much) including the network-level SSL negotiations. – McGarnagle Apr 01 '12 at 01:06
  • @dbaseman I used his code only. I didn't notice his mention of Wireshark. I will try that soon and see whether that gives me some information. Thanks! – Enchilada Apr 01 '12 at 01:20
  • @dbaseman I checked the traffic with Wireshark. I see no packets transferred with HTTP, so I can see no header information. It's all just some TCP and TLSv1 traffic with encrypted SSL data that I can't read. (In other words, I see no nice header information such as the guy gets in this Wireshark tutorial at 01:55: http://wiresharkdownloads.riverbed.com/video/wireshark/introduction-to-wireshark/) So I'm not sure whether this approach will help... – Enchilada Apr 01 '12 at 01:53

5 Answers5

1

What you are attempting to do sounds painful to do now and painful to maintain going forwards if anything changes in the server. It's really re-inventing the wheel.

If you haven't considered it already, I would:

(a) Research whether you can use the metadata you have for the service and use a proxy generator native to your target plaform. There aren't many platforms that don't have at least some tooling that might get you part of the way if not all of it. Perhaps repost a question targetting Ruby folk asking what frameworks exist to consume an HTTPS service given it's WSDL?

(b) Failing that, if your scenario allows it I would consider using a proxy written in C# that acts as a facade for the service which translates it into something easier to consume (for example, you might use something like ASP.NET MVC WebAPI which is flexible and can easily serve up standards compliant responses over which you can maintain total control).

I suspect one of these may prove easier and more valuable than the road you are on at the moment.

James World
  • 29,019
  • 9
  • 86
  • 120
  • In (b), you mean creating and maintaining my own Windows Server, running the web service of my choice (with, say, a more easily consumable REST interface)? I guess that’s an option to consider, but it seems to be somewhat of a hassle... – Enchilada Apr 04 '12 at 22:09
  • 1
    That is what I mean. Obviously I don't know your situation, there are many contributing factors to this decision I am not privvy to, but I just wanted to throw it out there. Another obvious but probably impractical option is replacing the original service. One thing I do know is that hand crafting that request/response would be nasty! :) – James World Apr 05 '12 at 10:57
  • Well, it would be nasty but it could still a way out. It's just a different SOAP envelope (in XML format) for each function of the web service, that's all. And I happen to know those XMLs now (using the Wireshark tool suggested by dbaseman). All I need to know is how to POST that XML to the HTTPS address. I agree that it's nasty, but I'm hoping it would work. – Enchilada Apr 05 '12 at 15:02
  • I'll consider setting up my own server as a last resort, since setting up a web server costs financial resources. As for your suggestion (a), I have tried parsing this particular WSDL using wsdl2whatever tools from many platforms, including Cocoa, Ruby and PHP. All of these tools fail to parse this particular WSDL file. It must be some "Microsoft-only" quirk. So that's why I just gave up on using the WSDL document, and why I am now trying to send out the SOAP envelopes directly to the HTTPS address via POST. – Enchilada Apr 05 '12 at 15:05
  • 2
    I expect the other platform proxy-generaters are tripping up on the policy describing the authentication - believe it or not there are very few platforms as comprehensive as WCF when it comes to implementation of authentication standards. These protocols are incredibly hard to implement by hand - I would really avoid it if I were you. You'll probably pay more to develop and maintain a bad solution than the cost of hosting a proxy server for a few years! This solution may irk the purist in you, but it is probably the most pragmatic. – James World Apr 10 '12 at 07:38
1

I think when you say that the service should be consumed from other platforms, which do not have proxy class generation logic, you can go with REST services. This will allow you to create input as simple string concatenation instead of complex XML. Though its applicability depends on the situation.

Check this discussion : http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/6907d765-7d4c-48e8-9e29-3ac5b4b9c405/

As far as the certificate is concerned, refer http://msdn.microsoft.com/en-us/library/ms733791.aspx on how to configure it.

I know this is not a very precise answer, but you will be the best person to evaluate above procedure, hence posted. Hope it helps.

AYK
  • 3,312
  • 1
  • 17
  • 30
  • I’m not in control of this particular web service, so I can’t change it. But yeah, you could be right. As James World mentioned, one option would indeed be to create my own “middle server” which implements RESTS. If I end up going that route, your links could end up being useful. – Enchilada Apr 09 '12 at 14:40
1

What I'll do:

1- Create a small c# app that can post on this webservice (using svcutil). And modify it to show the XML send/received. To view the xml there are several ways: logging, wireshark etc. To add it directly to the small app there is another question here that give a good answer.

2- Once you know what you have to send, you can do it in c# like this:

// implement GetXmlString() to return the XML to post
string xml = GetXmlString();


// create the url
string url = new UriBuilder("http","address.of.service",80).ToString();     


// create a client object
using(System.Net.WebClient client = new System.Net.WebClient()) {
    // performs an HTTP POST
    client.UploadString(url, xml);  

}
Community
  • 1
  • 1
Fabske
  • 2,106
  • 18
  • 33
  • But how to deal with the HTTPS authentication with a username and password (and the .cer certificate)? – Enchilada Apr 10 '12 at 01:21
  • Also, I have no idea what header information I should send along and how I should do it. – Enchilada Apr 10 '12 at 02:30
  • 1
    To send data you'll need: (1) To add username & password in the header (2) To sign the header with the certificate (3) maybe: to encrypt the header with the certificate I'm afraid it'll be very difficult to send the header if the service require the header to be signed (and encrypted?) with the certificate. So it depends on how your web service is configured (web.config). I suppose it shouldn't be encrypted using the certificate as it's over https, so the WebClient in my code above will handle this. – Fabske Apr 10 '12 at 07:31
  • 1
    Did you check these other questions ? (1) http://stackoverflow.com/questions/4666970/signing-soap-messages-using-x-509-certificate-from-wcf-service-to-java-webservic (2) http://stackoverflow.com/questions/2292284/how-to-sign-a-soap-request-with-wcf – Fabske Apr 10 '12 at 07:34
1

I'm not a .NET programmer but I've had to interoperate with a few .NET services and have lots of SOAP/WSDL experience. Sounds like you've captured the XML for your service. The other problem you'll face is authentication. OOTB, .NET web services use NTLM for authentication. Open-source language support for NTLMv2 can be hit and miss (although a quick google search pulled up a few possibilities for ruby), and using NTLM auth over HTTP may be something that you have to wire together yourself. To answer a question above: where are the auth creds? If the service is using NTLM over the wire, authentication is happening at some layer below HTTP. If the service is using NTLM to authenticate HTTP, your NTLM creds are in the HTTP Authorization header. You should be able to tell with wireshark where they are. You'll also probably need a SOAPAction header; this can also be sniffed with wireshark. For the C# client, I'm sure there are docs explaining how to add headers to your request.

Tim Jones
  • 985
  • 8
  • 14
1

I had to go through something similar when porting .NET WCF code to other platforms. The easiest approach I found was to enable message logging on the WCF client. This can be configured to save both envelope and body and once everything is working on the .NET side of the house, you can use the message log to have "known-good" XML request/response to port to other platforms.

I found this approach to be more elegant since I didn't have to add an additional behavior to log messages, and it can be easily enabled/disabled/tweaked in the config. The Service Trace Viewer Tool that ships with Visual Studio is also handy for reviewing the log files.

jwmiller5
  • 2,584
  • 1
  • 15
  • 34
  • Did you also have to POST to the service, and was your service also on an HTTPS address? – Enchilada Apr 01 '12 at 13:52
  • 1
    Yes and yes, the message logging accounts for this, so you don't have to worry about decrypting the SSL, and it logs both request and response so you will have the XML that you POST to the service with the WCF proxy class. That is the XML you will want to recreate in Ruby/Cocoa/other – jwmiller5 Apr 01 '12 at 14:05
  • Okay, the log gets written out, and I do see the XML that is being sent. However, I still don't know how to actually POST the thing to the service. In fact, there is no hint of any HTTPS addresses, username, nor password in any of the information provided by the log. Do you still have your project around? Could you elaborate on how you POSTed the data to the HTTPS address? – Enchilada Apr 01 '12 at 18:03