Commands for manual install/update certs on Windows Server 2012/IIS 8.5

FINAL NOTICE ON THIS TOPIC: 06/04/2018 The latest renewal cycle worked flawlessly, with no errors, and the new cert was installed perfectly and the old one removed perfectly and the bindings for ports 80 and 443 were set perfectly. Everything worked great! So, hopefully this can be a learning tool for those who want to know each stage of the cert renewal process and how it can be done using only native Windows Server commands (plus the cert tool, of course.)

EDIT: 03/23/2018 this renewal cycle has allowed me to find the final key to the puzzle. This is now a turnkey, fully-manual process for cert renewal including binding to the site(s)! Yes, I’d recommend using one of the several available clients instead, but I needed to learn how this all works myself from the ground up.

Since I had not found an end-to-end solution for a “manual” process for automatically updating LE certs under Windows without using one of the built software, I thought I’d post how I’ve automated the process myself using batch files and default commands and not even relying on Powershell. I have commands for the final step of binding the new cert to the website(s) which probably work but they have not yet been tested, so when my next renewal is required I will update this post.

This was done on Windows Server 2012 R2 running IIS 8.5 using Server Name Indication (SNI) and a single certificate for both www.mydomain.com and mydomain.com and both are bound to the same cert. Both websites are bound to port 443 with the SNI box checked in each and a final non-SSL website is bound to port 80 which redirects to the SSL site using regexp via URL Rewrite in the IIS management console. All minimum versions are critical because of required features or security patches! NOTE: of course you should change “mydomain.com” to your own domain everywhere in here.

This solution uses the command line LE64.exe 64-bit Windows binary available for free at Zero SSL version 0.28 minimum. It also requires a command-line email program, I used the free MailSend portable Windows binary (decided not to complicate things by telneting to port 25 of the mail server.) Finally it requires the OpenSSL 32-bit binary v1.1.0e minimum.

I set up Task Scheduler to launch this batch file weekly (ensure that the task will fire even if the account is not logged in, and make it a member of the Administrators or Backup group so it can run a batch file.) All of the required files, including the certificate, keys, and batch files are in the same directory except OpenSSL which is installed normally:

@echo off
le64.exe --key account.key --csr mydomain.csr --csr-key mydomain.key --crt mydomain.crt --domains "mydomain.com,www.mydomain.com" --path \inetpub\wwwroot\.well-known\acme-challenge\ --unlink --renew 21 --issue-code 100 --live
if errorlevel 255 goto err
if errorlevel 100 call send-good-email-to-webmaster.bat
goto xit
:err
call send-bad-email-to-webmaster.bat
:xit

This simply attempts the cert renewal. If it gets an error it jumps to send-bad-email which sends me a bad email and ends everything otherwise it launches the send-good-email batch file:

mailsend1.20b.exe -smtp mail.mydomain.com -to webmaster@mydomain.com -from no-reply@mydomain.com -sub "[Cert] success renewing certificate!" -M "There was no problem renewing the public website SSL certificate. This is an automated message. Yay!"
call convert-crt-to-pfx-certificate.bat
call import-cert-into-store.bat
call nf.bat
call rebind.bat

This sends me a success email and then calls the remaining batch files to complete the process. For completeness the send-bad-email is here:

mailsend1.20b.exe -smtp mail.mydomain.com -to webmaster@mydomain.com -from no-reply@mydomain.com -sub "[Cert] error renewing certificate!" -M "There was a problem renewing the public website SSL certificate. This is an automated message. Fix it ASAP!"

First we convert the cert to the required pfx format. The text file exportKey.txt is a single line that holds the Windows export password which is NOT the same as your cert hash:

for /f %%a in (exportKey.txt) do openssl pkcs12 -export -out mycert.pfx -inkey mydomain.key -in mydomain.crt -password pass:%%a

Then we import the new cert into the store. I use the built-in WebHosting store. You can see a list of store names using PowerShell (runas Administrator!):

PS C:\> Import-Module WebAdministration
PS C:\> dir cert:LocalMachine
Name : TrustedPublisher
Name : ClientAuthIssuer
Name : Remote Desktop
Name : Root
Name : TrustedDevices
Name : SPC
Name : CA
Name : REQUEST
Name : AuthRoot
Name : WebHosting
Name : TrustedPeople
Name : My
Name : SmartCardRoot
Name : Trust
Name : Disallowed

The batch file to import the new cert first deletes the old cert, then imports the new cert, then assigns the new cert a “friendly name”. Note the delay between commands, I found they were needed or I got random errors:

rem MUST use "WebHosting" without a space!!
rem the choice command is used for delay between commands.
certutil -delstore "WebHosting" "*.mydomain.com"
choice /t 10 /d y >nul
for /f %%a in (exportKey.txt) do certutil -f -p "%%a" -importpfx "WebHosting" "mycert.pfx"
choice /t 15 /d y >nul
certutil -repairstore "WebHosting" "www.mydomain.com" "change-friendly-name.inf"

The *.mydomain.com is critical and must start with an asterisk and match the “friendly name” in the .inf file. You can use a different friendly name as long as they all match in all the files. The choice command is set to not require user interaction. The certutil command to add the friendly name requires a separate text file with the .inf extension:

[Version]
Signature = "$Windows NT$"
[Properties]
11 = "{text}*.mydomain.com"

(That is an eleven above which is a special code that targets the friendly name in the cert. Yep…Windows…) The final step is to rebind your websites to the updated certificate. The code is as follows:

rem nf.bat
rem grab certificate hash
for /f "tokens=* USEBACKQ" %%x in (`nf1.bat`) do set CSH=%%x
rem grab application id
for /f "tokens=* USEBACKQ" %%x in (`nf2.bat`) do set AID=%%x
rem update website binding for mydomain.com
netsh http add sslcert certstorename=WebHosting hostnameport=mydomain.com:443 certhash=%CSH:~31% appid=%AID:~31%
%windir%\system32\inetsrv\appcmd set site /site.name:"YourWebsiteName" /+bindings.[protocol='https',bindingInformation='*:443:domain.com']
rem update website binding for www.mydomain.com
netsh http add sslcert certstorename=WebHosting hostnameport=www.mydomain.com:443 certhash=%CSH:~31% appid=%AID:~31%
%windir%\system32\inetsrv\appcmd set site /site.name:"YourWebsiteName" /+bindings.[protocol='https',bindingInformation='*:443:www.domain.com']
set AID=
set CSH=

{Those use a “backtick” [grave accent] in the parentheses above.) The netsh command requires the cert hash and the app ID for matching to the correct website. The “YourWebsiteName” is the name in IIS MMC under the “Sites” dropdown in the left column. The nf1.bat and nf2.bat batch files grab those pieces of data and output them so the calling batch file can grab them and set environment variables:

rem nf1.bat
rem grab certificate hash
netsh http show sslcert hostnameport=mydomain.com:443 | find "Certificate Hash"
rem nf2.bat
rem grab application id
netsh http show sslcert hostnameport=mydomain.com:443 | find "Application ID"

Note that you might find from Googling the netsh command that the examples say to use the ipport parameter. Actually, you either use the ipport parameter or the hostnameport parameter, not both, or you get an error. I can use the same domain above because it is the same cert for both the domain and subdomain.

The next code grabs the old cert fingerprint and the new cert fingerprint and uses appcmd to rebind your website to the new cert:

@echo off
rem rebind.bat
for /f "tokens=* USEBACKQ" %%x in (`nf1.bat`) do set CSH=%%x
for /f "tokens=* USEBACKQ" %%x in (`sl1.bat`) do set NWC=%%x
set CSH=%CSH:~31%
set NWC=%NWC:~17%
set NWC=%NWC::=%
\windows\system32\inetsrv\appcmd renew binding /oldcert:%CSH% /newcert:%NWC%
set NWC=
set CSH=
@echo off
rem sl1.bat
openssl x509 -in mydomain.crt -fingerprint -noout

The SET commands are used to strip off unneeded text such as those pesky colons between bytes. To test if your cert has updated (verify the expiration date) you can use any of a number of online checkers, the quickest might be Why No Padlock but the choice is yours. Done! I hope this helps someone, I sure wish I had all of these commands in one post when I started my journey…

4 Likes

Good article, a minor correction though - from version 0.27 you can export the certificate to PFX directly, eliminating the need to employ OpenSSL for that. The command line would be as normal for the issuance/renewal, but with --export-pfx YourPassword added.

NB: I didn’t forget about those codes and they should be configurable in the next release, but my schedule is a bit full at the moment (it works locally for me though, just needs some final touches) :slight_smile:

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.