Configuring your own CA is a task that can be done with OpenSSL. In this post I'll show how to create a self signed master root certificate and the steps to create signing requests and sign them.
Creating the CA environment directory
The certificate environment is not more than a directory to work on. Organizing files that way makes the CA management easier for us. We also create a
.conf file to save time on issuing OpenSSL commands.
So, create your working directory for the CA and then, within this directory, we need two create two more:
certs directory is for placing all the certs that will be created by our CA, and
private will be used to store the copy of our CA key. The
private directory permissions should be 0700.
Besides those directories, we will create four files that our CA infrastructure will need:
- One to track the last serial number that was used for cert generation. It's called
serialand we initialize it to contain the value
01. OpenSSL expects this value to be in hex, and it must contain at least 2 digits, so we must pad it with a 0 in the left.
- The second is kind of a database to keep track of the certs that have been issued by this CA. OpenSSL requires that this file exists, but as we haven't created any cert, the file will be empty. Its name is
- The third file is the database attributes file, which in the beginning should have the value of
unique_subject = no. The default of this property is
yes, but OpenSSL recommends to change it to
no, as explained here.
- The configuration file. We'll be working on this one in the next sections of this post. For OpenSSL to read that file, before starting executing commands we need to setup an environment variable called
OPENSSL_CONFwith the absolute path of the config file as its value:
An example of how the directory should look like, assuming our environment directory is
[drwxrwxr-x] foobar ├── [drwxrwxr-x] certs ├── [-rw-rw-r--] index.txt ├── [-rw-rw-r--] index.txt.attr ├── [-rw-rw-r--] openssl.cnf ├── [drwx------] private └── [-rw-rw-r--] serial
Issuing our self signed root certificate
So here we start with the configuration file. In this first section we'll set the variables needed to create out root certificate:
default_bitsspecifies the length of the key. A value of 2048 is considered safe here.
default_keyfileis the path where the key will be created.
default_mdis the message algorithm that will be used to sign certs and CRLs.
promptwill define if OpenSSL will ask for information to fill the distinguished name fields.
distinguished_namemakes reference to he section that contains the information for the distinguished name fields. A reference of the available fields can be found here.
x509_extensionsmakes reference to the section that lists the extensions for the certificate. These extensions determine how certificates are going to be used. A reference of the standard extensions can be found here. The only "contraint" we're using here, refers to the certificate being used as a certificate authority.
encrypt_keyis self explaining. The generated key will be encrypted depending on the value of this attribute. If encryption is enabled, you'll need to provide the passphrase each time a client certificate is signed.
Now that this configuration is in place, and assuming the
OPENSSL_CONF environment variable has been configured, the root key pair and certificate can be created:
openssl req -newkey rsa -x509 -days 365 -out ca.crt -outform PEM
-newkey rsa parameter tells OpenSSL to create a new certificate request along with a new private key (RSA), but
-x509 changes the certificate request for a self signed certificate, with a validity of
-days 365. The size of the private key and the location of it are specified in the
openssl.cnf file we created. The remaining
-outform parameters are self explaining.
There should not be duplicated cert serial numbers issued by the same CA (the serial file keeps track of the last used serial number).
Configuring the Certificate Authority
To configure the behavior of our CA, some other options should be specified in the
openssl.cnf file. These settings apply to the certificates signing process.
default_caspecifies the section that will contain the settings for our certificate authority.
diris the directory where our CA environment directory is located.
serialattributes are just references to the files we initially created to setup our environment directory.
default_crl_daysdefines how often CLRs are going to be issued.
default_daystells OpenSSL for how many days the certificates are going to be valid.
default_md, just as in the
reqsection, specifies with message digest algorithm to use.
policyspecifies the section that will be used to configure the certificates distinguished name fields. The 3 available options are
optional. Depending on their value, the fields on the certificate request should match with the ones of the root certificate, be supplied or make them optional.
x509_extensions, just like in the req section, specifies the extensions that the signed certificates will contain, in this case, no signed certificate from this CA can be used as a CA itself.
Creating CSRs for signing
To sign a certificate, a certificate signing request (CSR) should be sent to the Certificate Authority. These are created by the clients, so the
OPENSSL_CONF variable has nothing to do here. So, the clients should use the
openssl req command but without the
-x509 option. Execute this command in any other location, since the generated files are not part of the CA environment, but hypothetically generated by a client user.
openssl req -newkey rsa -keyout client.key -keyform PEM -out client.csr -outform PEM -nodes
.crt file is the one that should be sent to out CA to issue a certificate for the client. The command will ask for some data fields that correspond to the distinguished name of the root certificate. Depending on the CA configuration this CSR is being sent to, the value of the fields could be optional or strictly match with it. Remove the
-nodes option if you want the generated private key to be encrypted.
The previous command generates both a new private key and the CSR to sign. If the client already has a private key, the command for creating a new CSR is slightly different and simpler:
openssl req -new -key client.key -out client.csr
Actually signing the CSRs
Now, going back to the CA environment. As our CA configuration is already in place in the
openssl.cnf file (and the
OPENSSL_CONF environment variable set), singing certificates will be easy:
openssl ca -in /path/to/client.csr
OpenSSL will ask for confirmation, sign the cert, and update its database. That's it! Take a look at the
certs/ directory to retrieve the sign cert, and inspect the
index.txt fields to confirm that the database was actually updated.
This is also an easy process, just execute this command:
openssl ca -revoke /path/to/client.crt
The path to the client certificate could be in the same environment, since issued certificates are in the
certs/ folder. You can also grab a copy elsewhere and execute the command with it. The only change it does is to update the database and mark the certificate as revoked. It's our responsibility to create a publish the revocation lists, and that could be done publicly using a some HTTP server. To generate a revocation list, issue the following command:
openssl ca -gencrl -out /path/to/ca.crl
The only entity that currently knows that the cert is revoked is the CA itself. That's why the CRL should be published to let users known that their certs are revoked. It's worth noting that if you use the certs in your application to grant some kind of access to it, you should also have a mechanism to block those revoked certs.
An example of a working CA configuration can be found here.