Working with OpenSSL and DNS alternative names

Oren Oichman
4 min readMar 7, 2020

--

Why This Story

In Today’s world in some case you would want your certificates to be able to be legitimate for more then one domain. For that purpose we can apply DNS alternative names to our SSL certificates.

A good example for that is when you setup a website on OpenShift and you will want your certificate to be valid for both Openshift “apps.<cluster name>.<you Domain>” prefix and for your domain that you bought for your application (“app.exmaple.com” for example).

This tutorial

In our tutorial I will setup a certificate for my docker registry and at the end I will show additional step due to the way the docker command works.

Answer files

When running the “openssl” command without an answer file the command will ask use to feel in the blanks (unless we set then up in openssl.cnf in advanced). To same use time we will start by creating 2 answer files , one for the CA and one for our certificate , the reason for the separation is that the CA should not have alternatives names given to him at the certificate creation.

First we set a few environment variables :

# export DOMAIN="example.local"
# export SHORT_NAME="registry"

will write an answer file for our registry (domain) :

$ cat > ${SHORT_NAME}_answer.txt << EOF
[req]
default_bits = 4096
prompt = no
default_md = sha256
x509_extensions = req_ext
req_extensions = req_ext
distinguished_name = dn

[ dn ]
C=US
ST=New York
L=New York
O=MyOrg
OU=MyOrgUnit
emailAddress=me@working.me
CN = ${SHORT_NAME}

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = ${SHORT_NAME}
DNS.2 = ${SHORT_NAME}.${DOMAIN}
EOF

(you can change the dn values as you please except for the “CN”)

Next we will create the CA answer file which we will use (as mentioned) only for the CA creation.

$ cat > csr_ca.txt << EOF
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = usr_cert

[ dn ]
C=US
ST=New York
L=New York
O=MyOrg
OU=MyOU
emailAddress=me@working.me
CN = server.example.com

[ usr_cert ]
basicConstraints=CA:TRUE
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
EOF

Feel free to change the DN and the DNS values as you see fit. Make sure that the first DNS matches the Domain CN.You can apply the CA answer file to your domain in case you don’t need the alternative names options.

CA certificate and Key

We will start by creating the files we need for our CA. as a why of work we will always start with generate the RSA key with the length of 4096 (at the very list) .

Generate the Key:

$ openssl genrsa -out ca.key 4096

Next we will use the CA key we just created and the ca answer file to generate our CA certificate (that will be our public CA we will send to every machine that will want to connect to our registry over SSL.

Generate the CA

$ openssl req -new -x509 -key ca.key -days 730 -out ca.crt -config <( cat csr_ca.txt )

One the command was successful you can run “ls” and see the 2 files we created :

  1. ca.key
  2. ca.crt

Server Certificate and Key

for the following step we will create 2 additional files for our server (registry). One is (and obviously) the Server key and the other is the server certificate request.

Sense we need the CA to generate (and verify) our server certificate we are creating a request file so the CA will read for certificate details.

Generate Server Key

Same as we done for the CA , we are generating an RSA key with the length of 4096 chars.

$ openssl genrsa -out ${SHORT_NAME}.key 4096

Generate Server CSR

Now we will generate the certificate request using the domain Key and the domain answer file which we created in the beginning of the this tutorial.

$ openssl req -new -key ${SHORT_NAME}.key -out ${SHORT_NAME}.csr -config <( cat ${SHORT_NAME}_answer.txt )

It is a very good practice at this point to Test the CSR for DNS alternative names :

$ openssl req -in ${SHORT_NAME}.csr -noout -text | grep DNS
DNS:registry, DNS:registry.example.local

If you received the output as in the example you are good to go.

Sign the CSR :

now comes the tricky part , we need to tell the CA to use the “altrnames” we setup in the answer file but we need to tell it which section to look at for the values we need so we are going to add 2 more arguments for this purpose.

$ openssl x509 -req -in ${SHORT_NAME}.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ${SHORT_NAME}.crt -days 730 -extensions 'req_ext' -extfile <(cat ${SHORT_NAME}_answer.txt)

as you can see there are 2 more arguments :

  1. extentions — section from config file with X509V3 extensions to add
  2. extfile — configuration file with X509V3 extensions to add

Only when we team up those 2 options does our CA sign the certificate with our alternatives DNS names.

Certificate bundle

In some cases it is a good practice to join the certificate and the CA into a single file (not all servers has a CA configuration options).

$ mv ${SHORT_NAME}.crt ${SHORT_NAME}-certonly.crt
$ cat ${SHORT_NAME}-certonly.crt ca.crt > ${SHORT_NAME}.crt

Testing the Certificate

Now all that is left to do is to test our certificate :

$ openssl x509 -in ${SHORT_NAME}.crt -noout -text | grep DNS
DNS:registry, DNS:registry.example.local

And if we want to make sure the ca.crt is the signer of the certificate we can test it with the “verify” arguments:

$ openssl verify -CAfile ca.crt ${SHORT_NAME}.crt
registry.crt: OK

If your output is the same as the example you done everything right!!

updating the Registry

As promise to update the registry first we will copy our ca.crt to our “anchors” directory :

$ cp ca.crt /etc/pki/ca-trust/source/anchors/${SHORT_NAME}.crt
$ update-ca-trust extract

For the registry we will copy the file to our domain directory under “/etc/docker/cert.d/” as follow :

$ export MY_SERVER="registry.example.local"
$ mkdir /etc/docker/certs.d/${MY_SERVER}
$ cp ca.crt /etc/docker/certs.d/${MY_SERVER}/

Now all that is left is to restart the docker service and we are good to go.

$ systemctl restart docker

That is it

Have FUN!!!

--

--