Silverlight 4 OOB + Browser HTTP Stack + Client Certificates = FAIL? - silverlight

I'm having an issue where IIS 7.5 (on Windows 7 64-bit) is failing when I call it from an out-of-browser Silverlight 4 app using SSL and a client certificate, with the message "The I/O operation has been aborted because of either a thread exit or an application request. (0x800703e3)". The request does make it to IIS. here is a sample from the failed request trace:
The I/O operation has been aborted because of either a thread exit or an application request. (0x800703e3) http://www.slipjig.org/IISError.gif
I am using the browser HTTP stack, because the client HTTP stack does not support client certificates. The client code attempting to hit the server is the Prism module loader. If I run the app out-of-browser but ignore client certs, or if I run the application in-browser but require client certs, it works fine. It seems to be the combination of the two that is causing the problem.
I tried the following to gather more info:
Used Fiddler to view the failing request. It works if Fiddler is running (presumably because Fiddler is handling the client certificate differently?);
Created an .aspx web form to serve up the module .xaps;
Created an HTTPModule to see if I could intercept the request before it failed;
Used a packet sniffer to see if I could tell if the client certificate was being sent correctly.
None of the above gave me much useful information beyond what I could see in the trace file, although the Fiddler thing is interesting.
Any ideas? Thanks in advance!
Mike

I beat my head against the wall for weeks on this problem. Here's what I learned and how I finally worked around it.
Prism's FileDownloader class uses System.Net.WebClient to load modules. In OOB mode, WebClient seems to use the same stack as IE, but it apparently either doesn't send the client certificate, or (more likely) doesn't correctly negotiate the SSL/client cert handshake with the server. I say this because:
I was able to successfully request .xap files using Firefox and Chrome;
I was not able to successfully request .xap files using IE;
IIS would fail with a 500, not a 403.
I couldn't get good visibility into what was actually happening over the wire; if I used Fiddler, it would work, because Fiddler intercepts communications with the server and handles the client certificate handshake itself. And trying to use a packet sniffer obviously wouldn't tell me anything because of SSL.
So - I first spent a lot of time on the server side trying to eliminate things (unneeded handlers, modules, features, etc.) that might be causing the problem.
When that didn't work, I tried modifying the Prism source code to use the browser's HTTP stack instead of WebClient. To do this, I created a new class similar in design to FileDownloader, implementing IFileDownloader, that used the browser stack. I then made some changes to XapModuleTypeLoader (which instantiates the downloader) to make it use the new class. This approach failed with the same error I was originally experiencing.
Then I started researching whether a commercial third-party HTTP stack might be available. I found one that supported the features I needed and that supported the Silverlight 4 runtime. I created another implementation of IFileDownloader that used that stack, and BOOM - it worked.
The good news with this approach is that not only can I use this to load modules, I can also use it to protect communications between the client and our REST API (a benefit we were going to give up, before).
I plan to submit a patch to Prism to allow the downloader to be registered or bound externally, as it's currently hard-coded to use its own FileDownloader. If anyone is interested in that or in the commercial HTTP stack I'm using, contact me (msimpson -at- abelsolutions -dot- com) for links and code samples.
And I must say this - I still don't know for sure whether the root problem is in the HTTP stack on the client side or the server side, but it's a FAIL on Microsoft's part nonetheless.

What we (Slipjig and I) found out this week is that there does appear to be a way around these issues, or at least, we're on the trail to determining whether there is a reliable, repeatable way. We're still not positive on that, but here's what we know so far:
At first pass, if you have code like this you can start making requests with either the Browser or Client stack:
First, place a "WebBrowser" control in your Silverlight XAML, and make it send a request to your HTTPS site.
This may pop up the certificate dialog box for the user. Big deal. Accept it. If you have only one cert, then you can turn an option in IE off to suppress that message.
private void Command_Click(object sender, RoutedEventArgs e) {
// This does not pop up the cert dialog if the option to take the first is turned on in IE settings:
BrowserInstance.Navigate(new Uri("https://www.SiteThatRequiresClientCertificates.com/"));
}
Then, in a separate handler invoke by the user, create an instance of your stack, either Client or Browser:
private void CallServer_Click(object sender, RoutedEventArgs e) {
// Works with BrowserHttp factory also:
var req = WebRequestCreator.ClientHttp.Create(new Uri("https://www.SiteThatRequiresClientCertificates.com/"));
req.Method = "GET";
req.BeginGetResponse(new AsyncCallback(Callback), req);
}
Finally, the Callback:
private void Callback(IAsyncResult result)
{
var req = result.AsyncState as System.Net.WebRequest;
var resp = req.EndGetResponse(result);
var content = string.Empty;
using (var reader = new StreamReader(resp.GetResponseStream())) {
content = reader.ReadToEnd();
}
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
Results.Text = content;
});
}

I had the same issue and I fixed it by creating the certificate using makecert. Follow the steps from this article http://www.codeproject.com/Articles/24027/SSL-with-Self-hosted-WCF-Service and replace CN with your ip/domain. In my case I have tested the service on the local machine and run the commands as follows:
1) makecert -sv SignRoot.pvk -cy authority -r signroot.cer -a sha1 -n "CN=Dev Certification Authority" -ss my -sr localmachine
after running the first command drag the certificate from "Personal" directory to "Trusted Root Certification Authority"
2) makecert -iv SignRoot.pvk -ic signroot.cer -cy end -pe -n
CN="localhost" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr
localmachine -sky exchange -sp
"Microsoft RSA SChannel Cryptographic Provider" -sy 12
In case you want to run the silverlight application on another machine, export the certificate created at step1 and then import it on any machine where you want your application to run.

Related

silverlight accept invalid certificate

I'm doing https web requests in silverlight using "WebRequest"/"WebResponse" framework classes.
Problem is: I do a request to an url like: https://12.34.56.78
I receive back a versign signed certificate which has as subject a domain name like: www.mydomain.com.
Hence this results in a remote certificate mismatch error.
First question: Can I somehow accept the invalid certificate, and get the WebBresponse content ? (even if it involves using other libraries, I'm open to it)
Additional details: (for those interested on why I need this scenario)
I'm trying to give a client access to a silverlight app deployed on a test server.
Client accesses the silverlight app at: www.mydomain.com/app
Then I do some rest requests to: https://xx.mydomain.com
Problem is I don't want to do requests on https://xx.mydomain.com, since that is on our productive server. For this reason I use https://12.34.56.78 instead of https://xx.mydomain.com.
Client has some firewalls/proxies and if I simply change his hosts file and map https://xx.mydomain.com to 12.34.56.78, web requests don't resolve to the mapped IP.
I say this because on his network webrequests fail if I try that, on my network I can use the hosts changing without problems.
UPDATE: Fixed the problem by deploying test releases to an alternative: https://yy.domain.com and allowing the user to configure for test purposes, the base url to which I do requests to be: https://yy.domain.com.
Using an certificate that contained the IP in the subject or an alternative subject would've probably worked too, but would have cost some money to be issued by a certified provider and would not be so good because IP's might change.
After doing more research looks like Microsoft won't add this feature too soon, unless there's a scenario for non-testing/debugging uses.
See: http://connect.microsoft.com/VisualStudio/feedback/details/368047/add-system-net-servicepointmanager-servercertificatevalidationcallback-property

How to accept a self-signed SSL certificate in a WCF client?

This may be a stupid question but I just can't find the answer.
What I would like to do:
I have a WCF service hosted by IIS. It is working perfectly, I can access the wsdl, I have a self-signed certificate for the server etc. I would like to call this service from a WPF client.
The problem is, since I have a self-signed certificate, I get the following exception when calling the service:
Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost'.
If I access the site (or the service) from a browser, it is no problem, because the browser warns me about the certificate, and gives me the choice of viewing the page anyway. But the WPF client just throws an exception.
I don't want to completely turn off the authentication process, I simply would like to give the users the option of ignoring this warning (as browsers do).
Can anyone provide some code for this? If you ran into a good, detailed tutorial about this, it would be awesome too. (See, my problem with the tutorials I've found is the lack of details)
Here's the minimum amount of code you need to make WCF client accept an arbitrary certificate. This is not secure. Use for testing only. Don't blame me if this code goes berserk and eats your little kitten.
ServicePointManager.ServerCertificateValidationCallback +=
new System.Net.Security.RemoteCertificateValidationCallback(EasyCertCheck);
The call back:
bool EasyCertCheck(object sender, X509Certificate cert,
X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
return true;
}
Code shamelessly lifted from the least helpful answer to Is it possible to force the WCF test client to accept a self-signed certificate?
You can register the certificate yourself. If load the certificate in the client as well, and then register the it as trusted you shouldn't get that warning.
You need to find a X509CertificateCollection and add the certificate to that collection. I had this kind of problem with a SmtpClient running over Ssl.
By hooking the System.Net.ServicePointManager.ServerCertificateValidationCallback or implementing System.Net.ICertificatePolicy and identify my own installed certificate as valid/trusted (attached to the System.Net.ServicePointManager.CertificatePolicy).
This is not WCF stuff per se, but from what I could tell, this should translate to WCF as well. It all depends what WCF is uses under the hood.

Making a WP7 HttWebRequest POST with an untrusted cert?

I'm working on a Windows Phone 7 application that makes a REST service call. The third party that hosts the web services has an invalid certificate in the current environment. When I hit the URL in Firefox, I get a warning about the cert and I am asked if I want to continue. I'm also using the Poster FF extension to test the call. It works with Poster if I first accept the invalid cert in Firefox. If I don't, then POSTER wont make the request.
In my WP7 Emulator, I can't make the request at all. I get a 404 at the EndGetResponse method. I making the same request as in Poster, so I know there is nothing wrong with the request. I have successfully hit another web service using the same code (no certs involved), so I don't think it's the code. The only thing I can think of is that WP7 doesn't allow requests to an invalid cert. Has anyone had experience with this situation? Is there any way around it?
Is there a way I can tell my app to accept all communication, even if there is an invalid cert?
There is sadly no way to do this on the phone. Ordinarily, i.e. on the desktop this simple line of code will disable certificate checking.
System.Net.ServicePointManager.ServerCertificateValidationCallback = (se, cert, chain, sslError) => { return true; };
If you look at the ServicePointManager on the phone, there's no callback to hook into. It's a massive pain in the arrrrse.
Have you considered writing to the service owner and asking why they're being bad internet citizens? (essentially, what you're seeing here is web security in action, for better or worse)
As Matt says, you might be able to code a simple relay on a web server. It doesn't have to be a special service, but maybe just a web page that does the call for you and spits out RAW text or XML. Your phone client just GETs this page and picks through the response manually.
Where there's a will there's a way.
Luke
You need to install the root CA cert of the issuing party on the phone.
You can do this by emailing the RootCA to the user of the phone. They click on the attachement and it will prompt them to ask if they want to install the certificate on the phone.
Once you have done that your requests should go through.
I dont believe there is a way to do this programatically in your app however.
I'm not aware of a way to install additional certificates on the phone.
In this situation I'd create a proxy service between your app and the 3rd party site and have your app call that. If you need to, you could put the proxy behind a valid cert.

Windows Phone 7 - Cookies not sent to WCF service

I have a bunch of WCF services at a domain:
AuthenticationService (the standard MS one, over HTTPS)
AppService (HTTP)
Usually, I call the Authentication Service and a cookie is returned. For desktop applications, I detach the cookie and reattach it with every new service call to the AppService which exposes the meat of my API.
Silverlight in the browser automatically attaches the cookie in all calls to the domain. I expected the phone to do the same.
It doesn't.
Access to the headers is not supported on the phone, so manual manipulation is out. I wonder if its because some bright spark at MS thought that the phone should enforce that cookies are only reattached to HTTPS endpoints at the same domain or something...
Help!!
This is a nightmare to troubleshoot since the phone doesn't support the other major major helpful setting; ignoring self-signed certificates.
Thanks,
Luke
** UPDATE **
While I'm following up the method using CookieContainer I must point out that even though the Add method on the Headers collection is missing in Silverlight, one can still add headers using the indexer.
See http://cisforcoder.wordpress.com/2010/12/01/how-to-implement-basic-http-authentication-in-wcf-on-windows-phone-7/
** UPDATE 2 **
CookieContainer can be set as per Lex answer. I'm now stuck and continuing to investigate an ArgumentNullException thrown from within the WCF client upon References.cs EndInvoke. My server shows no sign of receiving the call.
Two key calls on the stack are:
System.Net.Browser.HttpWebRequestHelper.ParseHeaders
And
MS.Internal.InternalWebRequest.OnDownloadFailed
FINAL UPDATE
The ArgumentNullException seems to be thrown when called against a server with a self-signed certificate.
However, there's something odd with the emulator/SDK. I have had this exception against all my servers, even those with no SSL, and one with an issued certificate.
I've also had problems that have been resolved only by a local reboot. So I think my problems have been a result of having the right code, but thinking it was wrong because of other problems in the SDK.
Not sure what advice to give, except to be mistrusting of exceptions stemming from the WP7 WCF stack, particularly EndpointNotFoundException and ArgumentNullException, and to have a full framework test client app around as a sanity check.
Luke
In desktop SL, I do it by sharing cookie container between service proxies in client http stack. Did not try this on WP7, but you may check this out.
First, you need to insert <httpCookieContainer/> in your client-side binding configuration (usually in ServiceReferences.ClientConfig).
In code, switch to client http stack.
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp);
Then you need to create a CookieContainer instance and assign it to all service proxies.
var cc = new CookieContainer();
var service1 = new ServiceReference1.MyService1Client { CookieContainer = cc };
var service2 = new ServiceReference2.MyService2Client { CookieContainer = cc };
Now, when your cookie container receives cookie, it will reuse it for all web services. Make sure, that cookie comes with correct Path setting.

Silverlight and SSL Client Certificates

Can anyone point me in the right direction of how I can use SSL client-side certificates with Silverlight to access a restful web service?
I can't seem to find anything on how to handle them, or even whether they are supported.
Cheers.
Slipjig mentioned this:
"The browser stack does, and pretty much automatically, if you're willing to live with its other limitations (lack of support for all HTTP verbs, coercion of response status codes, etc.)."
If that is acceptable to you, look at how Microsoft themselves deal with this in some of their APIs using the custom X-HTTP-Method header, like how they do it for WCF and OData:
http://www.odata.org/developers/protocols/operations
In MSDN, Microsoft also mentions this about using REST in conjunction with SharePoint 2010's WCF based REST API:
msdn.microsoft.com/en-us/library/ff798339.aspx
"In practice, many firewalls and other network intermediaries block HTTP verbs other than GET and POST. To work around this issue, WCF Data Services (and the OData standard) support a technique known as "verb tunneling." In this technique, PUT, DELETE, and MERGE requests are submitted as a POST request, and an X-HTTP-Method header specifies the actual verb that the recipient should apply to the request. For more information, see X-HTTP-Method on MSDN and OData: Operations (the Method Tunneling through POST section) on the OData Web site."
Don Box's also had some words about this, but regarding GData specifically:
www.pluralsight-training.net/community/blogs/dbox/archive/2007/01/16/45725.aspx
"If I were building a GData client, I honestly wonder why I'd bother using DELETE and PUT methods at all given that X-HTTP-Method-Override is going to work in more cases/deployments."
There's an article about Silverlight and Java interop which also addresses this limitation of Silverlight by giving the same advice:
www.infoq.com/articles/silverlight-java-interop
"Silverlight supports only the GET and POST HTTP methods. Some firewalls restrict the use of PUT and DELETE HTTP methods.
It is important to point out that true RESTful service can be created (conforming to all the REST principles listed above) only using the GET and POST HTTP methods, in other words the REST architecture does not require a specific mapping to HTTP. Google’s GData X-Http-Method-Override header is an example of this approach.
The following HTTP methods overrides may be set in the header to accomplish the PUT and DELETE actions if the web services interpret the X-HTTP-Method-Override header on a POST:
* X-HTTP-Method-Override: PUT
* X-HTTP-Method-Override: DELETE"
Hope this helps
-Josh
It depends on whether you're using the browser HTTP stack or the client HTTP stack. The client stack does not support client certificates, period. The browser stack does, and pretty much automatically, if you're willing to live with its other limitations (lack of support for all HTTP verbs, coercion of response status codes, etc.).
I have however been running into a problem using the browser stack with client certificates in an OOB scenario. Prism module loading fails under these conditions - the request gets to IIS, but causes a 500 server error for no apparent reason. If I set IIS to ignore client certs, or if I run the app in-browser, it works fine :-/
take a look at this.
http://support.microsoft.com/kb/307267
just change your urls to https
hope this helps
Dim url As Uri = New Uri(Application.Current.Host.Source, "../WebService.asmx")
Dim binding As New System.ServiceModel.BasicHttpBinding
If url.Scheme = "https" Then
binding.Security.Mode = ServiceModel.BasicHttpSecurityMode.Transport
End If
binding.MaxBufferSize = 2147483647 'this value set to override a bug,
binding.MaxReceivedMessageSize = 2147483647 'this value set to override a bug,
Dim proxy As New ServiceReference1.WebServiceSoapClient(binding, New ServiceModel.EndpointAddress(url))
proxy.InnerChannel.OperationTimeout = New TimeSpan(0, 10, 0)

Resources