Last update: April 24, 2017

VS: Enable Multiple SSL Projects on IIS Express using Self-Signed Certificates

Goal

This document serves to instruct how to enable multiple Visual Studio (2013 & later) projects using self-signed certificates across a broad range of SSL ports, in addition to identifying common issues that may arise in the process. In brief, create certificates with friendly aliases, and point them to different SSL ports so we can support multiple projects on one development environment.

Intro

Over the past 7 years there have been few publications on how to enable SSL on IIS with varying degrees of information. There is however no single document that describes everything in detail from end-to-end. I am writing first for myself because it may be a year or two before the need arises once again. Therefore having good documentation on a challenging subject is a nice way to both plus success and save time instead of going through rediscovery.

Step-0: Before we being, let's create backups

  1. Here's the critical list that can save us should we need to restore; save these in an Archive Folder at the project root:
    1. Project File: <Name>.csproj
    2. IIS Express Applicationhost.config: Located in <USERPROFILE>\My Documents\IIS Express\config\
  2. Create a backup of the SSL Certificate bindings registered within Sys.Host
    1. Two ways to do this: Powershell or Visual Studio Dev Cmd Prompt; both are effective. If you don't have the VS Dev Cmd Prompt, the link below descripts how to create it (I did it manually):
      https://msdn.microsoft.com/en-us/library/ms229859(v=vs.110).aspx
    2. b. Open either PS or the Prompt. With PS, you need to navigate within the command structure which seems clunky. Below is the command for the Prompt:
      1. Output to file: netsh http show sslcert > SSLCertBindings.txt
      2. Output to Clipboard: netsh http show sslcert | clip
      3. Output single IP mapping: netsh http show sslcert ipport=0.0.0.0:443
  3. Backup Firewall Settings:
    1. Select the <Windows> key , type "firewall" or "windows firewall", and open it.
    2. Click on "Advanced settings"
    3. Right-Click on Inbound Rules, select "Export List…" and follow-through with the procedure.

Step-1: Have Visual Studio provision Ports

Simple is easy, so let Visual Studio do the figuring for us. Presuming we have a normal web application that compiles and runs fine:

  1. Open up the candidate Web project and select the web application.
  2. Reviewing the Project Properties,
    1. SSL Enabled = False
    2. SSL URL = empty
    3. URL = http://localhost:50203/ (having a port number assignment to that effect)
  3. The HTTP URL port must be noted for future reference; write it down.
  4. Let's get the next available SSL Port: Select SSL Enabled to equal True.
  5. Like the HTTP URL port, the SSL URL port must be noted as well; write it down.
  6. ow the SSL URL value equals something like https://localhost:44301/
  7. Run the project. The browser should still run the application successfully on HTTP, but when we drop in the new SSL URL into the browser address bar then we should receive an error. That's because
    1. We don't have an SSL certificate that binds our app to a URL, and
    2. Our certificate is not mapped to the specific binding.
      We're going to fix that!

Step-2: Create a Self-Signed Certificate

If we only needed one, this document would be a lot shorter because we'd default to Port 443 and move on. However we are production-type people with many clients so we're going to need more ports to service them if using one machine. We'll use two commands: makecert.exe and pvk2pfx.exe

Details of makecert.exe:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa386968(v=vs.85).aspx

Details of pvk2pfx.exe:
https://msdn.microsoft.com/en-us/library/windows/hardware/ff550672(v=vs.85).aspx

First, we can make a simple cert, although that means going back and modifying. Better to just create the thing we want and move on to the next, so here's the robust command example.

makecert.exe -n "CN=MyWebApp.local.net,OU=Engineering,L=Redmond,S=WA,C=US" -r -pe -a sha512 -len 4096 -cy authority -sky exchange -eku 1.3.6.1.5.5.7.3.1 -sv MyWebApp.local.net.pvk MyWebApp.local.net.cer

At first glance, the name of the cert reminds us of Active Directory, however the only element required to create a cert is CN (Certificate Name), and the rest is gravy. Avoid SHA1, and instead trust in SHA512 for the algorithm, therefore the length needs to change accordingly. Accept the defaults for Expiration. Make the private key exportable; this is a nice feature all around, especially if you want to import into traditional IIS Web Hosting. The output will be two files: The Certificate and the Private Key.

Next command, we create and set a password for the Private Key Exchange file. DO NOT SKIP THIS STEP.

pvk2pfx.exe -pvk MyWebApp.local.net.pvk -spc MyWebApp.local.net.cer -pfx MyWebApp.local.net.pfx -pi password -po password

For password, follow the convention for creating strong passwords; it just makes good sense. Between these three certificate formats, we now have full flexibility to add these where needed in the Console Root\Certificates.

Repeat this process for any additional project certificates.

Step-3: Adding the Certs

A great built-in tool is the MMC Certificate Management Snap-in. Here's the link on how to install this:
https://msdn.microsoft.com/en-us/library/ms788967(v=vs.110).aspx

We're going to use the Certificates for the Local Computer account. There are two folders in particular that are of interest: Personal and Trusted Root Certification Authorities. Open the Personal folder. If there are no certificates then right-click on the Personal folder, otherwise right-click on the Certificates folder. Select All Tasks, and Import… which launches the Certificate Import Wizard.

  1. Click Next to continue
  2. Browse to the location where we saved the certs
  3. Change the File Filter to Personal Information Exchange (*.pfx)
    Why: Instead of just importing the Cert we made, I personally like to validate the file with a password to demonstrate integrity of process.
  4. Select the appropriate file and click Open, then Next
  5. Prompt for Password: Enter the password earlier provided, and optionally check the box to "Mark this key as exportable". Leave the box checked for "Include all extended properties".
  6. Accept the default Certificate Store (Personal) and select Next.
  7. Click Finish to complete the importation.
  8. If there were no certificates before, then a new "certificate" folder is created. The certificate will be placed there.

Step-4: Prepare the Certificate for IIS by copying the Certificate Hash

  1. Open the Certificate Folder and find the new cert.
  2. Double-click to open it, and select the Details Tab to review all the neat information.
  3. Optionally, click Edit Properties… and give the Cert a friendly name and click OK. Adding this is particularly useful for IIS Manager.
  4. Scroll to the bottom and find the Thumbprint Field, then click it to read the Certificate Hash value:
  5. Click in the box, select all with Ctrl-A, and copy to the Clipboard.
  6. Open Notepad, paste into it the Value.
    1. Note: This is a Unicode string, so there are two hidden characters at the beginning of the string that we'll need to delete somehow. Pasting into Powershell will reveal the characters, however the Developer Command Prompt will not, and creates a pesky cryptic error if we try to use it in a command.
    2. Workaround: Select all but the first character of the string, and past that into Notepad, then manually add the first character to complete the sequence.
  7. In Notepad with the completed string, select Ctrl-H to bring up the Replace dialog, select Space to put a single space in what to find, and then click Replace All to concatenate the string. We now have a viable CertHash for IIS; write it down or copy it to Notepad.
  8. Close the Certificate dialog from where we copied the Thumbprint.
  9. While still in the Console Root|Certificates, open the Trusted Root Certification Authorities folder to view the Certificates subfolder.
  10. Select our Cert again from the Personal|Certificates folder and then hold the Ctrl-Key down whilst dragging the file and drop it onto the Trusted Root Certification Authorities|Certificates subfolder, essentially copying the certificate. Open that last folder to inspect that the certificate was indeed copied.
  11. We're done with this part. Repeat for any other project.

Step-5: Update SSL Certificate Bindings

We made a backup of these bindings at the very beginning; let's take a look at that list and inspect what we have.

  • Original SSL Binding is Port 443
  • Additional Range of Ports begins with 44300 to 44399,
  • Therefore we have a total of 101 ports altogether.
  • Each Binding contains the following fields of interest:
    • IP:port, example: 0.0.0.0:44301
    • Certificate Hash, example: 89b34f71bc72fc3080344b429409d2177b1ec19
    • Application ID, example: {b510df42-de68-4ddf-abb4-55c471ab0e1c}

Depending the Port we wish to use, we'll need to note the Application ID first before we attempt to update the information.

One other thing, we can set these ports manually, or we can have Visual Studio allocate the first free port for us as indicated in Step-1. For the moment, let's presume we know which port we want to modify so we can create the command arguments.

Get the Binding details for a specific Port

  1. Using Dev Cmd Prompt (or PS, which needs adjusted commands), create a command to find the details of Port 44301:
    netsh http show sslcert ipport=0.0.0.0:44301
  2. Copy the Application ID.
  3. Unfortunately there is no way to EDIT port information, so we need to delete the Port, then add it back. Create the command to delete the Port:
    netsh http delete sslcert ipport=0.0.0.0:44301
  4. The function will return success if all went well.
  5. Now we add the Port back using our CertHash, (using the examples above) with this command:
    netsh http add sslcert ipport=0.0.0.0:44301 appid={b510df42-de68-4ddf-abb4-55c471ab0e1c} certhash=89b34f71bc72fc3080344b429409d2177b1ec198 Note: Cross-check to be sure the certhash is the new Thumbprint and the appid matches the previous value.
    1. The function will return success if all went well.
    2. The most common error is "The parameter is incorrect". Causes can be:
      1. We didn't copy the Thumbprint and trim off the hidden Unicode characters.
      2. Spaces between the {} or other parameters. Just make the statement consise.
  6. Repeat this process for every project.

Step-6: Restart Associated Services

It's really important to follow this procedure to the letter, otherwise we'll spend hours trying to figure out why our stuff doesn't work at all.

Again, using the Dev Cmd Prompt (or PS), we need to stop and start the associated services. Some of the function calls have changed with time; I'm going to demonstrate the long-handed work-around how to make this work.

  1. net stop http
    Note: This actually stops several services which we'll need to restart manually.
  2. net start http
    Note: DOES NOT restart all associated services.

What's really going on: With my box, the first action stops 6 services:

  • World Wide Web Publishing Service
  • SSDP Discovery
  • IIS Admin Service
  • HomeGroup Provider
  • Function Discovery Resource Publication
  • Function Discovery Provider Host

These are easy enough to find in the Services; just understand that all must be restored (apparently) to have complete and total happy camper bliss. Now we are ready to go back and deal with our Visual Studio configuration.

Step-7: Create our Own Virtual Directory Mapping

  1. Open up the current IIS Express Applicationhost.config (and not the copy) using Notepad as Administrator.
  2. Search for the Project Name under the <sites> collection.
  3. Within the bindings collection, now that SSL was set to True, we'll have two entries that look like this:
    <binding protocol="http" bindingInformation="*:50203:localhost" />
    <binding protocol="https" bindingInformation="*:44301:localhost" />
  4. It's possible to have more than one set of bindings. Let's add a set using our new friendly-named cert:
    <binding protocol="http" bindingInformation="*:50203: MyWebApp.local.net" />
    <binding protocol="https" bindingInformation="*:44301: MyWebApp.local.net" />
  5. Save the file and exit.

Step-8: Configure the Firewall to allow more Inbound Ports

Very simply, IISExpressWeb has two sets of Inbound Port rules:

  • Port 80, Public and Private, and
  • Port 443, Public and Private.

We need to open up the Port 443 references to include the range of ports available.

  1. Select the <Windows> key , type "firewall" or "windows firewall", and open it.
  2. Click on "Advanced settings"
  3. Right-Click on Inbound Rules so that we see the List of Rules.
  4. Find the first IISExpressWeb entry, and double-click
    1. Select the "Protocols and Ports" tab
    2. If the Port says 443, update it so that is reads:
      443, 44300-44399
    3. Click apply
  5. Move to the next IISExpressWeb entry and repeat. If the Port is 80, move to the next. There's only two IISExpressWeb rules affected.

Caveat: This action opens up all the SSL ports. However for Security, it might be wise to lock this range down once the number of projects affected are completed and the last known port used have been given.

Step-9: Configure our Visual Studio Project to use the new Certificate

This is it! We're now ready to switch over to the newly minted Cert and binding. Before we switch, let's test the waters first to see if this raft floats:

  1. Run the Web application again. We expect the normal http address to work fine and without issues.
  2. Try again with https://localhost:44301/ - and the result now is: "There is a problem with this website's security certificate." That's because the port mapping now exists, but the certificate is not found.
  3. As an experiment, try http://MyWebApp.local.net:50203/ (using the HTTP port number): This should resolve normally and without errors.
  4. Finally, try SSL again but this time using https://MyWebApp.local.net:44301/ and we should see no errors, plus a little Lock in the address bar. Clicking the Lock present the certificate we had created earlier.
  5. End the session and return back to the Project. Let's make these changes permanent!
    1. Select the Web Application and click Alt-Enter to bring up the Project Properties
    2. Select the Web tab and change the Project URL to https://MyWebApp.local.net:44301/
    3. Click the Create Virtual Directory. There should be no conflicts.
  6. Re-run the application; it should run normally and with the Lock visible.
  7. We are done, except for the remaining projects that need SSL working in a Development Environment!

References

This work was inspired by the following influences for which I am grateful:

Disclaimer

I'm am responsible for myself, and no one else. It is possible that this solution may contain errors and typos; as it happens, I'm human and there are bound to be better ways to do this. If the solution works for you, great! I spent many hours searching – twice; there won't be a third time.

Background

Self-employed Engineering Consultant since 1987. I began writing software when the PC first came out, and for Microsoft under contract starting in 1993. Forays into website development began before Microsoft using Mozilla as a browser, although when ASP first emerged… that's when everything changed. Proud to have been a part of many prototypes and applications for all sorts. Enjoy!