Calvin's Blog

Create your own Private Certificate Authority

∙ openssl∙ certificate∙ https∙ tls

If you don’t want all the nitty gritty technical detail, click here.

Introduction

For this walkthrough I am going to be using openssl specifically version 1.1.1f The documentation for which can be found here. I’ve primarily used this to create certificates for local development, but you can also generate your own certificates for securing communications for your own applications, and for many other purposes. The only down side is that any machine that does not have your public CA certificate on it will give you guff about not trusting the certificate, but thats what properly signed certificates are for. For a free alternative to typical paid ssl certificates for public applications take a look at Let’s Encrypt.

Step 1: Create our CA certificate

First we need a private key for our self signed CA certificate

openssl genrsa -des3 -out CA.key 2048

Because we passed the -des3 flag we will be asked to provide a password for our encrypted private key. Without an encryption specified your private key will not be encrypted.

Generating RSA private key, 2048 bit long modulus (2 primes)
.............................................+++++
.+++++
e is 65537 (0x010001)
Enter pass phrase for CA.key:
Verifying - Enter pass phrase for CA.key:

Now we have a private key and we can use that to generate our self signed CA certificate

openssl req -x509 -new -key CA.key -sha256 -days 1500 -out CA.pem

Tip: CA private key and certificate in one command

Generation of the private key and CA certificate can be done in a single command:

openssl req -x509 -new -nodes -newkey rsa:2048 -sha256 -days 1500 -out CA.pem

NOTE: this command does not encrypt your private key due to the -nodes parameter. I prefer to generate them separately

Here we will be prompted for info on our CA certificate

Enter pass phrase for CA.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Georgia
Locality Name (eg, city) []:Forsyth
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Local
Organizational Unit Name (eg, section) []:Dev
Common Name (e.g. server FQDN or YOUR name) []:dev.local
Email Address []:notarealemail@emails.org

Tip: Inline subject cli flag

The questions here can be answered with data passed in the cli (-subj) option:

-subj "/CN={commonName}/emailAddress={emailAddress}/C={Country}/ST={State}/L={Locality}/O={Organization}/OU={OrganizationalUnit}"

or via a configuration file for req commands.

To see the details of a certificate you can run:

openssl x509 -in CA.pem -text -noout

Step 2: Create a certificate signing request

Now that we have out CA certificate and private key we need to install the CA certificate on machines we want to trust the certificate we sign with the CA certificate and private key.

Once our CA certificate is installed on our target devices we can create certificates signed by our CA certificate for our applications to use.

First like above we will want to generate a private key for our certificate:

openssl genrsa -out dev.local.key 2048 # Feel free to specify an encryption if you want to encrypt this key. Ex: -des3

With our domain private key created we can now create a certificate signing request so we can have our certificate signed by our newly created CA certificate.

openssl req -new -key dev.local.key -out dev.local.csr

Tip: Additional inline subject note

As with the other req command we can specify the answers to the questions asked by this command with the -subj command or an configuration file. See example here.

The output from this command is as follows

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Georgia
Locality Name (eg, city) []:Forsyth
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Local
Organizational Unit Name (eg, section) []:Dev
Common Name (e.g. server FQDN or YOUR name) []:local.dev
Email Address []:notarealemail@emails.org

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

You can leave the challenge password and optional company name blank for our local certificates. Now that we have a certificate signing request we are almost ready to create a signed certificate for our domain. First we need a ext file to define our subject alternative names of which we can have multiple if we need multiple domains or sub domains covered by one certificate.

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

[alt_names]
DNS.1 = dev.local
DNS.2 = sub.dev.local
DNS.3 = other.local

For my system I saved that data in a file called dev.local.ext

Now that we have our .ext file we can generate our CA signed certificate!

Step 3: Creating our self signed certificate

openssl x509 -req -in dev.local.csr -CA CA.pem -CAkey CA.key -CAcreateserial -out dev.local.crt -days 825 -sha256 -extfile dev.local.ext

Not we have our dev.local.crt and dev.local.key which we can supply to our app server and if the CA certificate is present on the device browsing the server using our signed cert it will be valid.

Command Details

I wanted to take a moment to give some detail on the commands and parameters / flags used in these commands and provide more context to them.

Glossary and additional info

Information on certificate file extensions here.

Extension file info

The extension file format for openssl v1.1.1x can be found here. The fields in the extension file we use are:

TLDR

Here is a script that will go through the whole process of generating a self signed CA certificate for as many domains as you provide the script.

To call this script you can save this script to a file and run as follows: ./produce_cert.sh domain.one domain.two you will have to provide a password for the CA private key, but other than that you can just hit enter to skip the questions except when its asking you for the CA password.

#!/bin/bash

if [ "$#" -lt 1 ]
then
        echo "must provide domain(s) for certificate"
        exit 1
fi

## Step 1 create self signed CA certificate
### Step 1.1 Create CA private key. WIll ask for password
openssl genrsa -des3 -out CA.key 2048

### Step 1.2 Create self signed CA certificate
openssl req -x509 -new -key CA.key -sha256 -days 1500 -out CA.pem

## Step 2 create certificate signing request
### Step 2.1 create ext file for self signed certificate
EXT_FILE_NAME="$1.ext"

rm -f $EXT_FILE_NAME 2> /dev/null # remove existing file if it exists.

echo "authorityKeyIdentifier = keyid,issuer" >> $EXT_FILE_NAME
echo "basicConstraints = CA:FALSE" >> $EXT_FILE_NAME
echo "keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment" >> $EXT_FILE_NAME
echo "subjectAltName = @alt_names" >> $EXT_FILE_NAME
echo "" >> $EXT_FILE_NAME
echo "[alt_names]" >> $EXT_FILE_NAME

INDEX=1
for var in "$@"
do
        echo "DNS.$INDEX = $var" >> $EXT_FILE_NAME
        ((INDEX++))
done

### Step 2.2 certificate private key
openssl genrsa -out $1.key 2048

### Step 2.3 create .csr file
openssl req -new -key $1.key -out $1.csr

## Step 3 create certificate signed with self signed CA certificate
openssl x509 -req -in $1.csr -CA CA.pem -CAkey CA.key -CAcreateserial -out $1.crt -days 500 -sha256 -extfile $EXT_FILE_NAME