We recently needed to restrict access to a small subset of our IIS-based website and decided to leverage client certificates as the second factor for our two factor authentication. I was surprised when I couldn’t find end-to-end instructions on how to do this anywhere on the Internet. The bits and  pieces I did find helped, but the overall implementation still took longer than I had anticipated. I’ll cover the steps I took in this blog post.

Some quick background first. Our ASP.NET based site running on IIS 7 makes use of standard cookie-based forms authentication. Our goal was to put in place a secondary auth mechanism that will further restrict access to a specific subdirectory with sensitive code. We settled on issuing each user permitted to access that directory their own SSL certificate so they and the IIS server can mutually authenticate. These so-called client certificates are just like the far more commonly used server certificates, except they authenticate the client to the server rather than the server to the client.

Step 1: Create a Self-Signed CA Certificate

Since we had full control over the environment in which our certificates were going to be used (i.e. on all clients and servers), we wouldn’t have benefited from having a top-tier certification authority (CA) like VeriSign or DigiCert issue our client certificates. That’s why we created our own and self-signed it.

As you may know, a CA certificate is sort of a master certificate that is ultimately trusted and that is used to sign other, lower level certificates. If a lower level certificate is signed by a CA certificate and a given computer trusts the CA that issued the CA certificate, it will implicitly trust the lower level certificate as well. The two certificates — the CA certificate and the lower level certificate — are related in what’s called a certificate chain. One simply derives from the other.

Utilizing a CA certificate for a purpose like ours is extremely useful. A server that is validating a large number of client certificates doesn’t have to individually trust each and every one (usually by installing them all in one of the trusted locations in its certificate store). All it needs to do is install the CA certificate (and only the CA certificate) and leave the rest up to the operating system and IIS.

Let’s create one. On Windows, you can use the MakeCert tool to create a wide range of certificates any way you like. The tool is part of the Microsoft Windows SDK and was located at C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\x64 on my computer.

makecert.exe -r -n “CN=My Personal CA” -pe -sv MyPersonalCA.pvk -a sha1 -len 2048 -b 01/21/2010 -e 01/21/2016 -cy authority MyPersonalCA.cer

This command will create two files, a PVK file that contains the certificate’s private key and a CER file that contains the public key. We’re also instructing MakeCert to self-sign the certificate (-r), give it a common name of My Personal CA (-n), make the private key exportable (-pe), use the SHA1 algorithm to sign the certificate (-a), generate a 2048-bit certificate (-len), make the certificate valid for about 6 years (-b and -e), and make this a CA certificate (-cy).

The MyPersonalCA.cer file will get installed on all the clients and servers that will participate in our two-factor authentication. The MyPersonalCA.pvk file must be closely guarded and will remain on the machine that creates our lower level certificates. The private key in the PVK file will sign them.

Step 2: Create a Client Certificate

Next, let’s create our first client certificate.

makecert.exe -iv MyPersonalCA.pvk -ic MyPersonalCA.cer -n “CN=John Doe” -pe -sv JohnDoe.pvk -a sha1 -len 2048 -b 01/21/2010 -e 01/21/2011 -sky exchange JohnDoe.cer -eku

We’re asking MakeCert to create a certificate that’s issued by our personal CA (-iv and ic), intended for John Doe (-n), valid for only a year (-b and -e), used for communication rather than signing (-sky), and intended only for purposes of Client Authentication (-eku). The -eku parameter is very important because certificates that aren’t marked with the Client Authentication purpose won’t be picked up by browsers.

On Windows, the convenient PKCS #12 certificate file (with a .PFX extension) is commonly used to transport certificates from one place to another. The format allows for storing the private key as well as the public key and makes password-protecting the private key and installing the certificate in general very easy. We’ll use another tool from the Windows SDK, PVK2PFX, to create John’s PFX file.

pvk2pfx.exe -pvk JohnDoe.pvk -spc JohnDoe.cer -pfx JohnDoe.pfx -po <new_private_key_password>

The PVK2PFX tool is pretty self-explanatory. It takes the .PVK file with the private key, the .CER file with the public key, and marries them together into a .PFX file. The tool was located at C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin on my machine.

At this point, we’ve got everything we need. We’ll send both MyPersonalCA.cer and JohnDoe.pfx to John and ask him to install the .CER file into the Trusted Root Certification Authorities folder and the .PFX file into the Personal folder of his computer’s certificate store. He can accomplish both tasks by double clicking on each file.

Step 3: Configure IIS 7 to Accept Client Certificates

Every site that looks to implement this mechanism must be exposed over HTTPS. There are a number of articles out there on how to do this already, such as this one from ScottGu.

Next, read over this article on IIS.net. The article does a great job describing what to do from this point onward. Few things to keep in mind:

  • One way to obtain the Base-64 encoded certificate for John Doe is to install John’s .PFX file, then open the Microsoft Management Console (run mmc.exe, CTRL-M, double click Certificates, Finish, OK, right click on John’s certificate under Personal/Certificates, export without a private key, and pick the Base-64 encoded X.509 (.CER) option).
  • You can get the IIS Administration Pack 1.0 from http://www.iis.net/expand/AdministrationPack.
  • Don’t forget to install MyPersonalCA.cer on the server hosting IIS7. You may need to restart IIS after adding/removing trusted root certificates, IIS may cache them.
  • We didn’t care about mapping client certificates to any Windows user accounts, so we left the password field empty and typed in the user’s name into the userName field to easily identify the row and the certificate.
  • To require client certificates for a given directory, select the directory in IIS Manager, double click SSL Settings, and select the Require radio button.

Is there a better/safer way to do this? Did something not work out quite right? Let me know in the comments section.