While developing a Windows Phone 7 app that calls a WS-Trust authenticated web service I ran into a general problem with the phone blocking SSL requests to a web server which uses a self-signed certificate, the kind typically used on dev and test servers. The result was the EndpointNotFoundException containing a WebException with NotFound shown below. I’ll explain the SSL request problem in detail then offer suggestions and code to make your development life easier.
System.ServiceModel.EndpointNotFoundException : There was no endpoint listening at https://localhost/testService/Service.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. InnerException System.Net.WebException : The remote server returned an error: NotFound. WebException.Status = System.Net.WebExceptionStatus.UnknownError
System.ServiceModel.EndpointNotFoundException : There was no endpoint listening at https://localhost/testService/Service.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
InnerException System.Net.WebException : The remote server returned an error: NotFound.
WebException.Status = System.Net.WebExceptionStatus.UnknownError
The WS-Trust communication going on behind the scenes masked the exception’s root cause and it took me two days to figure out what was wrong. Troubleshooting an exception with a message containing NotFound is especially difficult because WP7 is actually terminating the request during SSL negotiation and nothing is logged on the web server. To me the exception messages imply an incorrectly configured or missing service. I guess you just need to know that NotFound during an SSL request means that the SSL certificate is invalid and likely caused by the phone not having a corresponding trusted root certificate installed. If you figured it out in less than two days you’re doing better than me!
Usually a dev web server is configured to use a self-signed certificate called localhost for SSL bindings; one of ScottGu’s blogs covers IIS and self-signed certificates in detail. If the self-signed certificate is named localhost then the issuer is named localhost and the http agent (browser, WCF client, etc) must recognize localhost as a trusted root certification authority. Any failure of the SSL negotiation terminates the request and WP7 throws the exception with NotFound.
Additionally the http agent’s anti-phishing protection checks to make sure the SSL request is to a host name which matches the SSL certificate name. ScottGu shows this in his blog when he calls https://localhost but the certificate isn’t named localhost; his http agent, IE, displays a warning. The WP7’s WCF agent contains anti-phishing protection that throws the NotFound exception.
It is a little less common, but perfectly acceptable, for a dev web server to use a self-signed certificate named to match the server name, the server’s fully qualified domain name, or perhaps an environment name. Usually shared test servers are configured like this since the name localhost isn’t as useful in test scenarios.
The key points are:
WP7 ships with specific trusted root certificates installed which successfully validate certificates issued by the certificate authorities listed here:
http://msdn.microsoft.com/en-us/library/gg521150(v=VS.92).aspx
If you use the phone’s IE browser to make SSL requests the browser warns you that certificates issued by other certificate authorities are invalid. After choosing to continue the browser remembers the selection and makes subsequent SSL requests without displaying the warning.
The WP7 v1 framework lacks the ServicePointManager class with ServerCertificateValidationCallback for modifying the SSL validation via code so the developer has to get around the issue by either:
Remember that irrespective of the option you pick the certificate name must match the resolvable host name in the URI you’re using to call the server. In a future post I’ll offer advice on resolvable host names.
The “cost is no issue” crowd will just go with option 1 and buy unique SSL certificates for each dev, test, and production server. They’ll likely use the server name instead of localhost because I doubt a certificate authority will issue a localhost certificate.
I tend to avoid purchases to avoid expense paperwork. Luckily at Microsoft our developers have the ability to use a company hosted certificate authority which issues certificates chained to a root authority that WP7 trusts. You can implement your own certificate authority using Microsoft Certificate Services but you’ll still need a certificate from one of the phone’s certificate authorities to chain your certificate authority to. It looks like significant effort and policies are involved to implement Certificate Services correctly.
I didn’t test it but you might attempt to create a shared SSL certificate for dev and test with Subject Alternative Names (SAN) for each of your dev and test servers. The downside is the names are fixed after you create the certificate so you might need to make additional certificate purchases as your environments change.
Manually doing option 2 really isn’t a walk in the park either since the emulator starts from a default state each time it loads. None of the trusted root certificates you install are remembered, and neither are browser favorites, so you’ll frequently be clicking on the emulator’s keyboard to download the certificate. Don’t you hate manual steps? Plus if you’re like me you’ll forget and wonder why the program failed. That NotFound exception will key you in though.
I haven’t figured out how to uninstall a trusted root certificate from a real device so the good news is you only have to install it once on your real phone. The bad news is that your phone doesn’t know what localhost means so you’ll need to use certificate names that match host names resolvable via DNS. NetBIOS and WINS resolution of domain server names is not supported by the real device via Wi-Fi.
Maybe you’ll just forgo using SSL until you hit production. Some things, like WS-Trust, require it though.
In the next post I’ll introduce WP7CertInstaller; it eliminates the manual work of option 2 and makes option 2 easy enough that even I avoid trouble.
Hi David,
A very nice blog! :)
Did you find a way to do this via code?
Hi,
Yes, I just released the code. You can download it at wp7certinstaller.codeplex.com. I'm in the process of writing a blog about the code but until I'm done you should read the code comments for the TrustedRootCertificateInstaller class within the example project.
Thanks