I’m about to deploy a small sideproject I’ve been coding the past 2 weeks and I want to make sure that the site is served over https. Since I’ll likely put it on a subdomain to my own company and I don’t want to go get another signature from a public authority I thought I would try to roll my own mini-CA chain which I can use for these types of situations.
My basic idea is to set it up using a root certificate, an intermediate certificate and a project specific certificate. The root certificate signs the intermediate certificate which will sign the project certificate. So we have a very simple chain that way. I will store the root certificate offline on a usb device and have a accessible certificate revocation list hosted in a static directory on my company’s website.
Note that it’s important to keep the signed certificates somewhere safe too. If we don’t have access to the cert then we can’t revoke it!
First I will generate the root and intermediate certificates. I’ll put a password on the root cert
-des3 flag. The root cert will also be self-signed.
openssl genrsa -des3 -out blacknode.root.pem 4096 openssl genrsa -out blacknode.dev.pem 4096 openssl req -new -x509 -days 3650 -key blacknode.root.pem -out blacknode.root.crt
Since I want to use the
openssl ca tool I need to set up the directory a certain way.
mkdir ca mkdir ca/private mkdir ca/newcerts touch ca/index.txt echo "01" > ca/serial cp blacknode.root.crt ca/ cp blacknode.root.pem ca/private
Finally create a file
openssl.cnf describing the structure further. Please note
the defaultmd and defaultdays values. Defaultmd must be a hash algorithm good enough
to pass for firefox. The default MD5 which I copied from the openssl example was NOT.
Having a low defaultdays value might cause headaches in the future when you have
to renew the certs.
[ ca ] default_ca = CA_default # The default ca section [ CA_default ] dir = ./ca # top dir database = $dir/index.txt # index file. new_certs_dir = $dir/newcerts # new certs dir certificate = $dir/blacknode.root.crt # The CA cert serial = $dir/serial # serial no file private_key = $dir/private/blacknode.root.pem# CA private key RANDFILE = $dir/private/.rand # random number file default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha256 # md to use policy = policy_any # default policy email_in_dn = no # Don't add the email into cert DN name_opt = ca_default # Subject name display option cert_opt = ca_default # Certificate display option copy_extensions = none # Don't copy extensions from request [ policy_any ] countryName = supplied stateOrProvinceName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ v3_req ] basicConstraints = critical,CA:false keyUsage = nonRepudiation subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always [ v3_ca ] subjectKeyIdentifier = hash extendedKeyUsage = critical,serverAuth, clientAuth basicConstraints = CA:true keyUsage = cRLSign, keyCertSign, digitalSignature, nonRepudiation,keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign
Then sign the blacknode.dev certificate using the root cert.
openssl ca -config openssl-root.cnf -extensions v3_ca -out blacknode.dev.signed.crt -infiles blacknode.dev.csr
The output should look something like the following. Note the basicConstraints and key usage sections. These allow the signing certificate to sign new certs too.
Certificate Details: Serial Number: 1 (0x1) Validity Not Before: Feb 24 15:03:47 2015 GMT Not After : Feb 24 15:03:47 2016 GMT Subject: countryName = SE stateOrProvinceName = Stockholm organizationName = Blacknode organizationalUnitName = Development commonName = dev.blacknode.se X509v3 extensions: X509v3 Subject Key Identifier: 1B:E0:15:74:26:E8:9A:C6:D4:51:85:D8:73:EA:A4:5D:85:0A:C6:AD X509v3 Extended Key Usage: critical TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: CA:TRUE X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Key Agreement, Certificate Sign, CRL Sign
Phew, halfway there.
So now I have my root ca which I put on a usb stick. I copy the needed public certs
and create another ca directory in my home directory by repeating the procedure in the previous section, except this time
blacknode.dev.pem key instead. Basically a lot of this is the same as before. We also create a similar openssl.cnf file here, but with the right paths to the dev cert instead.
Since I trust myself, I’ll create the key for my https service here directly.
openssl genrsa -out faktura.pem 2048 openssl req -new -days 1095 -key faktura.pem -out faktura.csr openssl ca -config openssl.cnf -in faktura.csr -out faktura.signed.crt
Finally, create a chained certificate and verify.
cat blacknode.dev.signed.crt blacknode.root.crt > blacknode.chained.crt [tethik@asimov dev-ca]$ openssl verify -CAfile blacknode.chained.crt faktura.signed.crt faktura.signed.crt: OK
Now I can copy the keys and cert to the server. I intend to write a shorter post about setting up https on apache in the future.
Also left to do is setting up a certificate revocation list for each signing certificate and publishing them somewhere. Probably on my main company website.
For a linux system, installing the certs is easy. Just copy the
root public certificate (in my case blacknode.root.crt) to the
For accessing your https-enabled websites etc in your browser you may have to add the cert
to the browsers trusted certificate too. In Firefox, you can do this by going to
‘Preferences’, then under the ‘Advanced’ tab open the ‘View certificates’ window.
Then under Authorities import the root certificate.
Of course, this implies a great trust in that certificate. So be careful. I only intend to use this cert for projects which are not supposed to be used by the public.