OpenSSL is a useful tool for working with certificates, however its usage is often obscure and challenging to get right. This is a cheat sheet for common operations.

Installation

I find it easiest to work with OpenSSL at the Bash prompt. As I’m on Windows, this means using the Windows Subsystem for Linux. With this running, install at least OpenSSL v1.1.1f via the following:

sudo apt-get install -y openssl
# Check the version installed
openssl version

The file formats

When dealing with certificates, certificate requests, and private keys, you’ll generally be working with PEM format files. These are basically text files with a header and footer surrounding some base64 encoded data. The file extensions are typically .key for private keys, .csr for certificate requests, and .crt for certificates. The contents will appear similar to the below, with the text in the header and footer around the base64 data indicating the content.

-----BEGIN CERTIFICATE-----
MIIGNzCCBR+gAwIBAgIRAMXBvoYhfBEw5+K+kj/8nI4wDQYJKoZIhvcNAQELBQAw
gY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UE
    ...lines omitted for brevity...
XvKsEcO2TvY+Dcxff5jggYAdrv4IHSkA33L9zAtvFjt3NO/H3d3qxBPE2CSbBmWw
djPNIn7JiaYbhSyz+mBXcxhnPAGqDVh0NyOI+kfgsLNol8Y8QFUxlNPRZNRSt+Hf
eT6Rt49bqgXOgv8=
-----END CERTIFICATE-----

The above is for a certificate (.crt). You will also see “BEGIN CERTIFICATE REQUEST” (.csr) and “BEGIN PRIVATE KEY” (.key).

Another format you may have to work with is PKCS#12, which is typically in a .pfx file. This is a binary format for storing a certificate chain and private key in a single, encrypted file, and is often used if working with Windows or the Azure AppService to upload a certificate for a web server.

Generating self-signed certificates

For local testing, it is common to generate a “self-signed” certificate. As this isn’t signed by a known and trusted authority, it will need to be manually placed in the “Trusted Root Certification Authorities” certificate store on the computer to be used without displaying an error when browsing to a site using it. This is still useful in development.

The quickest way to generate a self-signed certificate for example.com is via the below.

openssl req -newkey rsa:2048 -sha256 -nodes -x509 -days 365 -out example.crt -keyout example.key \
    -subj "/C=US/ST=Washington/L=Seattle/O=Example Inc/CN=example.com"

Breaking down the command line switches here:

  • req: This is a request for a new certificate
  • -newkey rsa:2048: Use RSA encryption with a 2048-bit key length. (Usually the default, but being explicit).
  • -sha256: Use SHA256 as the digest algorithm. (Again, usually the default).
  • -nodes: No DES. Do not protect the output files (including the private key) with a password.
  • -x509: Generate a (self-signed) certificate, not a request for a certificate.
  • -days 365: How long the certificate should be valid for.
  • -out <filename>: The file to write the actual certificate to. (Does not include the private key).
  • keyout <filename>: The file to write the private key to. (Keep this safe and secret!)
  • -subj <path>: The details for the org the certificate is for. “CN” is the “Common Name”, and the URL of the site.

If you leave off the -subj argument, you will be prompted at the command-line to provide these values.

Extensions

Typically there are two “extensions” specified in certificates. The first is the “Subject Alternate Name”, which is a list of other valid URLs this certificate covers. For example, typically a site will be accessible via both the “naked” domain name (example.com) and the www-prefixed version (www.example.com). You aren’t just limited to domain names here. You can also put names such as localhost, or even IP addresses such as 192.168.1.14.

The other common extension is the intended usage of the certificate. For web certificates, this is commonly set to “client authentication” and “server authentication”.

To specify all of this on the command-line, rather than in a config file, use the -addext flag. For example, to make the certificate valid for www.example.com, localhost, and 127.0.0.1, use:

openssl req -newkey rsa:2048 -sha256 -nodes -x509 -days 365 -out example.crt -keyout example.key \
    -subj "/C=US/ST=Washington/L=Seattle/O=Example Inc/CN=www.example.com" \
    -addext 'subjectAltName=DNS:www.example.com,DNS:localhost,IP:127.0.0.1' \
    -addext "extendedKeyUsage=serverAuth,clientAuth"

Installing the self-signed certificate as trusted

To install the certificate on the machine so it is trusted (on Windows):

  1. In Chrome or Edge, navigate to chrome://settings
  2. In the search box type manage certificates
  3. Click the link to open the dialog and go to the Trusted Root Certification Authorities tab
  4. Click Import, then Next, then browse to the .crt file created above.

Using the certificate on a web site

How you bind the certificate to an actual web site depends on the web server being used. If using ASP.NET Core and it’s provided “Kestrel” web server, then first generate a password protected .pfx file containing the certificate and private key. For the above example files, this command would be:

# Convert into a triple-DES password protected pfx file including the private key (you be prompted for a password)
openssl pkcs12 -export -out example.pfx -inkey example.key -in example.crt -des3 -password

In your appSettings.Development.json (or secrets.json) you would then bind this to the site with:

  "Kestrel": {
    "Certificates": {
      "Default": {
        "Path": "/somewhere/example.pfx",
        "Password": "TopSecret!"
      }
    }
  }

Certificate requests

To provide a real SSL certificate for a web site, you must first generate a certificate request, and then provide this to a “trusted authority” to issue the certificate (which they do by signing the request). This is not too different to the above, with a couple of extra steps.

Generate a certificate request

Generating the certificate request itself is actually easier than generating a self-signed certificate. All you need to do is remove a couple of flags from the above command-line. Firstly, remove the -x509 flag, as we are not generating an actual certificate. Secondly, remove the -days 365 flag, as the certificate authority will set how many days the issued certificate is valid for. You’ll also want to change the output filename from .crt to .csr to indicate it is a request, not a certificate.

Many certificate authorities will also automatically generate the subject alternate names (including the “naked” domain if a “www” domain is requested), and set the intended usage of the certificate, so the -addext flags aren’t needed. The final command being something like:

openssl req -newkey rsa:2048 -sha256 -nodes -out example.csr -keyout example.key \
    -subj "/C=US/ST=Washington/L=Seattle/O=Example Inc/CN=www.example.com"

Again, keep the .key file safe and secret (the authority never sees the private key). The contents of the .csr file are what will be provided to the authority. (Typically the contents are cut and pasted into a web form when purchasing the certificate).

Prove domain ownership

Before any (trustworthy) authority issues your certificate, they are going to verify that you own the domain you are requesting a certificate for. Typically this is done by you either registering a DNS record in the domain to prove you own it, or via an email sent to an admin account for the domain (e.g. admin@example.com) which will contain a verification link.

Create the certificate bundle

Once the authority is happy you own the domain, they will send you an email containing a set of certificates. The primary certificate is the certificate you requested, now signed by the authority. The other certificates are the certificates in the trust chain all the way back to the root certificate. Your web server needs all the certificates in this chain, as well as the private key, to enable SSL connections.

Some web servers expect all the certificates in a bundle. This is simply a concatenation of all the text files (recall they are base64). For example, the below concatenates the certificates I got from my certificate authority (https://comodosslstore.com/). The first is my domain certificate, the next two are intermediate certificate authorities, and the last is the root certificate authority.

cat www_example_com.crt SectigoRSADomainValidationSecureServerCA.crt USERTrustRSAAAACA.crt AAACertificateServices.crt >> ssl_bundle.crt

As was done for the self-signed certificate, for use with ASP.NET Core’s Kestrel server (or with Azure AppService) this should be packaged along with the private key into a password protected .pfx file.

openssl pkcs12 -export -out example.pfx -inkey example.key -in ssl_bundle.crt -des3 -password

This is then bound to the web server via the settings as for the .pfx file for the self-signed certificate. (Or via the Azure Portal for Azure AppService - see https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate#upload-certificate-to-app-service).

Bonus material

Below are several other commands I’ve found handy when working with certificates with OpenSSL.

# To view the details of a private key
openssl rsa -text -noout -in example.key

# To view the details of a certificate request
openssl req -text -noout -in example.csr

# To view the details of a certificate
openssl x509 -text -noout -in example.crt

# To verify the private key, certificate request, and certificate match
# This extracts the public key from each file and reports the sha256 hash of it
openssl pkey -pubout -in ./example.key        | openssl sha256
openssl req  -pubkey -in ./example.csr -noout | openssl sha256
openssl x509 -pubkey -in ./example.crt -noout | openssl sha256