loader

The One Weird Gotcha of Generating Self-signed Public Keypairs with OpenSSL

Generating self-signed public keypairs is a staple of many involved in DevOps, system admin, network administration and other roles. They are useful for testing encryption configuration and even providing some level of encryption security, mainly for internal organisational HTTPS connections. The most common case for self-signed certificates is for use with Web Servers, but many other applications such as Elasticsearch, which use RESTful APIs, will benefit too.

What is self-signing?

When a web server offers a public key to an incoming client, how can the client trust it? How does it know that the web server represents the organisation it says it does? The answer to that is a Chain of Trust.

The Chain of Trust starts with a few organisations, called Root Certificate Authorities, whose details are baked-in to most browsers and other clients interacting with web services. They include Sectigo (Comodo SSL), DigiCert, Symantec, Thawte and others. In turn, they have authorised a larger number of Intermediate Certificate Authorities (CAs) who may then authorise other CAs or certificates for individual web servers.

When you visit an encrypted website, your browser checks the supplied certificate and based on the knowledge it has of the chain of Certificate Authorities, it will accept or reject that certificate. This process works well, but there are some drawbacks for those who need a certificate for internal or development purposes:

  • It costs money
  • It must be renewed – certificates typically don’t last longer than one or two years and cost more money for each renewal
  • It takes time

The usual process

Usually, the process for obtaining such a certificate follows these steps:

  1. Create a public/private keypair for your website
  2. Create a Certificate Signing Request (CSR) file
  3. Pay fees and send the CSR file to an Intermediate CA for approval
  4. Wait for the Intermediate CA to approve your request
  5. The Intermediate CA sends back a signed public key certificate
  6. Install the Private key and the Signed Public key in your webserver

The self-signing process

You can remove a lot of the hassle of getting a web server certificate by eliminating time and cost restrictions: you can create your own. This eliminates steps 3, 4 and 5. Here is a comparison of the processes:

Your own chain of trust

The self-signing process is more efficient but comes at a price: the only person that will trust this certificate is yourself. In fact, browsers will produce a warning each time a self-signed certificate is encountered, with most of them offering the option to allow it anyway, along with the associated risks. While production, internet facing systems will find this situation intolerable, it’s acceptable in some circumstances for test and development purposes

Step-by-step to Self-signing

To demonstrate, we will use openssl, available on Linux and other Linux-like systems. The process is a little more involved than the diagram above represents, so these instructions are more detailed.

1. Create an openssl.conf file containing certificate values. This file will have information that certificates need to be valid. This includes:

    1. A Distinguished Name (DN) of your web server’s identity. Such as the web server’s Country, State or Province, Location (e.g. a city), Organisation, Organisational Units (such as departments) and the host name itself.
    2. Alternative names that the certificate might use. For example, you may install this certificate on a web server called server1, but you wish the certificate to be valid for other names it is used under, including localhost or 127.0.0.1 or server1.somedomain.com.

Contents of the openssl.conf file may look like this example:

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
C = AU
ST = Victoria
L = Melbourne
O = AAA Business Name
OU = Development
CN = server1

[v3_req]
subjectAltName = @alt_names

[alt_names]
IP.1 = 127.0.0.1
IP.2 = 10.20.30.40
DNS.1 = localhost
DNS.2 = server1.aaabiz.tld

2. Create a Private key. Generate a private key for your webserver. The number of bits here determines key strength and should be more than 2048:

openssl genrsa -out private.key 4096

3. Generate the CSR file. Using openssl, you can generate a CSR file. In this example, the CSR file will be call request.csr:

openssl req -new -key private.key -out request.csr -config openssl.conf

Note that the private key (private.key) and  openssl.conf files are referenced here.

4. Create an x509 Extensions File. And here’s the weird gotcha! You would assume that since you have given alternative names in your original openssl.conf file, that this would ensure your certificate knows the names you would like your server to be known. But alas, this is a bug with openssl.  If you read the x509(1) Linux manual page, under the Bugs heading, it states:

Extensions in certificates are not transferred to certificate requests and vice versa.

This means that we must tell openssl at the signing stage to include the alternative names that we have already given! Despite being a little inconvenient, it is relatively straightforward. We create an x509 extensions file (in this case, we’ll call it ext-x509.conf) containing the alternative hostnames only. For example:

[v3_ca]
subjectAltName = @alt_names

[alt_names]
IP.1 = 127.0.0.1
IP.2 = 10.20.30.40
DNS.1 = localhost
DNS.2 = server1.aaabiz.tld

Note here, that the [alt_names] section is a copy of what exists in the openssl.conf file. Note too that the name of the configuration in the ext-x509.conf file is v3_ca.

Note also that if a change in the alternative name list occurs, this will need to be updated in both the openssl.conf file as well as the ext-x509.conf file too.

5. Sign the CSR file with your own public key. Using openssl’s x509 capability, you can sign your own request with your private key you generated earlier. Most of the time a one-year (365 day) validity is sufficient. The resulting public key will be placed in the file public.crt.

openssl x509 -req -days 365 -in request.csr -signkey private.key -out public.crt -extensions v3_ca -extfile ext-x509.conf

Note here the references to

  • the CSR file request.csr
  • the private key file private.key
  • the x509 extensions file ext-x509.conf; and
  • the heading within that file, v3_ca.

6. Deploy and enjoy! This process will leave you with two important files:

    1. Your private key in private.key; and
    2. Your public key in public.crt

This will be all your web server will need to start using self-signed HTTPS!

I hope that this post was informative and useful. Skillfield regularly posts informative and interesting content on its blog, and runs free educational webinars, all focussed around solving problems in cybersecurity! Follow us on LinkedIn for more details!

By Damian Wernert