Some recent experiences with using the PI Web API highlighted to me the benefits of using CA issued (read non self-signed) SSL certificates, even on a development system. One of the most obvious to me was how to properly handle certificate errors when calling the PI Web API from custom code.

 

In the Windows domain world, it's possible to create a domain certificate, which is an internal certificate that does not have to be issued by an external certification authority (CA). If your Windows domain has a server that acts as a CA, you can create a domain certificate. For use within your organisation this can be an acceptable option for securing a PI Web API instance. See the Microsoft article Create a Domain Server Certificate in IIS 7 for more information on this, as well as Alex Duhig's excellent blog post How to Create a Certificate Using Your Enterprise CA for PI Web-based Products.

 

The purpose of this blog post is to discuss obtaining and using an external CA issued certificate for PI Web API. A recent forum post Self Signed Certificate for public IP address by another PI Square community member prompted this exercise.

 

I recently discovered Let's Encrypt, a free and automated Certificate Authority. While there are some limitations in the certificates provided (90 day expiry), I found that this CA more than met my needs for a personal website project, and now for my personal Azure VM based PI Web API development system. Let's Encrypt recommends using a client tool called Certbot for generating and renewing certificates. This is primarily a Linux based tool (which was fine for my personal web server), but poses a bit of a problem for a Windows host. Fortunately, there are a number of Windows clients that can also be used. I used a PowerShell based client called ACMESharp, and this will be the tool discussed in this blog post.

 

How to obtain a certificate from Let's Encrypt

 

Install the ACME client

Firstly, you will need to install your ACME client. To install ACMESharp, the recommended method is to install from the PowerShell Gallery. You will need to be running PowerShell 5.0 or higher to use this. Open PowerShell as administrator, and enter the following command:

 

Install-Module -Name ACMESharp

 

You may be prompted to install and import the NuGet provider - accept this to continue:

 

 

You will probably also receive a warning about installing modules from an untrusted repository:

 

 

Select 'S' to suspend the operation, then change the installation policy to trust the PowerShell Gallery repository:

 

Set-PSRepository -Name PSGallery -InstallationPolicy Trusted

 

Now you can continue to install the ACMESharp client without further warnings.

 

 

Next, make sure the ACMESharp PowerShell module is loaded:

 

Import-Module ACMESharp

 

Pre-requisite steps to requesting a certificate

The following procedures are taken from the ACMESharp Quick Start wiki.

 

1. Start by creating a vault to store your Certificate(s) and related artefacts. If you are running the script as Administrator, this will be created in a system-wide location, typically in C:\ProgramData\ACMESharp\sysVault.

 

Initialize-ACMEVault

 

2. Register yourself with the Let's Encrypt CA, by providing a method of contact (email address), and accept their terms of service:

 

New-ACMERegistration -Contacts mailto:somebody@example.org -AcceptTos

 

This cmdlet will output information similar to the following:

 

 

3. Submit a DNS domain name that you want to secure with a PKI certificate. Obviously this should be a domain name that you own or have control of. In my case, as I already have a certificate for my main domain, I want a certificate for a specific host (effectively a subdomain):

 

New-ACMEIdentifier -Dns myserver.example.com -Alias myserver

 

 

4. Handle the Challenge to prove domain ownership. The Let's Encrypt server will respond with one or more Challenges that you need to complete in order to prove that you are the owner and operator of the DNS domain name you submitted. There are a few different choices for how you can choose to handle this. You can do this either by managing DNS records under this domain name, or by serving up HTTP content from a Web server that this domain name points to. I chose to handle the HTTP Challenge manually:

 

 

You then need to create a specific file in your web server for the Challenge to complete successfully. I found that the instructions in the Quick Start didn't quite match what I was seeing, and so you will need to complete the following command in order to get the instructions for the Challenge to complete:

 

(Update-ACMEIdentifier jmpi -ChallengeType http-01).Challenges | Where-Object {$_.Type -eq "http-01"}

 

 

Take note of the instructions to complete the challenge. When creating the file structure in the web root, Windows won't allow you to create a directory called .well-known, so when you name the new directory, add a period after the name as well - .well-known. Windows will then automatically strip off the trailing period and leave the leading period intact. The other step you need to take is to allow IIS to serve an extensionless file. Create a new web.config file in your "/.well-known/acme-challenge" directory, and add the following contents to it:

 

<?xml version="1.0" encoding="UTF-8"?>
 <configuration>
     <system.webServer>
         <staticContent>
             <mimeMap fileExtension="." mimeType="text/plain" />
         </staticContent>
     </system.webServer>
 </configuration>

 

Before continuing to the next step, test the "HTTP URL" that was specified in the results of the previous PowerShell command, to make sure the contents of the challenge file are rendered in your browser.

 

5. Now that you've handled the Challenge, you need to let the Let's Encrypt server know so that it can perform the verification step.Having used the HTTP Challenge, use the following cmdlet to submit your Challenge response:

 

Submit-ACMEChallenge jmpi-ChallengeType http-01

 

 

You can then check the status again with the command

 

(Update-ACMEIdentifier jmpi -ChallengeType http-01).Challenges | Where-Object {$_.Type -eq "http-01"}

 

You should the see the following result indicating that Challenge was successful:

 

 

6. You now create a new PKI certificate request, and then submit it for issuance by the Let's Encrypt CA:

 

 

7. You're almost ready to install your certificate. There are some additional steps you can take to export the certificate and related assets from your vault so that you can then manually install them where needed.

 

Export Private Key:

 

Export Certificate Signing Request:

 

Export Certificate issued by Let's Encrypt:

 

Export Issuer Certificate:

Note, if you get the error 'no issuer certificates found' you need to install them. This needs to be done only once on your machine. This can be done with:

 

Export PKCS#12 (PFX) Archive:

This is the format used by Windows and IIS.

 

Now that you have these various artefacts available, it's time to install the actual certificate into the certificate store.

 

8. Open Internet Information Services Manager, and select the server node

 

Double click the Server Certificates icon

Under the Actions pane on the right hand side of the window, click the Import option

 

Locate your .pfx file created in Step 7 and select it for Import. If you created a password for the certificate file, you will need to specify that too:

 

You should then see your new certificate

 

9. Now that your new certificate is installed, the final step is to re-run the PI Web API Admin Utility to select your CA issued certificate.

 

Click the Change button, and select Yes when presented with the following dialog:

 

Select the new certificate:

 

That's it! Complete the steps for the PI Web API Admin Utility and you're all set. You can test the new certificate by navigating to the public address of your PI Web API instance - no more certificate errors

 

 

Final Notes and Thoughts

Make sure you read and understand the T&C's of Let's Encrypt, and understand what certificate types they do and don't provide. Obtaining and using certificates from this CA may not be for everyone. Certificates issued by Let's Encrypt are only valid for a maximum period of 90 days, so you will need to renew your certificates within that time. On my Linux based web server, I've automated this by creating a cron job to run certbot to automatically renew the certificate every 60 or so days. For Windows based hosts, unfortunately the ACMESharp utility does not yet have support for automatic renewals (see the FAQ), so you will need to manage this process manually. I am in the process of testing and implementing the script from a blog post at https://marc.durdin.net/2017/02/lets-encrypt-on-windows-redux/ to handle this.

 

There are other Windows ACME clients that may better handle the initial certificate generation and subsequent renewal for your requirements - Certify looks to be possibly a good option, and it uses the ACMESharp library under the hood.

 

For now, I'm enjoying certificate error-free access to my development PI Web API instance.