WP7CertInstaller 1.0.0.0 Explained

WP7CertInstaller is a CodePlex project which includes the source code of a website to download your trusted root certificates from, a WP7 example showing how you include the certificate check / install functionality in debug builds, an Azure project for testing with a real device, and a batch script with commands for creating self-signed certificates.  The website’s default.aspx page also allows you to email a link so that others can easily download and install the certificate too.

Trying the App in the Emulator

Be sure to read my post on the WP7CertInstaller prerequisites before giving the app a try.  Assuming you have the prerequisites you can open the solution, set WP7CertInstallerExample as the startup project, and then start debugging.  The following screens will appear during certificate installation.

image_thumb8 image_thumb11 image_thumb18

After the certificate is installed you are left on a blank web page; use the phone’s back button to launch the example app again as shown below.

image_thumb21image_thumb24

Now each time you launch the application you’ll see the final screen which is just the example app’s way of proving to you that SSL requests succeed.  If you want to see WP7CertInstaller in action again just close the emulator and start debugging again.  Closing the emulator resets everything.

Trying the App on the Phone

Theoretically if the example app could access your localhost site you’d see the same behavior on your phone.  Unfortunately a real phone isn’t able to resolve the localhost domain name.  The phone is only able to resolve via DNS; over the air that is your carrier’s DNS server; via WI-FI that is your network’s DNS server.

Dev and Test server names are normally resolved via WINS or NetBIOS so they don’t work either.  On Microsoft’s corporate network I’m also running into other issues with fully qualified domain names even when the name is resolvable.  The easiest way around all this is to use Azure.  Alternatively you can use an ASP.NET host and add entries to an external DNS server so that they propagate to your carrier and network DNS servers.

The solution includes the WP7CertInstallerAzure project which deploys to https://wp7certinstaller.cloudapp.net.  While developing the code I verified that the project deploys and runs successfully in Azure and that the example app can install certificates on a real phone.

After development I deleted the Azure deployment so https://wp7certinstaller.cloudapp.net is offline.  If you want to give it a try you’ll need to:

  1. Create an Azure hosted service of your own in the Azure Management Portal.

  2. Modify the CreateCert.bat file to generate an SSL certificate matching you’re service’s name; only “wp7certinstaller.cloudapp.net” within the script needs modification.  Luckily the commands in the batch file will happily make self-signed certificates for cloudapp.net so no additional DNS entries are needed.

  3. Use the Azure Management Portal to upload the two certificates into your service.  You’ll end up with something like this:

    image

  4. Select each certificate and the Portal will display the thumbprint in the properties pane on the right.  Copy and paste the thumbprints into WP7CertInstallerAzure’s ServiceConfiguration.cscfg file.  It isn’t required but you can modify the name attribute, but if you do then make the same modification in ServiceDefinition.csdef in two places.

  5. Modify the CertificateUrl string in WP7CertInstallerExample’s MainPage.xaml.cs file.  You can comment out the localhost string and uncomment / modify one of the cloudapp.net strings.  Keep in mind that you are installing the root certificate into the phone not the SSL certificate.

  6. Do a Debug build.

  7. Deploy WP7CertInstallerAzure to Azure.

  8. Access your Azure service from a browser, via both http and https, just to make ensure the site is running correctly.

  9. Tether and unlock your phone.

  10. Select “Windows Phone 7 Device” next to the debug button in Visual Studio and then click the debug button.

    image

You should see the same screens you saw in the emulator.  After installation there isn’t a way to uninstall the certificate so installation is a one time task.  You’ll need to change the name of your hosted service and complete the steps listed above if you want to see WP7CertInstaller in action a second time.

Reusing WP7CertInstaller in Your Projects

Once you’ve deployed the WP7CertInstaller project to a web site you only need to include the TrustedRootCertificateInstaller.cs file from the WP7CertInstallerExample in your project and make calls to it as demonstrated in MainPage.xaml.cs.

The key is to make a call to TrustedRootCertificateInstaller.BeginEnsureCertificateIsInstalled() with the correct Uri and then WaitOne() on the WaitHandle that the method returns before making any SSL requests.  You can perform other app initialization logic before calling WaitOne(), just don’t make SSL requests.  This is demonstrated in the example by placing the webBrowser1.Navigate() call after the WaitOne() call.

The WebBrowser control in the example is just to prove that SSL calls actually work.  Your code doesn’t need a WebBrowser control since internally TrustedRootCertificateInstaller uses an HttpWebRequest instance.

Notice that the example’s MainPage.xaml.cs and TrustedRootCertificateInstaller.cs both use “#if (DEBUG)” conditional compilation to eliminate the WP7CertInstaller code from non-Debug builds.  You are free to delete these lines if you want your Release builds to include WP7CertInstaller functionality.

How Does WP7CertInstaller Work?

Simply put the WP7CertInstaller project provides a website from which to download certificates and the TrustedRootCertificateInstaller class, which is included in your app’s project, attempts to download an SSL page from the website.  If your app receives a WebException with Status equal to UnknownError while downloading the SSL page it starts a WebBrowserTask so that the phone’s IE browser downloads the same page via a non-SSL request.

The WebBrowserTask causes your app to deactivate and enter tombstone mode.  After the user installs the trusted root certificate they either active your app again using the phone’s back button or launch it anew using the app’s icon.  TrustedRootCertificateInstaller must get called for both cases and that is why the example is written to make the BeginEnsureCertificateIsInstalled() call in the main page’s Loaded event.

WP7CertInstaller Web Site
Certificate.p7b

Certificate.p7b is a web page which allows for the download of certificates.  Certificate.p7b is really just an .aspx page renamed to .p7b.  This was done because WP7 appears to trigger the certificate install wizard off of the file extension instead of the response’s content type header.  Caller’s pass parameters to the page, normally via the query string, which identifies the certificate to reply with.  The page either responds with an X509 certificate exported in the pkcs7 format or an HTML page saying the certificate was not found.  Parameters are findBy and findValue which map to the parameters expected by the X509Certificate2Collection.Find() method.  Normally FindBySubjectName is used but FindByThumbprint works too.

There are settings in web.config which cause IIS and ASP.NET to process requests for the .p7b file extension the same as is done for .aspx.

Default.aspx

Default.aspx lists the certificates available for download and provides a link to Certificate.p7b for each certificate.  There is also a link which uses mailto: so that the link to Certificate.p7b is sent via email.

The code behind file contains a constant called HttpPort which must be changed if your WP7CertInstaller web site is using a port other than port 80 for http traffic.  One scenario for this is when using the Azure DevFabric emulator.  Note that the HttpPort constant also appears in the phone project’s TrustedRootCertificateInstaller.cs file and must be kept in sync with the value in this file.

X509CertController.cs

X509CertController.cs contains all the certificate handling logic.  See the prerequisites post for an explanation of “#define NoAzureSDKInstalled”.  Notice that GetCertificates() obtains certificates from LocalMachine\My when running in Azure and LocalMachine\Root otherwise.  I did this to simplify the Azure deployment but you can change it if you like.

WP7CertInstallerExample

Unless noted below the files are as generated by the Visual Studio template.

MainPage.xaml

MainPage.xaml contains a WebBrowser control as simple proof that SSL requests are successful.

The code behind shows how to use WP7CertInstaller from within your project.  Notice that several CertificateUrl strings are commented out to provide examples of how Certificate.p7b is called for various scenarios.

The CertificateUrl is passed to TrustedRootCertificateInstaller.BeginEnsureCertificateIsInstalled() and needs to be an https Url.  The Url serves two purposes, 1) it specifies an SSL page that TrustedRootCertificateInstaller will attempt to download and 2) it specifies the page which a WebBrowserTask will use to make the phone’s IE browser initiate the certificate download; the code automatically changes from https during 1 to http during 2 and hence the need for the HttpPort constant described below.

Notice also that https defaults to port 443, and the port number is omitted in the Url, but is required when SSL runs on a port other than 443.  The Azure DevFabric emulator typically runs SSL on port 444.

TrustedRootCertificateInstaller.cs

The comments in TrustedRootCertificateInstaller.cs explain how the code works.  Basically a HttpWebRequest instance makes an asynchronous request to download the SSL page.  If it fails a WebBrowserTask is used to download the certificate.

A WaitHandle is used to signal successful completion of the SSL request.  Your code needs to refrain from making SSL calls until signaled via the WaitHandle’s WaitOne() method.

The file contains a constant called HttpPort which must be changed if your WP7CertInstaller web site is using a port other than port 80 for http traffic.  See Default.aspx above for more info.