Tuesday, October 8, 2019

How to create SSL certificate including a self-signed root certificate using Powershell

This blog post gives an overview of the steps needed to create self-signed certificate that can be used for SSL communication including a self-signed root certificate.

Context:
I wanted to have a HTTPS server that uses a wildcard certificate that was signed by my own (self-signed) root certificate. The HTTPS server is a small executable that is home made and running under the current user. The goal was to use the Chrome browser to fetch a page from the HTTP over TLS without the browser complaining about security issues.

I thought this would be a straight forward exercise. However it cost me some effort to get the right information and to get things working. I started out using the "makecert" tool. However I was unable to add a Certificate Alternative Name entry to the certificate. This resulted in an error from Chrome.

The overcome this I switched to PowerShell and finally got things to work. The credit goes to the blog entry posted on the InfiniteLoop.IO site: Powershell – Self-signed Certificate via Self-signed Root CA


Here are the steps:

Step 1 – Create the root certificate

The following code creates the certificate and installs it in the local machine certificate store. The code needs to run from with administrator rights.

$params = @{
  DnsName = "myroot"
  KeyLength = 2048
  KeyAlgorithm = 'RSA'
  HashAlgorithm = 'SHA256'
  KeyExportPolicy = 'Exportable'
  NotAfter = (Get-Date).AddYears(5)
  CertStoreLocation = 'Cert:\LocalMachine\My'
  KeyUsage = 'CertSign','CRLSign' #fixes invalid cert error
}
$rootCA = New-SelfSignedCertificate @params

Step 2 – Create the server cert signed by the new root

In this step we are going the create the server certificate that is signed by our root certificate. We first need to fetch the root certificate from the certificate store.

$rootCA = ( Get-ChildItem -Path cert:\LocalMachine\My | Where-Object { $_.Subject -Match "myroot" } )

After having a handle on the root certificate we can create the server certificate.

$params = @{
  DnsName = "*.mysite.test.com"
  Signer = $rootCA
  KeyLength = 2048
  KeyAlgorithm = 'RSA'
  HashAlgorithm = 'SHA256'
  KeyExportPolicy = 'Exportable'
  NotAfter = (Get-date).AddYears(2)
  CertStoreLocation = 'Cert:\LocalMachine\My'
}

$vpnCert = New-SelfSignedCertificate @params

Step 3 – Add self-signed root to trusted root certificate store of current windows client

The machine on which the client (browser) is running needs to validate the certificate provided by the server. For this it needs our root certificate.

Again we first need to fetch the root certificate

$rootCA = ( Get-ChildItem -Path cert:\LocalMachine\My | Where-Object { $_.Subject -Match "myroot" } )

Next we export the certificate to a file so we can install it on the client machine(s)

Export-Certificate -Cert $rootCA -FilePath ".\myroot.cer"

Import on the client machine(s). The file contains only the public key
Import-Certificate -CertStoreLocation 'Cert:\LocalMachine\Root' -FilePath ".\myroot.cer"


Step 4 – Export server certificate as .pfx

The server certificate can be exported to file so we can store it in a safe place or to import if we want to run the server on a different machine. Please fill in your own password.

Export-PfxCertificate -Cert $vpnCert -FilePath '.\server.pfx' -Password (ConvertTo-SecureString -AsPlainText 'password' -Force)

The pfx contains the public and private key so please store in a safe place.


Step 5 – Check Result and update access rights

You can check the results by starting the certlm.msc (Certificates Local Machine).

For my setup we need to give access rights to the certificate private key for the account under which the HTTP server is running. This can be done by right clicking the certificate (in certlm.msc) => all tasks => manage private keys.