Creating certificates with openssl

This is a tutorial on the steps for creating a certificate using openssl. It is based on my learnings of all the components and steps involved. I know there are more concise ways of generating the needed keys and certs (see section Quick version below), but I was interested in a comprehensive full system on creating a certificate usable by a internet server.

Create a certificate authority key and cert

I will start this guide by going through the steps of making a certificate authority (CA) to issue the certs. For the purposes of this tutorial, we are going to assume we are creating a certificate authority for a fake company called "Zebes"

Step 1: Create private key

First generate a private key using the genrsa subcommand of openssl.

$ openssl genrsa -aes256 -out ca-zebes.key 2048

This will generate a new key and prompt for a pass phrase (need to enter it twice):

Generating RSA private key, 2048 bit long modulus (2 primes)
..........+++++
........................+++++
e is 65537 (0x010001)
Enter pass phrase for ca-zebes.key:
Verifying - Enter pass phrase for ca-zebes.key:

This will write your private key into a file called ca-zebes.key

Step 2: Create CA certificate

Now create a certificate for the CA using the private key you just generated. Use the req subcommand of openssl

openssl req -new -x509 -key ca-zebes.key -sha256 -days 365 -out ca-zebes.cert.pem

This will prompt you for the pass phrase for the private key file you are referencing.

Enter pass phrase for ca-zebes.key:

Then it will prompt you for the fields that will be added into the distinguised name (DN). You can enter with the values that are most appropriate for your use case.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: /US
State or Province Name (full name) [Some-State]: /Florida
Locality Name (eg, city) []:Orlando
Organization Name (eg, company) [Internet Widgits Pty Ltd]: /Zebes Inc
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:ZebesCA
Email Address []:user@zebes.cc

Now you could theoretically install that certificate as a trusted CA on devices that with which you intend to use certificates issued from that CA.

The point of a certificate authority is to issue signed PKI certificates, which we will do next.

Make a new PKI certificate

Now we will make a certificate for use on a TLS server. In our example, we will use the example domain brinstar.net and alternate brinstar.lvh.me (this makes it easy to test because *.lvh.me points to 127.0.0.1).

Step 1: Generate a new private key

openssl genrsa -aes256 -out brinstar.key 2048

Again, this will ask you to enter and confirm a pass phrase for this key.

It is sometimes undesirable to have a server key with a passphrase on an apache server for example, because the pass phrase will need to be entered every time you restart the server, so if you want to recreate a key without a pass phrase, then run this command to generate a new key from your first key:

openssl rsa -in brinstar.key -out brinstar.nodes.key

Note that nodes means "no DES" which will not protect the key with a pass phrase and will not encrypt it in the file.

Step 2: Prepare config file

If you will need to create a subjectAlternativeName (SAN) section for your cert, to specify multiple DNS entries, create a config file similar to the following and save it to a file called server.cfg:

[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name (full name)
localityName = Locality Name (eg, city)
organizationName = Organization Name (eg, company)
commonName = Common Name (e.g. server FQDN or YOUR name)

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = brinstar.net
DNS.2 = www.brinstar.net
DNS.3 = brinstar.lvh.me

In the section alt_names include any number of DNS entries to provide more domains for which this certificate is to be used.

Step 3: Generate a certificate signing request

openssl req -new -key brinstar.nodes.key -config server.cfg -out brinstar.csr

This outputs a CSR file which is a certificate signing request: A request for a certificate to be signed by the CA (certificate authority). It will prompt you for some DN information.

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: /US
State or Province Name (full name) [Some-State]: /Florida
Locality Name (eg, city) []:Orlando
Organization Name (eg, company) [Internet Widgits Pty Ltd]: /Brinstar Inc
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:brinstar.net
Email Address []:user@brinstar.cc

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Alternative step 3:

Another option for generating the CSR using the config to include all the values to embed within the certificate without the prompts. Create a file server.cfg with the following contents:

FQDN = brinstar.net
COUNTRY = US
STATE = Florida
CITY = Orlando
ORGANIZATION = Brinstar, Inc
EMAIL = user@zebes.cc

[ req ]
default_bits = 2048
distinguished_name = req_dn
prompt = no
req_extensions = req_ext

[ req_dn ]
C = $COUNTRY
ST = $STATE
L = $CITY
O = $ORGANIZATION
CN = $FQDN
emailAddress = $EMAIL

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1 = brinstar.net
DNS.2 = www.brinstar.net
DNS.3 = brinstar.lvh.me

In this file you can include all the details that you would have been prompted to enter automatically, instead of being prompted. (Notice the prompt = no line in the file).

openssl req -new -key brinstar.nodes.key -config server.cfg -out brinstar.csr

Step 4: Verify CSR information

To verify the information is correctly embedded within the CSR file, run the following command and look for the section about Subject Alternative Name

openssl req -in brinstar.csr -text -noout | less -FX

Step 5. Make a certificate extension config file

If you have alternate subjects (multiple domains to support), you need to create an extension file, this will be used to configure the cert with the correct details.

Create a new file, called server.ext.cfg and put the following into it

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
subjectAltName=@alt_names

[ alt_names ]
DNS.1=www.brinstar.net
DNS.2=brinstar.net
DNS.3=brinstar.lvh.me

Step 6. Create the x509 certificate

Now you are ready to use the CA, the private key and the extension config file to generate the server's certificate using the openssl subcommand x509:

openssl x509 -req -in brinstar.csr -CA ca-zebes.cert.pem -CAkey ca-zebes.key -CAcreateserial -extfile server.ext.cfg -out brinstar.cert.pem

This will output the new signed certificate into the file brinstar.cert.pem.

Quick version

You can skip the Certificate Authority and the separate commands and create a self-signed cert really quickly with one command if it suits your needs.

This will create a new private key and certificate file with the settings from server.cfg and note the SAN options are specified in the -addext parameter.

openssl req -x509 -newkey rsa:4096 -nodes\
  -days 365\
  -config server.cfg\
  -addext "subjectAltName=DNS:brinstar.net,DNS:www.brinstar.net,DNS:brinstar.lvh.me"
  -keyout "certs/brinstar.key.pem"\
  -out "certs/brinstar.cert.pem"\

Attach cert/key to server config

For Nginx you will use something like this in your configuration to utilize the cert and key for the website.

server {
    listen              443 ssl;
    server_name         brinstar.net;
    ssl_certificate     brinstar.cert.pem;
    ssl_certificate_key brinstar.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ...
}

See The Nginx documentation for more information.

For Apache web server, you will use something like this:

LoadModule ssl_module modules/mod_ssl.so

Listen 443
<VirtualHost *:443>
    ServerName brinstar.net
    SSLEngine on
    SSLCertificateFile "/path/to/brinstar.cert.pem"
    SSLCertificateKeyFile "/path/to/brinstar.key"
</VirtualHost>