WP7 and Self-Signed SSL Certificates
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
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!
What are self-signed certificates and how does SSL validation work?
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:
- The host name must be resolvable by the http agent via DNS, WINS, hosts file, etc.
- The SSL certificate must be known by a name that matches the host name.
- The trusted root certificate must be installed with the http agent, i.e. on the phone.
So what works on the phone?
WP7 ships with specific trusted root certificates installed which successfully validate certificates issued by the certificate authorities listed here:
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:
Installing an SSL certificate on the web server that the phone is able to validate. This means purchasing / creating an SSL certificate from one of the phone’s trust root certificate authorities and setting the web’s bindings to use the certificate.
Installing the self-signed trusted root certificate into the phone so that the phone is able to validate the SSL certificate. This installation is done by either emailing the trusted root certificate to the phone or by using the phone’s browser to download it. The phone recognizes certain file extensions and launches a certificate installation wizard.
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.