I have a WCF SOAP service that responds with licensing information. The client will save the SOAP response and load it every time the program loads, verifying the user is not passed the expiration date etc. Because of this, one of the requirements of the response is to have a signature such that the client can run the data through some encryption algorithm and check the result against the signature that was sent over to verify nothing has been changed about the file. This is nothing new, this is XML signing. However, the service is written with DataContractSerializer, so I can't just take the data, create an XML signature, and inject that straight into the SOAP response.
I know WsHttpBinding has some security features, the WS-Security page on MSDN describes the Ws binding protocol WRT to SOAP as having the ability to...
Identify the entity or entities involved with the message.
Prove that the entities have the correct group memberships.
Prove that the entities have the correct set of access rights.
Prove that the message has not changed.
but I can't find exactly how it does that last part. Looking at the SOAP response I get with WsHttpBinding on, I see CipherData and CipherValue, but researching that leads me to believe that's more to do with the actual message encryption, not content validation. I see something like ValidateResponse and ValidateResult, but those look like spaces for another endpoint to validate the information, and this product needs to work on devices not connected to the internet once the file is gotten from this service.
I know I could theoretically just put all the data into a variable and SHA256 it and tell my client to do the same process but that's dirty and very unstandardized. I feel like there should be an equivalent to XML Signatures for SOAP responses but I can't find anything through searching.
wsHttpBinding supports WS-Security, which includes digital signature in the SOAP message. To enable it, you need to use the ServiceContractAttribute.ProtectionLevel or the OperationContractAttribute.ProtectionLevel on the service contract definition, rather than doing it in the service configuration like you would expect
So, on your service contract:
[ServiceContract(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
public interface IMyServiceThatIWantToEncyptAndSign
{
...
}
or
[ServiceContract(ProtectionLevel=ProtectionLevel.Sign)]
public interface IMyServiceThatIWantToSign
{
...
}
or
[OperationContract(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
string MyOperationThatIWantToEncryptAndSignSign(string msg);
or
[OperationContract(ProtectionLevel=ProtectionLevel.Sign)]
string MyOperationThatIWantToSign(string msg);
The default value for this is ProtectionLevel.None which is why I think you are not seeing any signature.
The relevant MSDN links are here for the service contract:
http://msdn.microsoft.com/en-us/library/system.servicemodel.servicecontractattribute.aspx
and here for the operation contract:
http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontractattribute.aspx
Related
I have a WSDL definition for a SOAP service and I have successfully generated *.cs file from it using SvcUtil.
Implementing client is quite straightforward - I just need to call the necessary functions from the generated *.cs and that's it.
Implementing server seems more complicated. As I understand I need to implement an interface from the generated *.cs and then use some magic to turn it into the web server.
But I don't need a new web server, I already have a web server written in C# which already has many functionality unrelated to the SOAP service that I need to implement. I don't want to create another web server but I want my SOAP service to be just a part of my existing application (server), that is my server can answer e.g. requests http://example.com/request1, http://example.com/request2 etc. and I want this SOAP service to be just http://example.com/request3.
Since HTTP is already handled by my server I don't need .NET to handle it for me, basically my server can accept client connections and call the necessary handler based on the URL. I have a handler for SOAP request which looks approximately like this:
MyResponse HandleSOAPRequest(MyRequest request)
{
// 1. parse soap message from request.body
// 2. process it
// 3. generate response, serialize it in SOAP format and return it
}
The question is - can I rely on WSDL definition and .NET libraries to do it?
Currently I'm parsing SOAP request using XDocument and manually extract fields from it and serialize using simple string concatenation. Using .NET built-in functions to serialize or parse XML doesn't work. That is if I try to serialize response from an object of the class defined in the generated *.cs file then produced XML is different from what is expected by the protocol, similarly, if I try to parse request as an object of the class defined in the generated *.cs file I get error because XML parser expects different format. This applies to both the SoapFormatter and XmlSerializer.
Since .NET can implement client this means that everything that is necessary to parse and serialize SOAP messages is already implemented, I just need to figure out a way how to use this functionality.
The documentation for ServiceModel wasn't very helpful.
The easiest way would be to start the service via the ServiceHost:
ServiceHost host = new ServiceHost(typeof(YourService));
host.Open();
(I assumed here the configuration will come from the app.config and will not be setup in code like in the linked example below.)
How to: Expose a Contract to SOAP and Web Clients
The only drawback of this is that the application has to run with admin rights or otherwise a weird cofiguration is necessary.
i am new in WCF. once i was searching code for uploading file to web server by wcf. i found a code which uses message contract for file details. i just do not understand why people use message contract. according to that situation the coder can use data contract only instead of message contract....so why they used message contract is not clear.
here is the link of source code for file upload by wcf. just have a look and tell me can we use data contract for file information instead of message contract. give some few good situation when one has to use message contract instead of data contract. thanks
These are the link
http://www.codeproject.com/Articles/166763/WCF-Streaming-Upload-Download-Files-Over-HTTP
http://kjellsj.blogspot.in/2007/02/wcf-streaming-upload-files-over-http.html
http://stefanoricciardi.com/2009/08/28/file-transfer-with-wcp/
The main difference between Data contract and message contract are:
DataContracts are used to descibe the data types used by a service. This enables the types to be described in metadata to enable clients to interop with the service. DataContracts can be used to describe either parameters or return values. DataContracts are unnecessary if the service only uses simple types.
MessageContracts are used to explicitly describe the soap message format. It can be used to declare which headers various message elements go.
On Some critical issue, developer will also require control over the SOAP message format. In that case WCF provides Message Contract to customize the message as per requirement.
Very good link is here:
http://izlooite.blogspot.co.il/2010/01/wcf-why-use-messagecontract-when.html
Can i use datacontracts in WCF for streaming
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.)
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.
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.
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.
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);
}
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.
I've generated a WCF proxy from a WSDL file, but now when I call the proxy methods, they return null. I've enabled message logging, and can see that the messages from the server are correctly returned.
I've checked the answer of this question, but in my case at least the name of the returned object was the same in the message and in the WSDL. I still believe the problem has to do with the WSDL file, since it is not fetched the usual way through the "?wsdl" URL (it is a 3rd party webservice), but was given separately.
The return type of the method is just a string.
Has anyone else had similar problems, and what was the corresponding solution, if any? What is the most likely source of the problem?
Re-edit:
It is a RPC/Encoded web service. As written, I can see the SOAP response through message logging, but WCF seems not to be able to parse the information.
The message part of the response from the service looks like this:
<ns1:ServiceResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="the target namespace">
<ns1:ReturnValue xsi:type="xsd:string">
However, when inspecting the outgoing message from my client, it's different:
<ns1:ServiceRequest soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="the target namespace">
<RequestValue xsi:type="xsd:string" xmlns="">
So maybe the proxy expects the response to have the same namespace structure, and thus fails to parse it.
I've tried to change the type attribute to element in the wsdl message definitions, and adding some new elements in the types part of the wsdl definition, but then the svcutil chokes when generating the proxy, complaining that there's a clash between the inferred style document and specified style rpc.
From the WSDL specification, section 3.5:
If use is encoded, then each message part references an abstract type using the type attribute.
But then I'm a bit confused, since it doesn't seem to have been a problem in this question. What would be needed to make a similar change, with the restriction that it is a RPC/encoded service?
You'll have to give specifics about the Java service in order to resolve this. However, I suspect that the Java service is using message parts defined with the type attribute. These do not conform to WS-I Basic Profile 1 because there is ambiguity about which namespace should be used for the elements of the message. Some services will use the namespace of the type, while others will (correctly) use the namespace of the web service itself.
Using the element attribute removes the ambiguity, and is therefore preferred.
Please post a snippet of the WSDL containing one of the messages you're having trouble with. When you then compare the definition of the message with what you're seeing on the wire, and then compare that to the details of the proxy class that's meant to consume the message, I believe you'll see what I mean. The proxy class is expecting one namespace, but on the wire, a different namespace is being used.
We have had something similar when using a WCF client against a WSDL from a Java web service.
Our problem was that we could not see the data that was coming back from the service, it looked like the data was missing.
However, when we looked at what was going over the wire, the data was there.
The problem was that the WSDL had many types that inherited from other types. By default we would only see the information in the base type.
The solution was to cast the object to the type we expected, then all the fields appeared.
So I'm using the PayPal API. They require bigger companies to send an X509Certificate along with each SOAP API request. I've never heard of using a cert, it's always been just send the API signature along with an API request.
So I first created a class called Cerficate that implements the .NET ICerfiticatePolicy. One of the member methods, really the only one you have to implement is:
System.Net.ICertificatePolicy.CheckValidationResult(System.Net.ServicePoint, System.Security.Cryptography.X509Certificates.X509Certificate, System.Net.WebRequest, int)
So far I'm having trouble really understanding what to pass to this method. I guess the method simply validates that the Cerfiticate is valid. So I'm not sure what ServicePoint is and what to pass into it. I assumed it was my web service reference and a proxy class within such as the PayPalAPIAAInterfaceClient
I also see a very old example using ServicePointManager.S for something but I don't understand it, even after looking at MSDN. So I guess you are to use ServicePointManager.ServerCertificateValidationCallback and I suppose set the callback to the CheckValidationResult? If so, when do you do this? It's just very confusing to me.
Also, So I guess I create an instance of my Certificate class and set the certificate properties by reading the P12 certificate from my disk and then pass in that to this method to check if it's valid? I guess that's right.
I'm still trying to figure out this whole thing and I'm really stuck on the ServicePoint as well as WebRequest because really I'm using a proxy class in PayPal which does the under the hood sending of the request. So I don't see how I can even pass in type WebRequest because I'm using a proxy method for that anyway. So what would I even pass for the WebRequest param? I'm using a SOAP API WSDL, not NVP here so I'm not for example creating an HttpWebRequest variable like you do with REST services in order to send the API request over Http.
so far here's what I've tried:
PayPalAPIAAInterfaceClient client = new PayPalAPIAAInterfaceClient();
Certificate x509Certificate = new Certificate();
ServicePointManager.ServerCertificateValidationCallback = x509Certificate.CheckValidationResult();
client.ClientCredentials.ClientCertificate.Certificate = x509Certificate;
the problem is, what do I pass in for the ServicePiont and the rest of the params for CheckValidationResult?? I don't even know if I'm calling this right.
It's certainly not unheard of and in fact fairly common to secure SOAP services with X.509 certificates using the WS-Security spec - in fact, we do this for all of our internal and external web services. All web service frameworks including WCF are specifically designed to make this as easy as possible.
You should never have to use the ServicePointManager or ICertificatePolicy with a SOAP service using WS-Security. Unless there's something truly bizarre about PayPal's API, I think you're on the wrong track with that. All you have to do in WCF is this:
var client = new PayPalAPIInterfaceClient();
X509Certificate2 certificate = (...);
client.ClientCredentials.ClientCertificate.Certificate = certificate;
client.AddressVerify(...); // or whatever method you want to call
You don't even really need to write this code; if you have the certificate installed in the server's certificate store then you just edit the binding and behavior elements of the app.config - or use the WCF Service Configuration Editor, which is a lot easier.
Of course, in order to do this you have to have an X.509 certificate, and PayPal has to know about it. You can't just write new X509Certificate2(). You need to have a .pfx or .p12 file somewhere or, as mentioned above, have the certificate physically installed (this is the easiest way and the most secure because you're not hard-coding a password). And you need to upload the public key to PayPal.
You might be able to use OpenSSL to create a cert. PayPal's EWP page suggests that they'll accept these and gives instructions on how to create them, although it's not entirely clear whether or not the same process can be used for their SOAP API. It could be that they require a "real" certificate from Verisign, Thawte, etc. - I would try OpenSSL first and see, or just ask them.
There's a pretty comprehensive guide to the whole process here - you'll probably want to skip the sections on generating the certificate unless you have a Microsoft CA somewhere. Again, for that part, you'll probably want to try using the OpenSSL utility instead and follow PayPal's instructions, then install the cert on your server and skip to step 7 of that guide.