Ubuntu - Self-signed certificates with its own certification authority

Logo

Introduction

Even for development purposes or to test products, we want to setup HTTPS, especially if we want the packets to be encrypted over internet network.

Creating a self-signed certicicate is quite easy in the "quick and dirty way" : public certificate + private key.

$ openssl req -x509 -out VPSFRSQLPAC.crt -keyout VPSFRSQLPAC.key \
  -newkey rsa:2048 -nodes -sha256 -days 3650 \
  -subj '/CN=VPSFRSQLPAC' -extensions EXT -config <( \
  printf "[dn]\nCN=VPSFRSQLPAC\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:VPSFRSQLPAC\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

In the above example, a self-signed certificate is created for the host vpsfrsqlpac.

  • -days 3650 : 10 years (for development purposes, should be enough…).
  • VPSFRSQLPAC.key : private key.
  • VPSFRSQLPAC.crt : public certificate.

The product for which we want HTTPS is then started with the certificates, for example InfluxDB :

influxdb.toml
tls-cert = "/etc/ssl/VPSFRSQLPAC.crt"
tls-key = "/etc/ssl/VPSFRSQLPAC.key"

On the client machine, the public certificate (VPSFRSQLPAC.crt) is imported in the trusted root certification authorities certificate store.

https://vpsfrsqlpac:8086 works very well on the client until more sophisticated commands are needed and invoked, commands which then raise x509 errors :

x509: certificate signed by unknown authority

Options exist to prevent checks up to certificate authority : curl --insecure, influx --skip verify. As time goes by, we don’t want to use these options everywhere, especially in maintenance scripts.

Let’s see how to create our own tiny fictitious certificate authority (SQLPAC in this paper).

For brevity reasons in the following sections, certificate authority will often be shortened to CA.

SSL CA architecture

Creating the Certificate Authority SQLPAC

For the ficticious Certificate Authority SQLPAC, a private key sqlpac.key (2048 bytes length) is created in the directory /etc/ssl/sqlpac :

$ openssl genrsa -out /etc/ssl/sqlpac/sqlpac.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
............................................+++++
..............+++++
e is 65537 (0x010001)

To apply a password if needed, add the flag -des3 or -aes256. Password will be prompted when generating the key.

The associated public certificate sqlpac.crt is generated :

openssl req -x509 -new -nodes \
    -key /etc/ssl/sqlpac/sqlpac.key \
    -sha256 \
    -days 3650 \
    -out /etc/ssl/sqlpac/sqlpac.crt \
    -subj "/O=SQLPAC/CN=SQLPAC - Certificate authority/OU=SQLPAC - Certificate authority"
  • -x509 : request to generate a X.509 certificate.
  • -days 3650 : certificate expiration period.
  • -sha256 : SHA-256 encryption algorithm (by default SHA-1 algorithm is used, weaker from a security point of view).
  • -new : generation of a new public certificate.
  • -nodes : no passphrase.
  • -key sqlpac.key : certificate authority RSA private key, generated previously.
  • -out sqlpac.crt : certificate authority public certificate filename in output.
  • -subj "/O=SQLPAC/CN=SQLPAC - Certificate authority" : specify the organization (/O=SQLPAC) and the common name (/CN=SQLPAC - Certificate authority). When omitted, openssl prompts for them.

The common name (CN) is the name displayed in the clients certificates stores.

Deploying the CA authority public certificate on clients

The public certificate sqlpac.crt is then deployed to all clients certificates stores.

Windows

On Windows, to import the public certificate sqlpac.crt, open Microsoft Management Console (MMC) :

MMC File Add/Remove Snap-in… Certificates

MMC Snap In

Import the certificate authority public certificate (sqlpac.crt) into the "Trusted Root Certification Authorities" store from the context menu Certificates All Tasks Import… :

MMC Certificate - import MMC Certificate - import details

Ubuntu

On Ubuntu, as root, copy the CA public certificate to the directory /usr/local/share/ca-certificates :

$ cp /etc/ssl/sqlpac/sqlpac.crt /usr/local/share/ca-certificates/extra

Run update-ca-certificates :

$ update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.

Clients are ready.

Generating the private key for https://vpsfrsqlpac and the signing request

The certificate authority SQLPAC is ready. A private key for https://vpsfrsqlpac is created : VPSFRSQLPAC.key.

openssl req -new -sha256 -nodes \
     -out /etc/ssl/VPSFRSQLPAC.csr \
     -newkey rsa:2048 -keyout /etc/ssl/VPSFRSQLPAC.key \
     -subj "/O=SQLPAC/CN=VPSFRSQLPAC"
Generating a RSA private key
.............................................................+++++
.............+++++
writing new private key to '/etc/ssl/VPSFRSQLPAC.key'
-----

The option -out /etc/ssl/VPSFRSQLPAC.csr produces a signing request file (CSR - Common Signing Request). This CSR will be submitted later to SQLPAC certificate authority to get the associated public certificate signed by the CA.

The CN parameter must be the same than the hostname in the URL.
CN=VPSFRSQLPAC      https://vpsfrsqlpac

If the private key is generated by root account, do not forget to modify the permissions. By default openssl creates the key RSA file in read only mode to the used account.

$ chmod o+r VPSFRSQLPAC.key

Getting the signed public certificate

Last step, the associated public certificate VPSFRSQLPAC.crt signed by SQLPAC CA is generated.

openssl x509 -req \
    -in /etc/ssl/VPSFRSQLPAC.csr \
    -CAkey /etc/ssl/sqlpac/sqlpac.key \
    -CA /etc/ssl/sqlpac/sqlpac.crt \
    -CAcreateserial -CAserial /etc/ssl/VPSFRSQLPAC.serial \
    -out /etc/ssl/VPSFRSQLPAC.crt \
    -days 3650 \
    -sha256 \
    -extfile /etc/ssl/VPSFRSQLPAC.sslv3.txt
Signature ok
subject=O = SQLPAC, CN = VPSFRSQLPAC
Getting CA Private Key
  • -in /etc/ssl/VPSFRSQLPAC.csr : the signing request in input.
  • -CAkey /etc/ssl/sqlpac/sqlpac.key : CA RSA private key.
  • -CA /etc/ssl/sqlpac/sqlpac.crt : CA public certificate.
  • -CAcreateserial -CAserial /etc/ssl/VPSFRSQLPAC.serial : optional, serial number generation.
  • -out /etc/ssl/VPSFRSQLPAC.crt : the signed public certificate in output.

In the file /etc/ssl/VPSFRSQLPAC.sslv3.txt sent with the argument -extfile, some SSL v3 parameters are specified, especially the alternative DNS names (https://<DNS names>)

VPSFRSQLPAC.sslv3.txt
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = vpsfrsqlpac

It is an important directive. For Apache, alternative names alt_names will look like the below example for all possible URLs (https://fr.sqlpac.uat, https://sqlpac.uat…) :

[alt_names]
DNS.1 = sqlpac.uat
DNS.2 = *.sqlpac.uat

Final steps, checks

That’s all, everything is ready. Restart the softwares running on https://vpsfrsqlpac:* with the private key and the public certificate :

influxdb.toml
grafana.ini
…
http-bind-address = "vpsfrsqlpac2:8086"
tls-cert = "/etc/ssl/VPSFRSQLPAC.crt"
tls-key = "/etc/ssl/VPSFRSQLPAC.key"
…
…
http_port = 20002
…
# https certs & key file
cert_file = /etc/ssl/VPSFRSQLPAC2.crt
cert_key = /etc/ssl/VPSFRSQLPAC2.key
…

No need anymore to add extra options to command lines (curl --insecure, influx --skip-verify…) in order to prevent CA check.

How to test the validation mechanism with the CA ? Use openssl with the option s_client, the validation path is displayed :

$ openssl s_client -connect vpsfrsqlpac:8086 
CONNECTED(00000005)
…
---
Certificate chain
 0 s:O = SQLPAC, CN = VPSFRSQLPAC
   i:O = SQLPAC, CN = SQLPAC - Certificate authority, OU = SQLPAC - Certificate authority
---
…
SSL handshake has read 1615 bytes and written 793 bytes
Verification: OK
…
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
…
Verify return code: 0 (ok)