Creating a CA for mTLS with OpenSSL
In this article I will explain how I created my own certificate authority and issued certificates to use for my Zookeeper setup. Thus, I also go into detail about how to convert certificates to use in a Java application.
To give a bit of background, I started working recently on a personal project to deploy Zookeeper on Kubernetes. One feature of Zookeeper that I was interested in is TLS encryption on connections and using mTLS for authentication. I some needed TLS certificates and I decided to roll my own.
For an introduction to mTLS, check out this article:
I decided that I’m going to use a single CA for all certificates. The advantage of this setup is that I only have to add a single CA certificate to my container images. The drawback is how to handle certificate revokation, for example if a key is compromised or a certificate is decommissioned. This is out of the scope of this article.
Creating a CA
I used OpenSSL to create a certificate authority and some certificates. With OpenSSL, I prefer supplying all configuration via config files. What I found very valuable is the .include
directive, that allows me to share a base config with all the certificate-specific entries delegated to different files.
Preparing the CA home
To create the CA repository, in a separate directory, create a directory and initialize it with some subdirectories and files:
mkdir my-ca
cd my-ca/
mkdir certsdb crl index private
touch index/index.txt
echo "1000" > crl/crlnumber
chmod 0700 private/
For the CA, I wanted to define some sensible defaults:
- Certificates are issued for 1 year;
- I use SHA512 as the message digest;
commonName
is for humans, and all other fields are absent.- Any DNS and IP constraints are delegated to the
subjectAltName
extension.
Then, create a configuration file for your repository:
openssl.conf
:
[ ca ]
default_ca = default_ca
[ default_ca ]
dir = /path/to/my-ca # <--- CHANGE THIS to your my-ca directory!
certs = $dir/certsdb
new_certs_dir = $certs
database = $dir/index/index.txt
certificate = $dir/ca.pem
private_key = $dir/private/ca-key.pem
serial = $dir/serial
crldir = $dir/crl
crlnumber = $dir/crl/crlnumber
crl = $crldir/crl.pem
RANDFILE = $dir/private/.rand
default_md = sha512
default_days = 365
policy = policy_default
x509_extensions = x509_extensions_mtls
[ policy_default ]
commonName = supplied
organizationName = optional
countryName = optional
stateOrProvinceName = optional
organizationalUnitName = optional
emailAddress = optional
[ x509_extensions_mtls ]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @x509_subject_alt_name
Creating the CA certificate
Create a configuration for your CA certificate:
ca.conf
:
.include ./openssl.conf
[req]
distinguished_name = req_dn_ca
prompt = no
[ req_dn_ca ]
commonName = Thoughts on code CA
[ x509_extensions_ca ]
basicConstraints = critical, CA:TRUE
keyUsage = critical, cRLSign, digitalSignature, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
Create a key for your CA, with a secure password in private/ca-key.pem
:
Create the self-signed CA certificate:
openssl req -config ca.conf -new -x509 -key private/ca-key.pem \
-days 3650 -sha512 -extensions x509_extensions_ca -out ca.pem
Creating the mTLS certificate for Zookeeper
Create a configuration for your zookeeper certificate:
zookeeper.conf
:
.include openssl.conf
[ req ]
prompt = no
distinguished_name = req_dn
[ req_dn ]
commonName = Zookeeper Dev
[ x509_subject_alt_name ]
DNS.1 = *.zk.dev.svc.cluster.local
Note: you can also add ip address restrictions by adding IP.1 = 192.168.x.x
, IP.2 = ...
lines to the x509_subject_alt_name
section.
Create a key for your certificate:
Create the certificate and sign with your CA:
Note: the subject of the certificate is supplied on the command line, and the subject_alt_name
section is in the configuration file.
Sign the certificate with your CA:
Note: the -create_serial
switch is only needed the first time you are issuing a certificate, and should be omitted subsequently.
To use this certificate in Java, we must convert it to PKCS12 or JKS (Java KeyStore) format. Oddly enough, to import a key into JKS, this can only be done from a PKCS12 file:
openssl pkcs12 -export -name zookeeper-dev -CAfile ca.pem -chain -in zookeeper.pem \
-inkey private/zookeeper-key.pem -out zookeeper.p12 -password pass:changeit
keytool -importkeystore -srckeystore zookeeper.p12 -srcstoretype PKCS12 \
-srcstorepass changeit -destkeystore zookeeper.jks -deststorepass changeit
Also import your CA certificate:
Note: I use the password changeit
here, which is the default password for the JDK CA certificate store as well. Make sure to use a secure password!
I hope you enjoyed this article!
Comments
Post a Comment