CFSSL FTW

After reading how CloudFlare handles their PKI and that LetsEncrypt will use it I wanted to give CFSSL a shot.

Reading the project’s documentation doesn’t really help in building your own CA, but searching the Internet I found Fernando Barillas’ blog explaining how to create your own root certificate and how to create intermediate certificates from this.

I took it a step further I wrote a script generating new certificates for several services with different intermediates and possibly different configurations (e.g. depending on your distro and services certain cyphers (e.g. using ECC) may not be supported).
I also streamlined generating service specific key, cert and chain files. 😀

Have a look at the full Gist or just the most interesting part:

#!/bin/bash
#
# Author: Riyad Preukschas <riyad@informatik.uni-bremen.de>
# License: MIT
#
# Generates root, internediate CA and service keys.
function generate_root_ca() {
local root_ca="$1"
echo "Generating root CA into ${root_ca}/"
[[ ! -d "${root_ca}" ]] && mkdir "${root_ca}"
cfssl genkey -initca "${root_ca}_csr.json" | cfssljson -bare "${root_ca}/root"
}
function generate_intermediate_ca() {
local root_ca="$1"
local intermediate_ca="$2"
echo "Generating intermediate CA into ${intermediate_ca}/ (for root CA ${root_ca}/)"
[[ ! -d "${intermediate_ca}" ]] && mkdir "${intermediate_ca}"
cfssl gencert -ca "${root_ca}/root.pem" -ca-key "${root_ca}/root-key.pem" -config="config.json" -profile="intermediate" "${intermediate_ca}_csr.json" | cfssljson -bare "${intermediate_ca}/intermediate"
}
function generate_service_keys() {
local root_ca="$1"
local intermediate_ca="$2"
local service_type="$3"
local key_name="$4"
local dist_dir="${intermediate_ca}/dist"
echo "Generating ${service_type} key pair into ${intermediate_ca}/"
[[ ! -d "${dist_dir}" ]] && mkdir "${dist_dir}"
cfssl gencert -ca "${intermediate_ca}/intermediate.pem" -ca-key "${intermediate_ca}/intermediate-key.pem" -config="config.json" -profile="server" "${key_name}_csr.json" | cfssljson -bare "${intermediate_ca}/${key_name}"
cp "${intermediate_ca}/${key_name}.pem" "${dist_dir}/"
cp "${intermediate_ca}/${key_name}-key.pem" "${dist_dir}/"
case "${service_type}" in
dovecot)
cat "${intermediate_ca}/intermediate.pem" "${root_ca}/root.pem" > "${dist_dir}/${key_name}-cachain.pem"
;;
nginx)
cat "${intermediate_ca}/${key_name}.pem" "${intermediate_ca}/intermediate.pem" "${root_ca}/root.pem" > "${dist_dir}/${key_name}.pem"
;;
postfix)
cat "${intermediate_ca}/intermediate.pem" "${root_ca}/root.pem" > "${dist_dir}/${key_name}-cachain.pem"
;;
esac
}
# Foo root CA
ROOT_CA="foo-root-ca"
#generate_root_ca "${ROOT_CA}"
# Some intermediate CA
generate_intermediate_ca "${ROOT_CA}" "some-intermediate-ca"
generate_service_keys "${ROOT_CA}" "some-intermediate-ca" "nginx" "foo-some-web"
# Another intermediate CA
generate_intermediate_ca "${ROOT_CA}" "another-intermediate-ca"
generate_service_keys "${ROOT_CA}" "some-intermediate-ca" "dovecot" "foo-another-imap"
generate_service_keys "${ROOT_CA}" "some-intermediate-ca" "postfix" "foo-another-smtp"
view raw renew-certs.sh hosted with ❤ by GitHub

You’ll still have to deploy them yourself.

Update 2016-10-04:
Fixed some issues with this Gist.

  • Fixed a bug where intermediate CA certificates weren’t marked as CAs any more
  • Updated the example CSRs and the script so it can now be run without errors

Update 2017-10-08:

  • Cleaned up `renew-certs.sh` by extracting functions for generating root CA, intermediate CA and service keys.