Quickstart: Sharing user certificates between Windows Store apps (HTML)
Certificate authentication provides a high level of trust when authenticating a user. Apps that require secure authentication beyond a userid and password combination can use certificates for authentication. In some cases, a group of services will want to authenticate a user for multiple Windows Store apps. This quickstart shows how you can authenticate multiple Windows Store apps using the same certificate, and how you can provide convenient code for a user to import a certificate that was provided to access secure web services.
Windows Store apps can authenticate to a web service using a certificate, and multiple Windows Store apps can use a single certificate from the certificate store to authenticate the same user. If a certificate does not exist in the store, you can add code to your app to import a certificate from a PFX file.
Objective: Authenticate a user using a certificate in a way that can be used from multiple Windows Store apps for multiple secure web services.
Prerequisites
This quickstart is for sample purposes only and will use the Microsoft Internet Information Services (IIS) on your local machine. To run this quickstart, you will need the following:
- Windows 8.1
- Microsoft Visual StudioMicrosoft Visual Studio
- IISMicrosoft Internet Information Server (IIS)
Instructions
1. Enable IIS and client certificate mapping
IIS is not enabled by default. You can enable IIS by using the Windows Control Panel.
- Open the Windows Control Panel and select Programs.
- Select Turn Windows features on or off.
- Expand Internet Information Services and then expand World Wide Web Services. Expand Application Development Features and select ASP.NET 3.5 and ASP.NET 4.5. Making these selections will automatically enable Internet Information Services.
- Click OK to apply the changes.
2. Create and publish a secured web service
In this step, you will create a secured web service and publish it to your IIS server.
Run Visual StudioVisual Studio as administrator and select New Project from the start page. Administrator access is required to publish a web service to an IIS server. In the New Project dialog, change the framework to .NET Framework 3.5. Select Visual C# -> Web -> Visual Studio - > ASP.NET Web Service Application. Name the application "FirstContosoBank". Click OK to create the project.
In the Service1.asmx.cs file, replace the default HelloWorld web method with the following "Login" method.
[WebMethod] public string Login() { // Verify certificate with CA var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2( this.Context.Request.ClientCertificate.Certificate); bool test = cert.Verify(); return test.ToString(); }
Save the Service1.asmx.cs file.
In the Solution Explorer, right-click the "FirstContosoBank" app and select Publish.
In the Publish Web dialog, create a new profile and name it "ContosoProfile". Click Next.
On the next page, enter the server name for your IIS server, and specify a site name of "Default Web Site/FirstContosoBank". Click Publish to publish your web service.
3. Configure your web service to use client certificate authentication
In this step, you will use the IIS manager to configure the web service that you just published to require a client certificate.
- Run the Internet Information Services (IIS) Manager.
- Expand the sites for your IIS server. Under the Default Web Site, select the new "FirstContosoBank" web service. In the Actions section, select Advanced Settings....
- Set the Application Pool to .NET v2.0 and click OK.
- In the Internet Information Services (IIS) Manager, select your IIS server and then double-click Server Certificates. In the Actions section, select Create Self-Signed Certificate.... Enter "ContosoBank" as the friendly name for the certificate and click OK. This will create a new certificate for use by the IIS server in the form of "<server-name>.<domain-name>".
- In the Internet Information Services (IIS) Manager, select the default web site. In the Actions section, select Binding and then click Add.... Select "https" as the type, set the port to "443", and enter the full host name for your IIS server ("<server-name>.<domain-name>"). Set the SSL certificate to "ContosoBank". Click OK. Click Close in the Site Bindings window.
- In the Internet Information Services (IIS) Manager, select the "FirstContosoBank" web service. Double-click SSL Settings. Check Require SSL. Under Client certificates, select Require. In the Actions section, click Apply.
- You can verify that the web service is configured correctly by opening your web browser and entering the following web address: "https://<server-name>.<domain-name>/FirstContosoBank/Service1.asmx". For example, "https://myserver.example.com/FirstContosoBank/Service1.asmx". If your web service is configured correctly, you will be prompted to select a client certificate in order to access the web service.
You can repeat the previous to quickstart steps to create multiple web services that can be accessed using the same client certificate.
4. Create a Windows Store app that uses certificate authentication
Now that you have one or more secure web services, you can create Windows Store apps that use certificates to authenticate to those web services. When you make a request to an authenticated web service using the HttpClient object, the initial request will not contain a client certificate. The authenticated web service will respond with a request for client authentication. When this occurs, the Windows client will automatically query the certificate store for available client certificates. Your user can select from these certificates to authenticate to the web service. Some certificates are password protected, so you will need to provide the user with a way to input the password for a certificate.
If there are no client certificates available, then the user will need to add a certificate to the certificate store. You can include code in your Windows Store app that enables a user to select a PFX file that contains a client certificate and then import that certificate into the client certificate store.
Tip You can use makecert.exe to create a PFX file to use with this quickstart. For information on using makecert.exe, see MakeCert.
Open Visual StudioVisual Studio and select New Project from the start page. Select Visual C# -> Windows Store -> Blank App (XAML). Name the new project "FirstContosoBankApp". Click OK to create the new project.
In the default.html file, add the following HTML to the default body element. This HTML includes a button to browse for a PFX file to import, a text box to enter a password for a password-protected PFX file, a button to import a selected PFX file, a button to log in to the secured web service, and a text block to display the status of the current action.
<div> <button id="Browse" style="width:400px;height:80px;font-size:18px">Browse for PFX file</button><br /> PFX password <input id="PfxPassword" type="text" style="width:200px" /> (optional)<br /> <button id="Import" style="width:400px;height:100px;font-size:20px">Import certificate (PFX file)</button><br /> <button id="Login" style="width:400px;height:100px;font-size:20px">Login</button> <div id="Result" style="width:400px;height:400px;color:white" /> </div>
Save the default.html file.
In the js folder, in the default.js file, replace the default app.onactivated function with the following variables and function. They specify the address for the secured "Login" method of your "FirstContosoBank" web service, and a global variable that holds a PFX certificate to import into the certificate store. Update the <server-name> to the fully-qualified server name for your IIS server.
var pfxPassword; var pfxCert; var requestUri = new Windows.Foundation.Uri("https://<server-name>/FirstContosoBank/Service1.asmx?op=Login"); var result; app.onactivated = function (args) { if (args.detail.kind === activation.ActivationKind.launch) { pfxPassword = document.getElementById("PfxPassword"); result = document.getElementById("Result"); document.getElementById("Import").addEventListener("click", import_click, false); document.getElementById("Login").addEventListener("click", login_click, false); document.getElementById("Browse").addEventListener("click", browse_click, false); args.setPromise(WinJS.UI.processAll()); } };
In the default.js file, add the following click handler for the login button and method to access the secured web service.
function login_click() { makeHttpsCall(); } function makeHttpsCall() { var returnMessage = "Login "; var response; try { var httpClient = new Windows.Web.Http.HttpClient(); httpClient.getAsync(requestUri).then( function (response) { if (response) { if (response.statusCode == Windows.Web.Http.HttpStatusCode.ok) { returnMessage += "successful"; } else { returnMessage += "failed with "; returnMessage += response.StatusCode; } result.innerHTML = returnMessage; } }); } catch (ex) { returnMessage += "failed with "; returnMessage += ex.Message; } result.innerHTML = returnMessage; }
In the default.js file, add the following click handlers for the button to browse for a PFX file and the button to import a selected PFX file into the certificate store.
function import_click() { try { result.innerHTML += "Importing selected certificate into user certificate store....<br />"; var certmgr = Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager.userCertificateEnrollmentManager; certmgr.importPfxDataAsync( pfxCert, pfxPassword.text, Windows.Security.Cryptography.Certificates.ExportOption.exportable, Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent, Windows.Security.Cryptography.Certificates.InstallOptions.deleteExpired, "Import Pfx").then( function () { result.innerHTML += "Certificate import succeded<br />"; }); } catch (ex) { result.innerHTML += "Certificate import failed with " + ex.Message + "<br />"; } } function browse_click() { var resultMessage = "Pfx file selection "; var pfxFilePicker = new Windows.Storage.Pickers.FileOpenPicker(); pfxFilePicker.fileTypeFilter.append(".pfx"); pfxFilePicker.commitButtonText = "Open"; try { pfxFilePicker.pickSingleFileAsync().then( function (pfxFile) { if (pfxFile != null) { Windows.Storage.FileIO.readBufferAsync(pfxFile).then( function (buffer) { var dataReader = Windows.Storage.Streams.DataReader.fromBuffer(buffer); var bytes = new Uint8Array(buffer.length); dataReader.readBytes(bytes); dataReader.close(); pfxCert = btoa(bytes) pfxPassword.text = ""; resultMessage += "succeeded"; }); } else { resultMessage += "failed"; } }); } catch (ex) { resultMessage += "failed with "; resultMessage += ex.Message; } result.innerHTML = resultMessage; }
Save the default.js file.
You can now press F5 to run your Windows Store app and log in to your secured web service as well as import a PFX file into the local certificate store.
You can use these steps to create multiple Windows Store apps that use the same user certificate to access the same or different secured web services.
Summary
In this quickstart, you learned how to create a web service that is secured using client certificates, and how to create a Windows Store app that can access that secured web service.