DevOps: Automate Certificate Process for IIS

Phylypo Tum
11 min readOct 4, 2021

--

This tutorial is part of the series on server certificates to secure websites with HTTPS.

  1. Here, we look at how to automate the creation of certificates from your company Certificate Authority (CA) on Windows OS.
  2. Then we look at how to use free issuer “Let’s Encrypt” to generate the certificate on Windows and Linux.
  3. Lastly, we will look into setting up a certificate on Kubernetes.

Introduction

HTTPS is an important part of having secured communication between your site and the users. This Public Key Infrastructure (PKI) framework allows the encryption of the communication via the public and private keys. The server public key which is signed by an authoritative certificate authority (CA) is trusted by the browser. This trust system will ensure that no one else can decrypt the data except two parties that are communicating.

As we set up more and more web services on Windows IIS with HTTPS, automation becomes a time saving for DevOps to automate this process. This page detail the process to automate the digital certificate signing process using your company CA. The automation involves using Ansible to generate a certificate signing request (CSR) and get the CA to sign the certificate then import it into IIS to bind a website as HTTPS.

Architecture and tools

  1. Ansible server (we use AWX running on Linux)
  2. Windows Server with IIS for web hosting
  3. Certificate Authority on Windows server

First, we will look at the manual process to create a certificate request to get it signed and then complete the certificate import and then create a binding to enable HTTPS. After that, we will look at how to automate each process using Ansible.

Assumptions

  1. You have your own Certificate Authority server.
  2. You have Ansible setup for automation. The ansible code will only cover the portion related to the task.

Manual Process to Setup Https

We will start with a summary of what the process involves.

  1. From the webserver, we first need to create a certificate signing request (CSR). This process involves creating a private key and generating CSR file with information about the server such as a common name that the CA can sign to create a certificate. This process allows only the server with the corresponding private key to use the certificate.
  2. Take the CSR file generated and use the CA to sign and generate the certificate. The CA will output a CER file.
  3. From the web server, we can import the CA file in the complete certificate request function. This will import to the server local store.
  4. Now we can set up the website on IIS to bind as HTTPS using this certificate.

Now let us look into the detail of each step.

1. Creating CSR

From the webserver, in Server Management, double click Server Certificates

Then select “Create Certificate Request” on the Actions tab and fill in the information.

Click Next, then select Cryptographic Service Provider such as change Bit length to 2048 and click Next.

Then save the file and click Finish.

Example output file:

-----BEGIN NEW CERTIFICATE REQUEST-----
MIIEOzCCAyMCAQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQswCQYDVQQH
...
fbceNKxOOlIyDFYMe36tzf/+EnkjDOFiZUR2kp5KVZquPflXDPIElhhbSgo2jaO8
nlq6Vewcu1HxLlQSLLoGOIN9eXX/qRoNcl+H+HgsOw==
-----END NEW CERTIFICATE REQUEST-----

2. Sign the CSR and Generate a Certificate in CA

In these steps, log in to your Certificate Authority (CA) server and import your CSR file that was created from the previous step.

First, open the CA tool from the Server Management, select the Tools menu, and then select “Certification Authority”.

From the server icon, right-click and select “All Tasks”, then select “Submit new request…”.

Then select the file created from the previous step.

Then from the “Pending Requests” folder, right-click the item, select “All Tasks” then select “Issue”.

Now the request will be moved to “Issued Certificates”. You can double-click to verify the certificate information.

Now export the certificate for IIS by right-clicking on the certificate, then selecting “All Tasks” and “Export Binary Data…”.

Now select “Binary Certificate” from the Column that contains binary data, and select “Save binary data to a file” for Export options.

Then select the directory and file to save.

3. Complete Certificate request in IIS

Now go back to the webserver and complete the certificate request. From the Server Certificate, click on “Complete Certificate Requests…”. Then choose the file from the previous step, and enter the Friendly name that will be shown in the certificate personal store, and click OK.

Now you should see the new certificate, you are ready for the binding with your website.

4. Bind the certificate to HTTPS

From your site, select Binding…, then click “Add…”. On the “Add Site Binding” screen change the Type to “https”, then add hostname. Finally, select the newly created certificate and click OK.

Now you should be able to have the site configured for HTTPS.

DevOps Automation

Now we cover the automation part with Ansible. We will divide into 4 different steps:

  1. Automate CSR Request
  2. Automate Generation of File and Script for CA
  3. Automate Signing and Generating Certificate
  4. Automate Importing and Binding the Certificate in IIS

With each of the steps, we want to check the state that if the certificate already exists it does not regenerate. This approach is all based on the existing share drive data that indicate if the CSR or CER file is needed.

Let’s us go into detail.

1. Automate CSR Request

We will be using certreq.exe to generate the CSR. The command takes in a conf file and the name of the output CSR file.

certreq.exe -new reqconfig.inf cert_csr.txt

A simple example of reqconfig.inf:

[NewRequest]
; At least one value must be set in this section
subject = "CN=test.tovnah.com"
Exportable = TRUE
KeyLength = 2048
KeySpec = 1
KeyUsage = 0xf0
MachineKeySet = TRUE

When you run the command you should see the output file.

Example of the output CSR file:

-----BEGIN NEW CERTIFICATE REQUEST-----
MIIDpTCCAo0CAQAwGjEYMBYGA1UEAwwPdGVzdC50b3ZuYWguY29tMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApjsXV8PicJFrbsCgCbNai6DaPmUmwFjm
<cut for brevity>
gzNZn7KBFxwIW8xM+mLr1D7sLNTqodSlo0yMyXP+9BhthwIpblktLutDblh7b745
kdETiEodJa2qk2dIZTpNfgUs7kmB7b6AVw==
-----END NEW CERTIFICATE REQUEST-----

To automate this, in Ansible we use the win_command module in our task. It looks like this:

- name: 'Generate Certificate Signing Request (CSR)'
win_command: 'certreq -new -f {{cert_req_inf}} {{csr_file}}'
register: cmdout
become: yes
become_method: runas
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
vars:
ansible_become_user: '{{admin_usr}}'
ansible_become_pass: '{{admin_passwd}}'

Notice that we run runas to become the user that has permission to generate the CSR.

2. Automation Generating File and Script for CA

The request file config and the output CSR file are put in the share directory so it can be accessed from the webserver and the CA server. The folder structure is set up to have one server request per folder.

The cert_req_inf file is generated certificate to match its hostname. To accommodate this we use the win_template module.

- name: Create cert_req_inf file from template
win_template:
src: 'cert_req_inf.j2'
dest: '{{cert_req_inf_file_path}}'
become: yes
become_method: runas
become_flags: logon_type=new_credentials logon_flags=netcredentials_only
vars:
ansible_become_user: '{{admin_usr}}'
ansible_become_pass: '{{admin_passwd}}'

The template file will have the full content just with variables it can substitute to match the specific hostname. The template file will look something like this:

[NewRequest]
Subject = "CN={{ssl_cert_subject}}" ; website hostname
Exportable = TRUE ; TRUE = Private key is exportable
KeyLength = 2048 ; Valid key sizes: 1024, 2048, 4096, 8192, 16384
KeySpec = 1 ; Key Exchange – Required for encryption
KeyUsage = 0xA0 ; Digital Signature, Key Encipherment
MachineKeySet = True
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
RequestType = PKCS10 ; or CMC.
[EnhancedKeyUsageExtension]
; If you are using an enterprise CA the EnhancedKeyUsageExtension section can be omitted
OID=1.3.6.1.5.5.7.3.1 ; Server Authentication
OID=1.3.6.1.5.5.7.3.2 ; Client Authentication
[Extensions]
; If your client operating system is Windows Server 2008, Windows Server 2008 R2, Windows Vista, or Windows 7
; SANs can be included in the Extensions section by using the following text format. Note 2.5.29.17 is the OID for a SAN extension.
2.5.29.17 = "{text}"
_continue_ = "dns={{ssl_cert_subject}}&"
_continue_ = "dns={{ssl_cert_subject}}.mycompany.com"
[RequestAttributes]
; If your client operating system is Windows Server 2003, Windows Server 2003 R2, or Windows XP
; and you are using a standalone CA, SANs can be included in the RequestAttributes
; section by using the following text format.
"SAN="dns={{ssl_cert_subject}}&dns={{ssl_cert_subject}}.mycompany.com"
; Multiple alternative names must be separated by an ampersand (&).
CertificateTemplate = MyWebServerTemplate ; Modify for your environment by using the LDAP common name of the template.

The same is needed to generate the batch command file that will be executed at the CA server to sign and generate the certificate.

- name: Create sign_req.bat file from template
win_template:
src: 'sign_req.bat.j2'
dest: '{{cert_bat_file_path}}'
become: yes
become_method: runas
<same options as earlier>

The sign_req.bat will be using the same certreq.exe command with submit option. Make sure you get the config option correct, or you will get the prompt that needs to click which will pause and fail the automation. Test your script and ensure you don’t get the prompt.

Here is an example output of the sign_req.bat file:

certreq ^
-config "<ca_cert_server\mycompany-ca>" ^
-attrib "CertificateTemplate:MyWebServerTemplate" ^
-submit "new_ssl_cert_req.csr" "my_cert.cer"

3. Automate Signing and Generating Certificate

After the batch file is generated, we just need to run that file in the CA server. Since the host that we have been running is on the webserver, we need to give WinRm access to all of the web servers. If you need to automate a lot of the web servers, this might pose more security risks. One approach to limit the risk is by giving WinRm access to the CA server to only the ansible server and you can harden the firewall accordingly. To run a remote connection from the Ansible server, we use the delete_to command.

- import_tasks: cert_gen.yml
delegate_to: localhost

The trick is that the cert_gen task will use delegate_to to connect to the CA server. So from localhost, which is the Ansible server, we delegate_to CA server to a batch file. In that case, the Ansible server will remote connect to CA server using WinRM and execute the batch file. The Ansible code in the cert_gen task looks like this

# tasks/cert_gen.yml  
- name: 'Execute Submit_req.bat to sign and create certificate'
win_command: '{{cert_bat_file_path}}'
become: yes
become_method: runas
<similar options as earlier>
delegate_to: '{{ca_cert_server}}'

4. Automate Importing and Binding the Certificate in IIS

We can import the certificate using the win_certificate_store module. The Ansible code is as follow:

- name: 'Importing Certificate:{{cert_cer_file_path}}'
win_certificate_store:
path: '{{cert_cer_file_path}}'
store_location: LocalMachine
key_storage: machine
key_exportable: yes
state: present
become: yes
become_method: runas
<same options as early>
register: cert_import

Again, the cert_cer_file_path is the cer file in the share drive that the CA and webserver have access to. The runas option is important since that user has access to the share drive.

Note that we register to cert_import variable. This is important for us to be able to get the thumbprint for the next few steps.

We also need an extra step to repair the certificate from the import process.

- name: 'Repair import cert in case private key is missing'
win_command: 'certutil -repairstore my "{{cert_import.thumbprints[0]}}"'
become: yes
become_method: runas
<same options as early>
when: cert_import.changed == true

Next is to bind the HTTPS to this certificate thumbprint with win_iis_webbinding module.

- name: 'Configure https binding'
win_iis_webbinding:
name: '<my hostname>'
protocol: 'https'
host_header: '<my hostname>'
port: '443'
state: 'present'
certificate_hash: '{{cert_import.thumbprints[0]}}'

Other steps may include the DNS update win_dns_record module as you create a new DNS A-record for the website.

Hope this will be helpful in your process to automate certificate creating and binding. Let me know your feedback below.

Next, see how we can use a free certificate issue “Let’s Encrypt” to generate certificates for us.

Other Format and Tools

We only cover the IIS setup above. A similar process can apply to set up the certificate on different applications and OS. But you need the right format that each application is expecting.

PEM Encoded

Some applications expect two files as input: a public key and a private key in PEM format. PEM or Privacy Enhanced Mail is a Base64-encoded of the binary formatted certificate or DER. To take the certificate in our IIS and convert it to PEM format we can use openssl utility that is available in many OS.

To produce a format that OpenSSL needs, you will have to export the certificate into a pfx (Personal Information Exchange) format that has a public and a private key. From the Windows Certificate plugins in MMC, select the desired certificate and select “All Tasks”, then “Export…”. In the Certificate Export Wizard, select the “Yes, export the private key” option and enter the password when prompted.

This password is needed by the OpenSSL command below when the tool try to open the pfx file.

openssl pkcs12 -in c:\mycert.pfx -clcerts -nokeys -out c:\mycert.pemopenssl pkcs12 -in c:\mycert.pfx -clcerts -nocerts -out c:\mykey.pem -nodes

These commands will ask for the password and out the file mycert.pem which is the public key and mykey.pem which is the private key.

Other applications may require one PEM encoded file that contains a private and a public key. To produce the file from the pfx format above, run:

openssl -in c:\mycert.pfx -out c:\mycert.pem
Enter Import Password:
Enter PEM pass phrase:
Verifying — Enter PEM pass phrase:

This will first prompt for “Import Password” to open the pfx file, then it will prompt for “PEM pass phrase” to be set.

Java Keystore

For applications that use Java such as Tomcat, you may need a different tool to convert your certificate. Java has a keytool that can store the certificate and its key. Some options in Tomcat can import crt and pem format directly.

Here is the keytool command that imports the certificate and the key into a key store.

keytool -importkeystore -deststorepass <DEST_STORE_PASSWORD>
-destkeypass <DEST_KEY_PASSWORD>
-destkeystore <KEYSTORE_FILENAME>
-srckeystore <PKCS12_KEYSTORE_FILENAME> -srcstoretype PKCS12
-srcstorepass <PKCS12_KEYSTORE_PASSWORD> alias tomcat

The KEYSTORE_FILENAME defaulted under your profile directory with the name “.keystore” (ie. c:\Users\me\.keystore). Some use the convention of naming jks extension such as mykeystore.jks.

If you use the keytool to create the CSR request then you can use the keytool command with “-importcert” option to import the certificate signed by your CA. See more options using simple guide here.

In the next post, you see how you can use a free Let’s Encrypt Certificate Authority that has a complete life cycle automation.

--

--