Tyrone Tudehope
blog.tyrone.dev

Naive guide to generating development certificates

  • 10/06/2019 - Published

The easy way: mkcert.

Recently I was working on an Ansible role to provision Nginx with TLS already configured and enabled, however to test it effectively, I needed to have valid certificates installed in the testing container. This was my brief foray into OpenSSL.

Setting up

Before we can set up a CA and start signing certificates, there are a few requirements. We need a place to store the certificates, we need some configuration, and we need a database for CA lookups. Fortunately, that is all readily available.

Create a directory somewhere on your machine where you’d like your certificates to be stored:

mkdir -p ~/.local/share/certs
cd ~/.local/share/certs

Add in your OpenSSL configuration, here’s the config I used to generate my certicates for the Ansible role:

HOME = .
oid_section = new_oids
[ new_oids ]
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = .
certs = $dir
crl_dir = $dir/crl
database = $dir/index.txt
unique_subject = no
new_certs_dir = $dir
certificate = $dir/ca.crt
serial = $dir/serial
crlnumber = $dir/crlnumber
crl = $dir/crl.pem
private_key = $dir/ca.key
x509_extensions = usr_cert
name_opt = ca_default
cert_opt = ca_default
default_days = 24855
default_crl_days= 30
default_md = default
preserve = no
policy = policy_anything
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca
string_mask = utf8only
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = AU
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Internet Widgits Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
basicConstraints=CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ proxy_cert_ext ]
basicConstraints=CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
[ tsa ]
default_tsa = tsa_config1
[ tsa_config1 ]
dir = ./demoCA
serial = $dir/tsaserial
crypto_device = builtin
signer_cert = $dir/tsacert.pem
certs = $dir/cacert.pem
signer_key = $dir/private/tsakey.pem
signer_digest = sha256
default_policy = tsa_policy1
other_policies = tsa_policy2, tsa_policy3
digests = sha1, sha256, sha384, sha512
accuracy = secs:1, millisecs:500, microsecs:100
clock_precision_digits = 0
ordering = yes
tsa_name = yes
ess_cert_id_chain = no
ess_cert_id_alg = sha1
view raw openssl.cnf hosted with ❤ by GitHub

When generating a signed certificate with openssl ca, OpenSSL will try to update a database file which, depending on your configuration, might be index.txt.

touch index.txt

Create a Certificate Authority

First generate a key:

openssl genrsa -out ca.key 2048

Then create a certificate signed with that key:

openssl req -new -x509 -key ca.key -out ca.crt

Note that the key and certificate filenames are important here. They should match the names in your configuration file.

To avoid prompts when generating the certificate, an extra -subj argument can be passed:

openssl req -new -x509 -key ca.key -out ca.crt -subj "/C=ZA/ST=Western Cape/L=Cape Town/O=Bush Co/OU=Tech/CN=my-domain.dev"

The fields passed to the subject argument correspond with:

[C] Country Name (2 letter code) 	The two-letter country code where your company is legally located.
[ST] State or Province Name (full name) 	The state/province where your company is legally located.
[L] Locality Name (e.g., city) 	The city where your company is legally located.
[O] Organization Name (e.g., company) 	Your company's legally registered name (e.g., YourCompany, Inc.).
[OU] Organizational Unit Name (e.g., section) 	The name of your department within the organization. (You can leave this option blank; simply press Enter.)
[CN] Common Name (e.g., server FQDN) 	The fully-qualified domain name (FQDN) (e.g., www.example.com).

Generate your certificate

Create your site key:

openssl genrsa -out localhost.key 2048

Generate a signing request:

openssl req -new -key localhost.key -out localhost.csr -subj "/C=ZA/ST=Western Cape/L=Cape Town/O=Bush Co/OU=Tech/CN=localhost"

And finally, sign the key and generate your certificate:

openssl ca -config openssl.cnf -in localhost.csr -out localhost.cer -create_serial -batch

Using the certificates

Install your CA on Firefox

In Firefox, navigate to Preferences -> Privacy & Security -> Certificates. Click “View Certificates” and under “Authorities” click Import and import your ca.crt file.

Nginx

In your vhosts file for your site:

ssl_certificate_path: "/etc/path/to/your/localhost.cer"
ssl_certificate_key_path: "/etc/path/to/your/localhost.key"

curl

In my case, I was using Molecule which runs tests against a Docker container. I needed to make a call to my test site over TLS and verify the response:

curl --cacert /etc/path/to/my/ca.crt -I -H "Host: test.dev" https://localhost

HTTP/2 200
date: Mon, 10 Jun 2019 20:22:57 GMT
expires: Wed, 10 Jul 2019 20:22:57 GMT
server: nginx
x-xss-protection: 0
x-frame-options: SAMEORIGIN
...

To dive into more details around setting up a CA, Igor Soarez has an in-depth guide here: https://gist.github.com/Soarez/9688998.

Leave a comment