Creating TLS Certificates for Home Use

I used to create self-signed certificates, but they have the problem that I have to accept them the first time when using a browser, and when openssl library connects, I have to disable the certificate verification in curl, Node.js etc.

The proper fix is to create your own CA which your computers trust. Then signing certificates with this CA makes your computer trust those certificates. Luckily creating a CA is simple.

Note that all this is very insecure and should only be used for home use. For the real Internet, use Let’s Encrypt . I wish I could use the same for local certificates, alas this is not supported as Let’s Encrypt verifies DNS ownership.

Here the steps. See the original instructions by mrkiril which this is based on. And OpenSSL’s ca command (check the Warning section!). Also this includes plenty examples including something about the Java keytool.

1. Create your CA

You need 3 config files. First one: config_ssl_ca.cnf is for the CA.

[ req ]
default_bits = 2048

prompt = no
distinguished_name=req_distinguished_name
req_extensions = v3_req

[ req_distinguished_name ]
countryName=JP
stateOrProvinceName=Tokyo
localityName=Tama
organizationName=root organisation
organizationalUnitName=root department
commonName=Harald Kubota
emailAddress=harald.kubota@gmail.com

[ alternate_names ]
DNS.1 = t620.lan

[ v3_req ]
keyUsage=digitalSignature
basicConstraints=CA:true
subjectKeyIdentifier = hash
subjectAltName = @alternate_names

And this is how to create your CA certificate (self-signed):

mkdir CA
openssl genrsa -out ./CA/CA.key 4096
openssl req -new -x509 -key ./CA/CA.key -out ./CA/CA.crt -days 3650 -config config_ssl_ca.cnf

2. Create a CSR

Next one config_ssl.cnf is for generating a certificate sign request. The alternate_names should match the URL you use:

[ req ]
default_bits = 2048

prompt = no
distinguished_name=req_distinguished_name
req_extensions = v3_req

[ req_distinguished_name ]
countryName=JP
stateOrProvinceName=Tokyo
localityName=Home
organizationName=MyOrg
organizationalUnitName=Tech Department
commonName=Harald Kubota
emailAddress=harald.kubota@gmail.com

[ alternate_names ]
DNS.1 = web.lan
DNS.2 = web.net
IP.1  = 192.168.2.84

[ v3_req ]
keyUsage=digitalSignature
basicConstraints=CA:false
subjectAltName = @alternate_names
subjectKeyIdentifier = hash

Below is how to create a CSR. Note that “$x” is just a filename. The DNS name is in the previously mentioned config_ssl.cnf.

export x=web.lan
openssl genrsa -out "$x.key" 2048
openssl req -new -sha256 -key "$x.key" -config ./config_ssl.cnf -out "$x.csr"

3. Sign the CSR

And the last one config_ca.cnf to sign the CSR:

# we use 'ca' as the default section because we're using the ca command
[ ca ]
default_ca = my_ca

[ my_ca ]
#  a text file containing the next serial number to use in hex. Mandatory.
#  This file must be present and contain a valid serial number.
serial = ./CA/CA.srl

# the text database file to use. Mandatory. This file must be present though
# initially it will be empty.
database = ./CA/index.txt

# specifies the directory where new certificates will be placed. Mandatory.
new_certs_dir = ./

# the file containing the CA certificate. Mandatory
certificate = ./CA/CA.crt

# the file contaning the CA private key. Mandatory
private_key = ./CA/CA.key

# the message digest algorithm. Remember to not use MD5
default_md = sha256

# for how many days will the signed certificate be valid
default_days = 365

# a section with a set of variables corresponding to DN fields
policy = my_policy

# MOST IMPORTANT PART OF THIS CONFIG
copy_extensions = copy

[ my_policy ]
# if the value is "match" then the field value must match the same field in the
# CA certificate. If the value is "supplied" then it must be present.
# Optional means it may be present. Any fields not mentioned are silently
# deleted.
countryName = match
stateOrProvinceName = supplied
organizationName = supplied
commonName = supplied
organizationalUnitName = optional
commonName = supplied

This is how to sign:

openssl ca -config ./config_ca.cnf -out "$x.crt" -in "$x.csr" -batch

Should you try to sign 2 certificates with the same DN, you’ll get this error:

ERROR:There is already a certificate for /C=JP/ST=Tokyo/O=MyOrg/OU=Tech Department/CN=Harald Kubota

The fix is to change ./CA/index.txt.attr to be

unique subject = no

4. Import CA to Ubuntu

Your machine should not yet trust your new CA certificate. Use above created certificate and present a web page or similar. Confirm via

openssl s_client -connect HOST:PORT

to connect to that process using your previously created host certificate. You should see something along those lines. Note line 4 and 7 with a “verify error” part.

❯ openssl s_client -connect web.lan:8443
CONNECTED(00000003)
depth=0 C = JP, ST = Tokyo, O = MyOrg, OU = Tech Department, CN = Harald Kubota
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = JP, ST = Tokyo, O = MyOrg, OU = Tech Department, CN = Harald Kubota
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
[...]

To make OpenSSL trust the certificate, you have to make it trust your CA. This is how:

if [[ ! -d /usr/share/ca-certificates/extras ]] ; then
  mkdir /usr/share/ca-certificates/extras
fi
cp CA.crt /usr/share/ca-certificates/extras/HaraldCA.crt
echo "extras/HaraldCA.crt" >>/etc/ca-certificates.conf
update-ca-certificates -v

and after updating the root certificates on your computer:

❯ openssl s_client -connect web.lan:8443
CONNECTED(00000003)
depth=1 C = JP, ST = Tokyo, L = Tama, O = root organisation, OU = root department, CN = Harald Kubota, emailAddress = harald.kubota@gmail.com
verify return:1
depth=0 C = JP, ST = Tokyo, O = MyOrg, OU = Tech Department, CN = Harald Kubota
verify return:1
---
Certificate chain
[...]

5. Import CA into your Browser

Browsers bring their own CA store, so you have to update those. Copy CA.crt into a directory you can access from the browser, and import it.

Firefox: Preferences → Privacy & Security → Certificates View Certificates → In the Authorities pane click on Import… and import your CA.crt

Chrome: Settings → Privacy and security → Click on More → Manage certificates → Authorities → Import and import your CA.crt

From now on if you go to any web page which is signed with your CA, you browser will not show a warning anymore.

Caveats

This has no password/passphrase on any private key. Also the CA should be on a secured server.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Create your website at WordPress.com
Get started
%d bloggers like this: