Usually you don’t need to manage your own PKI infrastructure with OpenSSL. The normal modus operandi is to generate your CSR, get a recognized certificate authority to sign it, use it until the CA reminds you it’s about to expire then repeat the process.
Occasionally, though, you need to establish your own chain of trust. Either doing it through a recognized CA would be expensive or you need the CA to be more responsive than a third party could ever be. For example, when you maintain an entire enterprise system filled with mail servers, LDAP servers, file shares, etc you need to be able to generate the certificates that you need without any regard for a CA’s verification procedure or having to wait on them before a new system can go into production.
Looking around I could see plenty of copy/paste tips online and incomplete guides but I couldn’t find anything that really served as an acceptable “Certificate Authority 101” as opposed to just one or two simple tricks done inefficiently.
Contents
- Generate The Root CA’s Certificate and Private Key
- Configuring your local OpenSSL install for CA Operation
- Procedure for Signing CSR’s
- Revoking a Signed Certificate
- Troubleshooting and Helpful Advice
- Further Reading
Generate The Root CA’s Certificate and Private Key
First let’s create the private key we’re going to use for signing. This is only used for signing certs so it can be absurdly strong:
[root@localhost ~]# mkdir ssl [root@localhost ~]# cd ssl [root@localhost ssl]# openssl genrsa -out ca.key 10240 Generating RSA private key, 10240 bit long modulus ............................ [[....snip....]]
Now that we have the key we’re going to sign with, we need a certificate that describes the who and what of our. To do this you can either generate a self-signed certificate or have your CA certificate signed by a recognized CA with certificate signing as being enabled. Since that costs money, though, we’re just going to create a self-signed certificate with that key we just made.
To that end, let’s generate a CSR and then sign it ourselves:
[root@localhost ~]# openssl req -new -sha256 -key ca.key -out ca.csr 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) [XX]:US State or Province Name (full name) []:North Carolina Locality Name (eg, city) [Default City]:Durham Organization Name (eg, company) [Default Company Ltd]:Mr Wammy Incorporated Organizational Unit Name (eg, section) []:DoIT Common Name (eg, your name or your server's hostname) []:ca.wammy.io Email Address []:admin@wammy.ioPlease enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: [root@localhost ~]# openssl x509 -req -sha256 -days 1825 -in ca.csr -signkey ca.key -out ca.pem Signature ok subject=/C=US/ST=North Carolina/L=Durham/O=Mr Wammy Incorporated/OU=DoIT/CN=ca.wammy.io/emailAddress=admin@wammy.io Getting Private key
You’ll notice that on my last command, I told it to generate a self-signed cert with a lifespan of 5 years. Compared to normal certificates that usually last a year at most, CA certificates need to stick around for a while. By CA standards this is a minuscule amount of time but for enterprise-level CA’s this middle ground is probably an ideal. It keeps you from continually renewing the root CA certificate while also changing it out every so often in case the server it’s on was compromised.
Now that we have our private signing key and root CA certificate, we need to get the system ready to use it as such.
Configuring your local OpenSSL install for CA Operation
Now for the CA-specific stuff. On Debian-based systems the root openssl configuration directory is located at /etc/ssl
while on RHEL-based systems it’s located at /etc/pki
. The layout underneath is substantially similar though so I’ll abbreviate using $dir
to mean this directory.
Understanding How to Read openssl.cnf
Outside of updating $dir
on Ubuntu below, you probably won’t have to change much anything in the stock openssl.cnf
file of either Ubuntu-based or RHEL-based but you may need to check your distribution’s configuration to understand how it works.
First thing to note is its location. On Ubuntu-based systems it is located at /etc/ssl/openssl.cnf
whereas on Red Hat-based systems it’s located at /etc/pki/tls/openssl.cnf
. As for the file itself, though it may look like a regular .ini
file, there are some important differences. Probably the biggest differences are:
* Pound signs (#
) are used to begin comments, not semi-colons (;
).
* Directive values can later on be referenced by treating them as a variable. For instance in the stock Ubuntu 16.04 config, the root of the CA tree is set in dir
underneath the [CA_default]
section and then subsequently referenced in the directives that point to various components of your CA (for example new_certs_dir
and private_key
).
* Many directives will point to other sections by way of a directive value. For instance, the hard coded section ca
establishes the CA configuration for however many CA’s you end up running on this box. The default CA used is specified by the default_ca
directive by setting it equal to the section that defines the CA. That CA configuration section will in turn include a CA signing policy referenced by the policy
directive in that section.
Ensuring the CA directory tree is populated
On stock Ubuntu, issue the following commands to create the directory structure that your openssl ca
commands will assume already exist:
[root@localhost ~]# mkdir -p /etc/ssl/demoCA/{certs,crl,private,newcerts} [root@localhost ~]# chmod 755 /etc/ssl/demoCA/{certs,crl,private,newcerts} [root@localhost ~]#
Open /etc/ssl/openssl.cnf
in your preferred editor, locate the [ CA_default ]
section and modify the dir
directive so that the path to demoCA
is absolute (e.g “/etc/ssl/demoCA
“).
On RHEL-based systems, this directory structure and configuration has already been created (albeit at /etc/pki/CA
instead of /etc/ssl/demoCA
as mentioned previously).
In either case, the CA index (a summary of all issued certificates) and a starting serial number will need to be initialized:
[root@localhost ~]# touch $dir/index.txt [root@localhost ~]# echo 1000 > $dir/serial [root@localhost ~]#
Where $dir
is the root directory of the default CA for your distro.
Once the directories are in place, you need to copy the private key and the root CA cert to the appropriate directories so that openssl
can find them later on:
[root@localhost ~]# cp ca.pem $dir/cacert.pem [root@localhost ~]# cp ca.key $dir/private/cakey.pem [root@localhost ~]#
Procedure for Signing CSR’s
Now that our software infrastructure is in place, we can start the actual business of issuing certificates. For demonstration purposes, let’s just generate a bogus public/private key with a CSR:
[root@localhost ~]# openssl genrsa -out server.key 4096 Generating RSA private key, 4096 bit long modulus ........................................++ ................................++ e is 65537 (0x10001) [root@localhost ~]# openssl req -new -sha256 -key server.key -out server.csr 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) [XX]:US State or Province Name (full name) []:North Carolina Locality Name (eg, city) [Default City]:Durham Organization Name (eg, company) [Default Company Ltd]:Mr Wammy Incorporated Organizational Unit Name (eg, section) []:DoIT Common Name (eg, your name or your server's hostname) []:ca.wammy.io Email Address []:admin@wammy.ioPlease enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Now that the “client” has a request for us to sign, we can use our root certificate to sign it for them:
[root@localhost ~]# openssl ca -in server.csr -out server.pem Using configuration from /etc/pki/tls/openssl.cnf Check that the request matches the signature Signature ok Certificate Details: Serial Number: 4096 (0x1000) Validity Not Before: Nov 25 04:41:44 2017 GMT Not After : Nov 25 04:41:44 2018 GMT Subject: countryName = US stateOrProvinceName = North Carolina organizationName = Mr Wammy Incorporated organizationalUnitName = DoIT commonName = ca.wammy.io emailAddress = admin@wammy.io X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 2F:CC:90:79:54:F8:45:D2:2E:4B:F7:64:34:1A:C6:F1:CF:CD:7C:06 X509v3 Authority Key Identifier: DirName:/C=US/ST=North Carolina/L=Durham/O=Mr Wammy Incorporated/OU=DoIT/CN=ca.wammy.io/emailAddress=admin@wammy.io serial:A3:CA:7F:58:62:F7:0C:DACertificate is to be certified until Nov 25 04:41:44 2018 GMT (365 days) Sign the certificate? [y/n]:y1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated
A copy of the signed certificate will now live both in the current directory (since we specified an -out
option) and in the $dir/newcerts
directory where it will be named ${serialNumber}.pem
this certificate can now be delivered to the client for use in their web server, SMTP server, whatever it is they’re protecting with SSL/TLS.
That’s basically all there is to it. Once your infrastructure is setup, all subsequent CSR’s users send you can be signed with that same openssl ca -in server.csr -out server.pem
command.
Revoking a Signed Certificate
When is revocation useful?
Occasionally, a certificate will be given out but need to be revoked so that others don’t trust it anymore. For the majority of SSL uses, you’re just attempting to ensure private communication between nodes and simply replacing compromised certificates is sufficient. If all you’re interested in is that, then revocation is probably additional complexity you can do without.
The usual example for the usefulness of revocation would be SSL certificates used for client authentication. Compromised must be revoke and the user given new certificates. The latter process is usual and detail above, but the process for revoking them not so much.
Revoking a Signed Certificate
Revocation is pretty straight forward:
[root@localhost ~]# openssl ca -crl_reason keyCompromise -revoke server.pem Using configuration from /etc/pki/tls/openssl.cnf Revoking Certificate 1002. Data Base Updated
In the above we just accessed the ca
functions of openssl and instructed it to revoke a certificate called server.pem
in the current directory. It then loaded the serial number from the certificate and then updated its local database to reflect its revoked status.
The -crl_reason
is interesting because it provides us with a finite number of explanations on why we’re revoking the certificate:
keyCompromise
:: The private key is known to an untrusted party.CACompromise
:: The entire CA was compromised.privilegeWithdrawn
:: The security of the previous certificate is fine, there is just a privilege listed in the previous certificate (such a certificate signing, code signing, etc) that they no longer need.cessationOfOperation
:: The previous certificate is fine, the CA that issued it is just no longer going to be around to verify/revoke it.affiliationChanged
:: Security is fine, user’s role in the organization has just changed.certificateHold
:: Certificate is on “hold.” It can still be used but this is an advertisement that it may be subject to revocation soon.superseded
:: The user has a newer certificate, likely substantially similar, just newer.removeFromCRL
:: An unimplemented feature where several CRL’s can be combined, this one instructs the application assembling the final CRL to not include anycertificateHold
instances. This has the effect of “unholding” the certificate.unspecified
:: Means “dunno, my reason wasn’t in the list.”
This is useful in the event of an audit since it establishes a paper trail if it’s later on found that a user’s account was used. If it was before the revocation but after the determined date of compromise, then their current credentials should be secure still.
Providing the CRL For Consumption
OK great, openssl doesn’t consider the certificate good anymore but who cares about that? I’m trying to prevent an attacker from using the certs to impersonate the user. To do this we have to publish a Certificate Revocation List (“CRL”).
Luckily generating a new CRL from the current database is pretty easy. First we need to make sure we have a valid serial for the CRL’s we create. If it doesn’t already exist create a file at $dir/crlnumber
and populate it with a starting serial of 1000.
For example, on RHEL:
[root@localhost ~]# echo 1000 > /etc/pki/CA/crlnumber [root@localhost ~]#
Then we generate a new CRL saving it to a file:
[root@localhost ~]# openssl ca -gencrl -out newer.crl Using configuration from /etc/pki/tls/openssl.cnf [root@localhost ~]#
You can then check this CRL out to make sure our latest revocation is in there:
[root@localhost ~]# openssl crl -in newer.crl -text | grep -A4 "Serial Number: 1002" Serial Number: 1002 Revocation Date: Nov 25 21:26:47 2017 GMT CRL entry extensions: X509v3 CRL Reason Code: Key Compromise [root@localhost ~]#
Now that the CRL file has been generated you can then make it accessible to the clients that need it. As per RFC this can be anything you can make a URL out of but is usually an HTTP(S) address that returns a content type of application/pkix-crl
along with the DER encoded CRL we generated above.
Alternatively, you can have some sort of automated job pull a static version of your enterprise’s CRL and give that to the application performing the SSL negotiation. For example Apache has the SSLRevocationFile directive for a PEM-encoded CRL file (note: we generated a DER encoded one above so it needs to be converted to PEM before giving it to apache).
Troubleshooting and Helpful Advice
There’s very little in the above that can actually go wrong in the text book use case. In the real world, though you’ll run across unique situations that may just happen to not line up with that cookie cutter situation I presented up above:
- You may need to adjust the signing policy to allow the CA to sign certificate requests outside the CA’s organization or province.
- You may need to modify the CA configuration to change defaults for
default_days
(default lifespan of signed certificates) ordefault_md
(default message digest used when signing) to something more appropriate for your environment. - I would recommend against the tendency of many to roll all your
openssl
commands into a single command unless you absolutely need to. Being able to monitor a certificate’s progression from private key to signed cert will help you in the long run figure out where certain values are coming from or where a particular problem starts. If it’s all one command, you may not be able to guess what’s actually happening. - I would also recommend against another common tendency I see which is to use a special CA-specific configuration file. The stock config file that comes with your distribution is perfectly fine and keeping all relevant configuration in one standard place enables people who come behind you to more easily tell what you’re doing.
- It may be worth investigating the distinguished name defaults in the
req_distinguished_name
section ofopenssl.cnf
(specifically the directives ending in_default
). By setting these to something specific to your organization you reduce the amount of typing required for a CSR and help ensure CSR’s have the correct information listed.
Further Reading
I plan on continually adding/updating this this post so please consider it a living document and submit and anachronisms or omissions. In the mean time, you can find more information on the internet:
- Creating Self signed certificate with keyUsage extensions (Useful if you plan on submitting certificates with different uses such as code signing or S/MIME non-repudiation.
- OpenSSL PKI Tutorial: Appendix B: CA Database