Adam's "Blog"

That's all, really.

Generating an Https Certificate for an IIS Web Site Using Letsencrypt-win-simple

Let’s Encrypt, a service to provide free https certificates, recently entered public beta. I ran it on a Linux box and manually generated a certificate for an IIS server, using the following command:

./letsencrypt-auto certonly --manual -d example.com -d domain2.example.com -d domain3.example.com

That worked well, to generate a certificate for example.com with Subject Alternate Names domain2.example.com and domain3.example.com. It was a manual process, however, requiring me to create temporary files on each of those domains (and deal with some IIS configuration to serve them properly). For web servers which are directly supported by letsencrypt, this would be an automatic process.

While checking to see if IIS support might be forthcoming, I found the ACMESharp project (formerly named letsencrypt-win), and from there, the letsencrypt-win-simple project.

I cloned and built the letsencrypt-win-simple project, ran letsencrypt.exe with no parameters, and got the following output (well, without all the X’s):

Let's Encrypt (Simple Windows ACME Client)

ACME Server: https://acme-v01.api.letsencrypt.org/
Config Folder: C:\Users\adam\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org

Getting AcmeServerDirectory
Enter an email address (not public, used for renewal fail notices): XXXXXXX@XXXXXXX.XXX
Calling Register
Do you agree to https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf? (Y/N)
Updating Registration
Saving Registration
Saving Signer

Scanning IIS 7 Site Bindings for Hosts
 1: IIS devXXXXXX (%SystemDrive%\inetpub\wwwroot)
 2: IIS deXXXXXXX (C:\Source\XXXXXXX\XXXXXXXXXXXX)
 3: IIS umbraco4 (c:\Source\XXXXXXXX\umbraco)
 4: IIS local.XXXXXXXXX (C:\Source\XXXXXX)
 5: IIS cXXXXXXXXXXXXX (C:\Source\XXXXXXXXXXXXXXX\XXXXXXXXXXX)
 6: IIS dXXXXXXXXXXXX (C:\Source\XXXXXXXXXXXX\XXXXXXXXXXXXXXXX\XXXXXXXX)
 7: IIS local.XXXXXXXXX (c:\Source\XXXXXXXXX)
 8: IIS test.XXXXXXXXX (c:\Source\XXXXXXXXX)
 9: IIS local.XXXXXX.XXXXXXXXXX (c:\Source\XXXXXXXXX\XXXXXXXXXXX\XXXXXXX)
 10: IIS cXXXXXXX (C:\Source\XXXXXXXXXX\XXXXXXXXXXXX)
 11: IIS devXXXXXXXXXXX (C:\Source\XXXXXXXXXXXX\XXXXXXXXX\XXXXX)
 12: IIS pXXXXXXXXXXXX (C:\Source\XXXXXXXXXXX\XXXXXXXXX\XXX)
 13: IIS www.XXXXXXXXXX (C:\Source\XXXXXXXXXXXX\XXXXXXXXXXX)
 14: IIS cXXXXXXX (c:\inetpub\wwwroot)

 M: Generate a certificate manually.
 A: Get certificates for all hosts
 Q: Quit
Which host do you want to get a certificate for: q

As you can see, I have a lot of sites in IIS, but the test site I wanted the certificate for was not shown. This is because it had no host headers — it was bound to a port on all unassigned IPs, without a host header. I added another binding, with a host header, to that site, then ran letsencrypt.exe again, and it showed my site in the list. I entered its number at the prompt and continued. (I’m not sure how you would create a certificate with Subject Alternate Names, like I did with letsencrypt above, or if that is even supported in letsencrypt.exe.)

Which host do you want to get a certificate for: 10

Authorizing Identifier XXXXX.XXXXX.XXX Using Challenge Type http-01
 Writing challenge answer to C:\inetpub\XXXXXX\.well-known/acme-challenge/eue_UrCJxw9Xpu3F7QxxIoXPq77GwaEuXSzj4RTj6so
 Writing web.config to add extensionless mime type to C:\inetpub\XXXXXX\.well-known\acme-challenge\web.config
 Answer should now be browsable at http://XXXXX.XXXXX.XXX/.well-known/acme-challenge/eue_UrCJxw9Xpu3F7QxxIoXPq77GwaEuXSzj4RTj6so
 Submitting answer
 Refreshing authorization
 Refreshing authorization
 Authorization Result: invalid

******************************************************************************
The ACME server was probably unable to reach http://XXXXX.XXXXX.XXX/.well-known/acme-challenge/eue_UrCJxw9Xpu3F7QxxIoXPq77GwaEuXSzj4RTj6so

Check in a browser to see if the answer file is being served correctly.


This could be caused by IIS not being setup to handle extensionless static
files. Here's how to fix that:
1. In IIS manager goto Site/Server->Handler Mappings->View Ordered List
2. Move the StaticFile mapping above the ExtensionlessUrlHandler mappings.
(like this http://i.stack.imgur.com/nkvrL.png)

******************************************************************************
Press enter to continue.

It told me what to do, but instead of doing that, I created a web.config in the .well-known directory as described here. (I removed the , as it duplicated the section in the web.config which letsencrypt.exe created in the acme-challenge directory, causing an error.)

And it worked:

 Authorization Result: valid
 Deleting answer

Requesting Certificate
 Request Status: Created
 Saving Certificate to C:\Users\adam\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org\XXXXX.XXXXX.XXX-crt.der
 Saving Issuer Certificate to C:\Users\adam\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org\ca-100823E47413E5750B34E74D1E912E45DE-crt.pem
 Saving Certificate to C:\Users\adam\AppData\Roaming\letsencrypt-win-simple\httpsacme-v01.api.letsencrypt.org\XXXXX.XXXXX.XXX-all.pfx (with no password set)
 Opened Certificate Store "My"
 Adding Certificate to Store
 Closing Certificate Store
 Adding https Binding
 Committing binding changes to IIS
 Creating Task letsencrypt-win-simple httpsacme-v01.api.letsencrypt.org with Windows Task Scheduler at 9am every day.
 Renewal Scheduled IIS XXXXX.XXXXX.XXX (C:\inetpub\XXXXXX) Renew After 2/10/2016
Press enter to continue.

But hitting the web site failed. I looked in IIS Manager and found that the site was stopped. When I tried starting it, I got this error:

---------------------------
Internet Information Services (IIS) Manager
---------------------------
The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)
---------------------------
OK   
---------------------------

Something else was bound to the port.

C:\> netstat -nao | grep LISTENING | grep :443
TCP    0.0.0.0:443            0.0.0.0:0              LISTENING       2840

Process Explorer showed me that process ID 2840 belongs to Skype.exe. This Stack Overflow answer told me to go into Skype’s Tools, Options, Advanced, Connections, and uncheck “Use port 80 and 443 for additional incoming connections”. After clicking Save and restarting Skype as prompted, port 443 was freed up and I was able to start the web site.

Hitting it remotely still didn’t work, however. I tried hitting https://localhost/ to test it from my machine; it came up, but showed a 404 error because the hostname on that site binding was not “localhost”. But that was enough to tell me that IIS was listening on that port, and I realized it was probably Windows Firewall. I added an inbound rule allowing port 443, and at last my site was secured with https.

letsencrypt.exe also (as mentioned in its output above) created a Task Scheduler task to run every day and keep my certificates renewed. Pretty cool. I will find out if it works in three months.