Enabling SSL Client Certificates in ASP.NET Web API

You can get an excellent description of what client certificates are and how they work in this article – if you want to really understand this post take a minute to read it. In a nutshell, client certificates allows a web app to authenticate users by having the client provide a certificate before the HTTP connection is established (during the SSL Handshake). It’s an alternative to providing username/password. As the article explicitly mentions, client certificates have nothing to do with HTTPS certificates – that means you can have HTTPS communication without client certificates. However, you cannot have client certificates work without enabling HTTPS on your site.

ASP.NET Web API can take advantage of client certificates to authenticate clients as well. In this post, I’ll walk you through the steps of configuring client certificates in your IIS and test it on a Web API. Please notice that this steps should only be executed on a Development environment, as a production environment might require a more rigorous approach.

1. Let’s first create the necessary certificates (as explained here). To do this open the Visual Studio Developer Command Prompt and run the following command. Type the certificate password as prompted. This first command will create a ‘Root Certification Authority’ certificate. If you want more details, you can read about how these commands this MSDN article.

[code language=”powershell”]
makecert.exe -n "CN=Development CA" -r -sv DevelopmentCA.pvk DevelopmentCA.cer
[/code]

2. Install the DevelopmentCA.cer certificate in your Trusted Root Certification Authorities for the Local Machine store using MMC (right-click over the Trusted Root Certification Authorities folder | All Tasks | Import).

image

Note: For production scenarios, this certificate will obviously not be valid. You will need to get an SSL certificate from a Certificate Authority (CA). Learn more about this here.

3. Now let’s create an SSL certificate in a .pfx format, signed by the CA created above, using the following 2 commands. The first command will create the certificate and the second one will convert the .pvk certificate containing the private key to .pfx. This certificate will be used as the SSL certificate.

[code language=”powershell”]
makecert -pe -n "CN=localhost" -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1
-ic DevelopmentCA.cer -iv developmentCA.pvk -sv SSLCert.pvk SSLCert.cer

pvk2pfx -pvk SSLCert.pvk -spc SSLCert.cer -pfx SSLCert.pfx -po 123456
[/code]

4. Install the SSLCert.pfx certificate in the Personal store of Local Computer using MMC. Notice that the certificate shows it was issued by the Development CA.

image

5. Run this third command to create a private-key certificate signed by the CA certificate created above. The certificate will be automatically installed into the Personal store of  Current User, as shown in the figure below. Notice also that the Intended Purpose shows Client Authentication.

[code language=”powershell”]
makecert.exe -pe -ss My -sr CurrentUser -a sha1 -sky exchange -n "CN=ClientCertificatesTest"
-eku 1.3.6.1.5.5.7.3.2 -sk SignedByCA -ic DevelopmentCA.cer -iv DevelopmentCA.pvk
[/code]

image

6. Now let’s get into the code. A good place in ASP.NET Web API 2 to validate the client certificate is an ActionFilterAttribute, by calling GetClientCertificate on the request message (see some examples here). An action filter is an attribute that you can apply to a controller action — or an entire controller — that modifies the way in which the action is executed.

[code language=”csharp” highlight=”5″]
public override void OnActionExecuting(HttpActionContext actionContext)
{
var request = actionContext.Request;

if (!this.AuthorizeRequest(request.GetClientCertificate()))
{
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
}
[/code]

7. Use your local IIS to host your Web API code. Under the site configuration, open the site bindings and configure HTTPS using the SSL certificate created above. Select the ‘localhost’ certificate create in step 3.

image

8. Open the SSL Settings under your web site in IIS and select Accept. The options available are:

  • Ignore: Will not request any certificate (default)
  • Accept: IIS will accept a certificate from the client, but does not require one
  • Require: Require a client certificate – to enable this option, you must also select “Require SSL”

clip_image001

Changing this value will add the following section in the ApplicationHost.config (by default located under C:WindowsSystem32inetsrvconfig). The value SslNegotiateCert equals the Accept we’ve selected before.

[code language=”xml” highlight=”6″]
<configuration>

<location path="subscriptions">
<system.webServer>
<security>
<access sslFlags="SslNegotiateCert" />
</security>
</system.webServer>
</location>
</configuration>
[/code]

Note: If you want to enable this from Web.config instead of using ApplicationHost.config, notice that the  <access> element is not allowed to be overridden from the Web.config by default. To enable overriding the value from Web.config you can change the overrideModeDefault value of the <access> section like this: <section name=”access” overrideModeDefault=”Deny” />. Please notice this is not recommended for production servers, as this would imply changing the behavior for the entire IIS server.

9. Now when browsing to the site using HTTPS in a browser like Internet Explorer you should get prompted for a client certificate. Select the ClientCertificatesTest client certificate you’ve created. As we’ve only selected ‘Accept’ in IIS SSL Settings, if you click Cancel, you should be able to browse to the site all the same, even if you didn’t provide a client certificate.

Also, notice that you are now shown an untrusted certificate warning because you’ve installed the Development CA cert as a Trusted Root Certification Authority.

image

Finally, if you want to know how to perform a request programmatically using client certificates, you can check this Gist.

Note: I’m actually not an expert in security, this post is mostly the results of a couple of battles, some of them won some of them lost – so feel free to provide feedback!



6 Comments

Leave a Reply