OpenSSL tips and tricks

You're probably at least peripherally familiar with OpenSSL as a library that provides SSL capability to internet servers and clients. OpenSSL, however, in addition to providing a library for integration, includes a useful command line tool that can be used for effectively every aspect of SSL/PKI administration. It's a bit under-documented though; this post doesn't aim to fully document it, but I've come across some fairly useful shortcuts that I thought I'd share with you, in "cookbook" style format.

Get help on OpenSSL subcommands

The OpenSSL command-line application is a wrapper application for many "sub-programs". When you invoke OpenSSL from the command line, you must pass the name of a sub-program to invoke such as ca, x509, asn1parse, etc. However, if you want information on these sub-programs, the OpenSSL man page isn't going to be much help. Instead, each one has its own man page, so to see the options available for openssl x509, type:

$ man x509

See the raw structure of an ASN.1 file

SSL is an implementation of a Public-Key Infrastructure (PKI) and as such, deals quite a bit with certificates. In fact, it's safe to say that if something goes wrong with an SSL setup, there's a 90% probability that a misconfigured certificate will end up being the root cause. Certificates (X.509 certificates, to be precise) are described in a formal language called "ASN.1" or Abstract Syntax Notation (.1). (ASN.1 is somewhat similar, at least in concept, to XML or JSON). There are quite a few different sorts of file associated with certificates, and there are no standards on exactly how they should be named or what extensions they should use. When presented with a mysterious PKI-related file, viewing the raw structure can be helpful in determining what exactly you're dealing with or possibly what's gone wrong with it. However, if you open one up in, say, a text editor, it probably looks something like:

MIICbDCCAioCAQAwaDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlRYMQ4wDAYDVQQHEwVQbGFubzER
MA8GA1UEChMIMnhvZmZpY2UxETAPBgNVBAsTCFJlc2VhcmNoMRYwFAYDVQQDEw1Kb3NodWEgRGF2
aWVzMIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2
USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4
O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmC
ouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCB
gLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhR
kImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAOPlkQGq47HhKxmGBTDWaLaa140+Rl3b+
rZk3TpODy/BTS6O+v608EEBnJvF6ck26qFjLBHPC8IihovdcczKEAofIiR+do7CMjUEWdWIFnwKX
6W46ElBJN1ieWl1HtGj5RXnSfcfitiRGOiee1jsyV7wVn0Y4/8vbYPAUFRSPpk2gADALBgcqhkjO
OAQDBQADLwAwLAIUFgkt1lvVhcR1JE6wW7pyBAsgA1wCFHcSuOTaZdIM/dKwJ5dOFgsK0zOi

This is clearly Base64 encoded, but Base64 decoding it won't provide you with any useful information; it's a Base64 encoding of the Distinguished Encoding Rule (DER) representation of an ASN.1-formatted file. To parse it more meaningfully, use the asn1parse subcommand:

$ openssl asn1parse -in mysterious_file.pem
    0:d=0  hl=4 l= 620 cons: SEQUENCE          
    4:d=1  hl=4 l= 554 cons: SEQUENCE          
    8:d=2  hl=2 l=   1 prim: INTEGER           :00
   11:d=2  hl=2 l= 104 cons: SEQUENCE          
   13:d=3  hl=2 l=  11 cons: SET               
   15:d=4  hl=2 l=   9 cons: SEQUENCE          
   17:d=5  hl=2 l=   3 prim: OBJECT            :countryName
   22:d=5  hl=2 l=   2 prim: PRINTABLESTRING   :US
   26:d=3  hl=2 l=  11 cons: SET               
   28:d=4  hl=2 l=   9 cons: SEQUENCE          
   30:d=5  hl=2 l=   3 prim: OBJECT            :stateOrProvinceName
   35:d=5  hl=2 l=   2 prim: PRINTABLESTRING   :TX
   39:d=3  hl=2 l=  14 cons: SET               
   41:d=4  hl=2 l=  12 cons: SEQUENCE          
   43:d=5  hl=2 l=   3 prim: OBJECT            :localityName
   48:d=5  hl=2 l=   5 prim: PRINTABLESTRING   :Plano
   55:d=3  hl=2 l=  17 cons: SET               
   57:d=4  hl=2 l=  15 cons: SEQUENCE          
   59:d=5  hl=2 l=   3 prim: OBJECT            :organizationName
   64:d=5  hl=2 l=   8 prim: PRINTABLESTRING   :2xoffice
   74:d=3  hl=2 l=  17 cons: SET               
   76:d=4  hl=2 l=  15 cons: SEQUENCE          
   78:d=5  hl=2 l=   3 prim: OBJECT            :organizationalUnitName
   83:d=5  hl=2 l=   8 prim: PRINTABLESTRING   :Research
   93:d=3  hl=2 l=  22 cons: SET               
   95:d=4  hl=2 l=  20 cons: SEQUENCE          
   97:d=5  hl=2 l=   3 prim: OBJECT            :commonName
  102:d=5  hl=2 l=  13 prim: PRINTABLESTRING   :Joshua Davies
  117:d=2  hl=4 l= 439 cons: SEQUENCE          
  121:d=3  hl=4 l= 300 cons: SEQUENCE          
  125:d=4  hl=2 l=   7 prim: OBJECT            :dsaEncryption
  134:d=4  hl=4 l= 287 cons: SEQUENCE          
  138:d=5  hl=3 l= 129 prim: INTEGER           :FD7F53811D75122952DF4A9C2...
  270:d=5  hl=2 l=  21 prim: INTEGER           :9760508F15230BCCB292B982A...
  293:d=5  hl=3 l= 129 prim: INTEGER           :F7E1A085D69B3DDECBBCAB5C3...
  425:d=3  hl=3 l= 132 prim: BIT STRING        
  560:d=2  hl=2 l=   0 cons: cont [ 0 ]        
  562:d=1  hl=2 l=  11 cons: SEQUENCE          
  564:d=2  hl=2 l=   7 prim: OBJECT            :dsaWithSHA1
  573:d=2  hl=2 l=   0 prim: NULL              
  575:d=1  hl=2 l=  47 prim: BIT STRING

If you have some familiarity with PKI concepts and workflows, you can see immediately that this is a certificate signing request. More recent versions of OpenSSL include the -i parameter to asn1parse that will indent based on parse depth — this can be useful especially in longer structures like certificate chains.

Deal with PEM and DER-encoded files

Most of the time, the command above (openssl asn1parse -in file) will work with any SSL/PKI related file. However, you might use it as shown and be presented with the error:

$ openssl asn1parse -in request.der 
Error: offset too large

Most likely, this means that you're dealing with a DER-encoded, rather than PEM-encoded, file. What's the difference? Well, DER is the "raw form" that SSL itself needs in order to process the certificate correctly; this is what's actually transferred by the server to the client to authenticate itself. As you probably know, though, binary files and e-mail don't mix, so files are usually (but not always) Base64 encoded before being attached to an e-mail. A Base-64 representation of a DER-encoded certificate or signing request is called (somewhat illogically) a Privacy Enhanced Mail or PEM format. In fact, a common (but not required) convention is to name raw certificate files with a .der extension and Base64-encoded files with a .pem extension.

The term PEM originates from its first uses as part of Phil Zimmerman's PGP (Pretty Good Privacy) secure e-mail efforts from the 90's which itself was based on an IETF effort to describe an encryption infrastructure for e-mail. Nowadays, we call any Base64 encoded representation of a certificate-related file "PEM-encoded", even though it's not necessarily private.

OpenSSL assumes this Base64 encoding of everything it deals with and automatically tries to do an in-place Base64 decode before processing its input. However, if you need to bypass this, specify -inform der:

$ openssl asn1parse -inform der -in request.der 
    0:d=0  hl=4 l= 342 cons: SEQUENCE          
    4:d=1  hl=4 l= 256 cons: SEQUENCE          
    8:d=2  hl=2 l=   1 prim: INTEGER           :00
...

If you try this and you still get an "offset too large" error, you may not be dealing with a certificate file at all, or the file itself may have been corrupted in transit.

View the contents of a certificate

Now, let's say that you've determined that you're dealing with a bona-fide X.509 certificate, and you've figured out what format it's in. asn1parse will give you all the details about the certificate file; probably more than you were looking for. What you really want is a summary — what is its validity period and what distinguished name is it a certificate for?

OpenSSL has a dedicated x509 subcommand for precisely this. However, If you just type:

$ openssl x509 -in sample.cer

You won't be too impressed by the results:

-----BEGIN CERTIFICATE-----
MIIEszCCA5ugAwIBAgIJAMR0RhX+M2iEMA0GCSqGSIb3DQEBBQUAMIGXMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCVFgxDjAMBgNVBAcTBVBsYW5vMREwDwYDVQQKEwgy
...
qRH8SwcC2/+kLp5/SqpC7Tv1K1466jrxltb4ncJL6Is8p2FDRn1GTLTj3Z086JgX
s9y/DGoyTw==
-----END CERTIFICATE-----

All this does is (fairly uselessly) show you the exact text contents of the certificate, just as if you had typed:

$ cat sample.cer

OpenSSL can display useful bits and pieces of the certificate as well, but you have to pass in the correct command-line attributes to see them. Some of the more useful ones are:

-subject
Output the subject name
-issuer
Output the issuer name
-dates
Output the validity dates of the certificate

However, by default these will all be printed below the dump of the full text contents of the Base64-encoded certificate file. The useful -noout (which, honestly, ought to be the default) suppresses this, so you just get the information you asked for. If you'd like a summary, which is a good assumption in most cases, you can pass in the -text option which displays a readable overview of all of the information in the certificate:

$ openssl x509 -in sample.cer -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            af:69:46:11:10:bd:82:88
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, ST=Texas, L=Plano, O=2xoffice, OU=Architecture, CN=Joshua Davies/emailAddress=joshua.davies.tx@gmail.com
        Validity
            Not Before: May 21 21:49:10 2014 GMT
            Not After : Jun 20 21:49:10 2014 GMT
        Subject: C=US, ST=Texas, L=Plano, O=2xoffice, OU=Architecture, CN=Joshua Davies/emailAddress=joshua.davies.tx@gmail.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (512 bit)
                Modulus (512 bit):
                    00:b7:38:0d:e0:ab:37:18:a7:26:95:9d:9e:6f:a2:
                    69:b1:b9:ee:b3:7f:29:04:fb:f0:94:b3:d0:d5:55:
                    c0:d8:6b:14:7f:94:13:3c:d9:a2:61:bf:ba:3f:0a:
                    44:37:dc:18:b5:23:c7:ee:96:2d:7c:d8:92:04:48:
                    74:f8:c6:46:a5
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                1A:A5:C9:C8:36:EA:7D:FA:B4:DF:A4:9C:11:F9:C1:BE:78:C4:42:DD
            X509v3 Authority Key Identifier: 
                keyid:1A:A5:C9:C8:36:EA:7D:FA:B4:DF:A4:9C:11:F9:C1:BE:78:C4:42:DD
                DirName:/C=US/ST=Texas/L=Plano/O=2xoffice/OU=Architecture/CN=Joshua Davies/emailAddress=joshua.davies.tx@gmail.com
                serial:AF:69:46:11:10:BD:82:88

            X509v3 Basic Constraints: 
                CA:TRUE
    Signature Algorithm: sha1WithRSAEncryption
        56:32:44:76:86:8c:08:92:74:71:0e:ac:a6:7d:ba:1d:7c:d3:
        b6:74:ef:27:7a:5e:53:21:fc:8e:eb:26:58:e0:6e:4f:5c:01:
        f1:40:ca:0a:e9:d2:0e:00:60:ae:1f:f6:a5:a4:4c:47:fb:e0:
        68:7f:25:63:ab:60:38:0f:74:94

Create a self-signed test certificate

For production use in a real public-key infrastructure (like the one that the Internet relies on), a certificate must be signed by a trusted root certificate authority like Verisign or Thawte. However, these certificates are expensive and time-consuming to obtain; it's perfectly reasonable to want to be able to test security out in a closed environment using a "fake" certificate. You can do this by creating a "self-signed" certificate — one that defines a public key and then includes a signature using that same public key. Such a certificate is perfectly valid — a browser can verify its validity — although it doesn't assert anything useful since anybody can create one. However, since they're so useful for testing purposes, OpenSSL includes a shortcut command to create one with the -x509 option to the req subcommand.

$ openssl req -x509 -newkey rsa:2048
Generating a 512 bit RSA private key
...++++++++++++
..++++++++++++
writing new private key to 'privkey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
...

The -newkey option tells OpenSSL that you want it to generate a private key as well; if you forget it, OpenSSL will hang waiting for you to input a private key.

This will output the certificate to stdout, which is probably not what you want; you should provide a -out parameter as well.

$ openssl req -x509 -newkey rsa:2048 -out selfsign.cer

Also, the private key will go to a file named privkey.pem, overwriting any identically-named file that happens to be there (with no warning!) This can be frustrating, especially if you're already using the corresponding certificate. So, get in the habit early of always specifying a private key file:

$ openssl req -x509 -newkey rsa:2048 -out selfsign.cer -keyout selfsign.key

In fact, if you'd like to get SSL up and running for a test Apache2 instance (for testing ONLY!), uncomment the line:

#Include /private/etc/apache2/extra/httpd-ssl.conf
in /etc/apache2/httpd.conf and run the command:
$ openssl req -x509 -newkey rsa:2048 -out server.crt -keyout server.key

You'll be prompted for a few elements of the subject; the only one that matters is the Common Name; must be "localhost".

Create a certificate request for upload to a certificate authority

Of course, eventually, you're going to need to use a real certificate. If you install a self-signed certificate in a web server, for example, it will sort of work, but the browser will present a dire warning message scaring users away from your site:

Figure 1: Chrome's untrusted certificate warning message

Even if the user clicks through the "Proceed Anyway" message, some parts just won't work at all in some browsers (Chrome won't load images from your unprotected site if the page was loaded as HTTPS, for example). This is workable while testing, but for a real site, you need a real certificate. You can't create a "real" certificate by yourself — you need to have one signed by a "real" certificate authority. I use GoDaddy because they're cheap and easy — but before you can use them, you first need to create a certificate signing request (CSR). The CSR contains all the information that you want the end certificate to contain including the public key and the domain name of the site being protected, but without the signature. You create one just as you created the self-signed certificate above, but omitting the -x509 option:

$ openssl req -newkey rsa:2048 -out selfsign.cer -keyout selfsign.key

This command form creates a PKCS #10 file (AKA certificate signing request), which you should upload to the certificate authority. Procedures vary from one CA to the next, but in general they'll let you upload a CSR file to a web form as the first step. The CA will do some verification that you are authorized to create a certificate identifying the site in the first place and when finished, will make a signed certificate available to you. Note that there's nothing sensitive about this certificate — it doesn't contain the private key (you generated that separately when you generated the certificate) and in fact, is a public piece of information that your server will present to every single client that tries to connect using HTTPS.

Remember what I said before about being frustrated when accidentally overwriting your private key file? Well, it's more than frustrating when you generate a CSR, submit it to an expensive CA like Verisign and then accidentally overwrite it. Remember, a private key file cannot be recovered, ever, if lost. If it could, the entire architecture that SSL relies on would be broken. If the private key file is lost or overwritten, the certificate that it goes with is useless. Managing backups is a big part of a server administrators job — managing backups of secure files like private key files is an even bigger part.

Sign a certificate request (as a certificate authority)

In more advanced usage, SSL permits clients to authenticate themselves by presenting certificates, rather than just the server. In this case, the client certificates must be signed by a certificate authority to be trustworthy and, although you can certainly create a request for a commercial certificate authority in this case, you can save quite a bit of money by acting as your own mini certificate authority. OpenSSL provides the tools to do so.

Somewhat by accident, actually. The documentation states that these tools were never intended to be used as a real certificate authority, but just to demonstrate that it could be done. From the man page:
"The ca utility was originally meant as an example of how to do things in a CA. It was not supposed to be used as a full blown CA itself: nevertheless some people are using it for this purpose."
However, for casual/light use, this works just fine; if you need it to scale to multiple administrators and certificate revocation, however, you'll need a more robust certificate management infrastructure.

The first step is to create a self-signed root certificate to act as the certificate authority as shown above. Once this has been done, create a CA configuration file. OpenSSL installs a sample openssl.cnf file in its configuration directory (which varies from one installation to the next). For the most part, especially for testing purposes, you can just use this sample configuration file as is; if you're going to be spending much time dealing with certificates, though, it's worth getting acquainted with exactly what is in this file. Some of the interesting options, at least with regards to certicate authority management, are the location of the serial numbers and the location of the Certificate Revocation List. I won't review all of the options here (and to be honest, there are a few I'm not that familiar with myself), but for the most part you can get away with accepting the defaults, especially for non-production (i.e. test) usage.

However, some of the options in the [ CA_default ] section refer to directories and files which, if not present, will cause the CA to fail, so you must create them before you can sign a CSR. The OpenSSL source distribution ships with a simple perl utility called CA.pl that simplifies this process, but all it's really doing is creating the directory structure that the default openssl.cnf expects. The relevant section is:

mkdir demoCA
mkdir demoCA/certs
mkdir demoCA/crl
mkdir demoCA/newcerts
mkdir demoCA/private
touch demoCA/index.txt
echo "01" | demoCA/crlnumber

This is enough to get a basic sample CA up and running; if you don't like the directory or filenames, each can be changed in openssl.cnf. In particular, you may want to change dir to something more meaningful than demoCA.

Finally, once you have the configuration file and a valid CSR, sign the certificate signing request:

openssl ca -config ca.cnf -in csr.pem -out certificate.pem

The certificate can be sent unsecured to the requester — remember that one of the reasons PKI works is that the security is in the private key, so the CSR and the certificate are not sensitive data.

If you take a look at the generated certificate, you'll notice that there's a text summary (identical to what openssl x509 -noout -text would generate) at the top. This section, included by default, confuses some systems. You can include the -notext option to omit this — or just delete it by hand if you forget (it's not necessary).

So, from start to finish, here are the minimum steps you need to take in order to create your own certificate authority:

  1. Create the configuration file. The smallest configuration file that will allow you to sign certificates is:
    [ ca ]
    default_ca = miniCA
    
    [ miniCA ]
    certificate = ./cacert.pem
    database = ./index.txt
    private_key = ./cakey.pem
    new_certs_dir = ./certs
    default_md = sha1
    policy = policy_match
    serial = ./serial
    default_days = 365
    
    [policy_match]
    commonName = supplied
    
  2. Create the directory structure:
    $ mkdir certs
    $ touch index.txt
    $ echo "01" | serial
    
  3. Create the root certificate:
    $ openssl req -x509 -newkey rsa:2048 -out cacert.pem -keyout cakey.pem
    Generating a 2048 bit RSA private key
    ...........+++
    .................................................+++
    writing new private key to 'cakey.pem'
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:
    -----
    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]:Texas
    Locality Name (eg, city) []:Plano
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:2xoffice
    Organizational Unit Name (eg, section) []:Architecture
    Common Name (e.g. server FQDN or YOUR name) []:Certificate Authority
    Email Address []:joshua.davies.tx@gmail.com
    
    Note that, if you intend to use this CA for anything resembling a secure purpose, you must protect the cakey.pem with a strong password and make sure to use the OS to protect it as well:
    $ chmod 400 cakey.pem
    
    The certificate itself (cakey.pem in this case) will be distributed to the endpoints that will be validating the signed certificates; e.g. the web servers that are configured to request client certificates.
  4. Create a certificate signing request:
    $ openssl req -newkey rsa:2048 -out csr.pem -keyout privkey.pem
    
  5. Finally, sign the request. Before you do, you may want to take note of the directory contents of the CA directory, just to get a sense of what signing a certificate does with regards to the CA directory:
    $ ls
    ca.cnf      certs       index.txt   serial
    cacert.pem  cakey.pem
    
    Sign the request:
    $ openssl ca -config ca.cnf -in csr.pem -out signed.pem
    Using configuration from ca.cnf
    Enter pass phrase for ./cakey.pem:
    Check that the request matches the signature
    Signature ok
    The Subject's Distinguished Name is as follows
    countryName           :PRINTABLE:'US'
    stateOrProvinceName   :PRINTABLE:'Texas'
    localityName          :PRINTABLE:'Plano'
    organizationName      :PRINTABLE:'2xoffice'
    organizationalUnitName:PRINTABLE:'Architecture'
    commonName            :PRINTABLE:'Joshua Davies'
    emailAddress          :IA5STRING:'joshua.davies.tx@gmail.com'
    Certificate is to be certified until May 29 14:54:35 2015 GMT (365 days)
    Sign the certificate? [y/n]:y
    
    1 out of 1 certificate requests certified, commit? [y/n]y
    Write out database with 1 new entries
    Data Base Updated
    
    And take a look at the updated directory contents:
    $ ls
    ca.cnf    certs   index.txt.attr  serial
    cacert.pem  index.txt.old serial.old
    cakey.pem index.txt signed.pem
    
    index.txt.attr, index.txt.old and serial.old are new, added by the ca command. Also, the certs directory now contains a copy of the signed certificate:
    $ ls certs/
    01.pem
    
    This file is identical to the generated signed.pem file.

It's also worth noting that the signed certificate's subject is simply:

$ openssl x509 -in signed.pem -noout -subject
subject= /CN=Joshua Davies

This doesn't match the subject of the request:

$ openssl req -in csr.pem -noout -subject
subject=/C=US/ST=Texas/L=Plano/O=2xoffice/OU=Architecture/CN=Joshua Davies/emailAddress=joshua.davies.tx@gmail.com

The reason is the [ policy ] section of the configuration file; since I only listed commonName in the policy section, that's the only attribute that's output. If I wanted to allow the requester to include other attributes, I'd have to list them individually:

countryName = optional
stateOrProvinceName = optional

The important takeaway here is that the subject of the signed certificate won't necessarily be identical to the subject in the request; only the public key is guaranteed to be propagated forward.

This minimal CA leaves a lot to be desired; in particular, I haven't added any support for certificate revocation. This is meant to be illustrative to make the certificate authority process clearer, not intended for production use. You've been warned.

Sign a certificate with a back-dated start date

One issue I've run into with certificates has to do with time zones; by default, when a certificate is signed, its notBefore timestamp is the time on the server when it was created, in the timezone of the server. I've run into scenarios where I wasn't able to install a certificate on a server located on the west coast because it was signed by a certificate authority located on the east until a few hours had passed. Not life-threatening, but annoying, and the OpenSSL ca command allows you to "backdate" a certificate via:

$ openssl ca -in csr.pem -startdate 140529000000Z

Provide subject information on the command line

If you've been following along, creating test certificates (and maybe even submitting one to a commercial CA!), you've probably gotten a bit tired of typing in the country, state, locality, etc. over and over again. You can pass this information in on the command line; it doesn't save much time since you still have to type it in, but you can take advantage of this to create an automation script:

#!/bin/sh

if [$# -ne 1]
then
  echo "Usage: $0 "
  exit
fi

openssl req -subj /C=US/ST=Texas/L=Plano/O=2xoffice/OU=Architecture/CN=$1

The codes here (C, ST, L, etc.) are from the X.509 distinguished name (DN) specification

CNCommon Name
LLocality (city)
STState (or province)
OOrganization (for example, company)
OUOrganizational Unit
CCountry

Technically speaking, the only value you must provide for an SSL certificate is the common name (CN), and it must match the server host name. So, you can get away with:

$ openssl req -x509 -newkey rsa:2048 -out server.crt -keyout server.key -subj /CN=localhost

However, I prefer to fill in at least the organization and organizational unit as documentation in case I ever have to determine what or where I created this certificate for in the first place. Another non-standard, but widely recognized attribute is emailAddress; there's no short form of this one, but you can type it out as in:

$ openssl req -x509 -newkey rsa:2048 -out server.crt -keyout server.key -subj /CN=localhost/emailAddress=joshua.davies.tx@gmail.com

This email address is strictly informational, but can be handy if it identifies the administrator when the certificate causes problems (such as when it expires).

Check to see if the public key in a certificate matches a private key

Once you start dealing with a reasonable volume of certificates — more than about ten or so, you'll run into issues where you're trying to match a private key with a certificate. You can deal with a lot of this via naming conventions (I include a timestamp in all of my certificate and key names), but you're likely to come across a certificate that came from a source outside your control at some point.

Reconciling these would actually be practically impossible, except for the fact that private key files (although they strictly don't have to) include the public key that corresponds to the private key. You can dump the entire structure of a private key file just as you can the structure of a certificate.

$ openssl rsa -in privkey.pem -noout -text
Enter pass phrase for privkey.pem:
Private-Key: (512 bit)
modulus:
    00:b7:ed:99:89:e3:37:fa:28:54:7e:1e:4c:72:1b:
    d1:b3:be:d1:99:50:59:83:8e:bf:8a:42:2f:42:fb:
    38:2a:d1:3f:31:9e:20:26:7f:2e:c2:02:62:e8:b4:
    8d:fc:1b:60:49:b6:78:f6:7d:74:21:56:c7:f5:f9:
    53:ae:e3:a9:59
publicExponent: 65537 (0x10001)
privateExponent:
    00:9e:98:13:b2:5f:e8:5e:9d:f3:ed:23:b7:0b:15:
    8d:c6:8d:9d:31:b3:a4:db:d9:74:b3:84:ca:37:d9:
    62:df:17:43:1a:56:21:eb:02:3e:44:04:b1:4c:48:
    72:ef:cc:9e:a2:e5:d3:1d:da:57:83:d8:48:ad:e0:
    19:8d:ec:55:01
prime1:
    00:eb:15:16:7b:3c:69:2b:df:a8:e3:78:98:a4:3b:
    c6:99:2e:24:ec:33:c4:52:e1:18:30:1a:06:3e:bf:
    98:23:71
prime2:
    00:c8:4b:46:90:06:6f:60:70:aa:46:1b:d4:b3:cb:
    bb:28:5d:14:f1:60:90:dc:09:c3:fc:7b:8d:92:80:
    7f:20:69
exponent1:
    00:8d:77:e8:4a:8b:45:43:48:da:6a:e1:75:02:48:
    92:b0:36:0b:b4:35:46:ed:15:56:a8:03:d1:44:4b:
    aa:73:91
exponent2:
    00:9f:b7:87:19:2a:48:7e:3a:d9:4c:f6:bc:72:73:
    2f:57:4c:82:7a:c8:6a:3b:4c:7e:40:43:b5:ec:f1:
    12:6e:a1
coefficient:
    00:88:14:47:88:9a:c1:31:b0:16:74:1a:2d:5d:9e:
    17:a9:c8:ef:c4:b4:72:4a:9d:8b:19:ec:c7:b2:4f:
    a8:54:eb

There's quite a bit of information in here — the first three entries are the public key (which takes the form of a modulus and a public exponent) and the private key (the private exponent) and then 5 values that represent the components of the private exponent and can be used to speed up private key calculations. Notice that you'll probably be prompted for a password — key files are stored in encrypted form by default. If you open the file in a text editor, you'll see that it starts out like this:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,DCD8BE451A81F09A

bc6tsFhLlJ9bouUgr6H4F4u8ROSqZBrIABX1/oUlqbESK30Ubjz2ZINCkpuyW6QS

Those entries at the top are a signal to the OpenSSL parser to prompt you for a decryption password. However, encrypted or not, key files should still be treated with the highest level of sensitivity and protected by the OS to the greatest extent possible — a determined adversary can brute force this, given enough time.

If you're trying to match a private key file to a certificate, though, all you really care about is the modulus — if the modulii match in the private key and the certificate, the key is the private key for the certificate. So output the public modulus of the private key:

$ openssl rsa -in privkey.pem -noout -modulus
Enter pass phrase for privkey.pem:
Modulus=B7ED9989E337FA28547E1E4C721BD1B3BED1995059838EBF8A422F42FB382AD13F319E20267F2EC20262E8B48DFC1B6049B678F67D742156C7F5F953AEE3A959

And that of the certificate:

$ openssl x509 -in selfsign.cer -noout -modulus
Modulus=B7ED9989E337FA28547E1E4C721BD1B3BED1995059838EBF8A422F42FB382AD13F319E20267F2EC20262E8B48DFC1B6049B678F67D742156C7F5F953AEE3A959
This works whether you're using RSA or DSA (but... why would you use DSA?)

Extract and output a private key

Certificates and their keys can be bundled in PKCS #12 format — when you export a client certificate from a browser, you'll get a PKCS #12 file, for example. This bundle includes the certificate and the private key in a single list; it may have an extension like .p12 or .pfx. You can parse, manage, and manipulate it with the pkcs12 OpenSSL subcommand. If you just want to dump the contents of the file on the console, use:

$ openssl pkcs12 -in private.pfx

As with the x509 and rsa subcommands, by default this just shows you a base64-encrypted representation of the certificate, followed by a base64-encrypted representation of the corresponding private key. Unlike x509 and rsa, though, there's no -text option to summarize the contents of the files. However, the output appears like this:

MAC verified OK
Bag Attributes
    friendlyName: Client Certificate
        localKeyID: A3 78 69 B4 A2 12 96 A7
subject=/CN=Joshua Davies/OU=Architecture/O=2xoffice
issuer=/CN=Joshua Davies/OU=Architecture/O=2xoffice
-----BEGIN CERTIFICATE-----
MIIFqTCCBJGgAwIBAgIIcho1mMr1AE0wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV
BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
...
vBdHySICRxmp3onOOj86v/+SdRyvHLc6Nl4aP6J/l4lF/IVfni2flWXgB5pPjBx1
f/T4I5bCAqzvQTdfPw==
-----END CERTIFICATE-----
...

The OpenSSL developers made OpenSSL's x509 smart enough to look for those BEGIN CERTIFICATE and END CERTIFICATE markers and parse the certificate in between them, so you can actually do this if you just want a quick summary of the certificate:

$ openssl pkcs12 -in private.pfx | openssl x509 -noout -text

If you do, you'll be prompted for the password for the .pfx file and then again for the password for the private key; since there's no reason to output the private key just to discard it, you can issue the -nokeys option to omit the prompt:

$ openssl pkcs12 -nokeys -in private.pfx | openssl x509 -noout -text

You can use the same piping trick to output the private key in summary form (there's even a -nocerts to omit the certificate if you'd like), but I can't think of a case where that would actually be useful, since you already have the certificate that corresponds to the private key.

Modify default parameters

I mentioned above that if you create a certificate request and don't specify a private key file name, the private key will go to privkey.pem by default. While that is the default, you can change the default default by changing the openssl.cnf file. I mentioned that there's a default one when I talked about certificate authorities above; this is actually used in addition to certificate authorities as the principal configuration file for all OpenSSL usage. It might be a bit tricky to find; it may be under /etc/pki/tls or /usr/share/ssl, for example.

If you look under the [ req ] section, for example, you'll find this entry:

default_keyfile = privkey.pem

As I mentioned above, if you forget to provide the -keyout attribute when generating self-signed certificates or signing requests, OpenSSL will overwrite any existing privkey.pem file that already may exist. One way to avoid ever allowing this to happen is to remove the filename completely:

default_keyfile = 

Now, if you try to generate a CSR without specifying -keyout, you'll get a command line error:

$ openssl req -x509 -newkey rsa:2048 -config openssl.cnf
Generating a 512 bit RSA private key
.++++++++++++
.............++++++++++++
writing new private key to ''
No such file or directory
2995:error:02001002:system library:fopen:No such file or directory:/SourceCache
/OpenSSL098/OpenSSL098-50/src/crypto/bio/bss_file.c:356:fopen('','w')
2995:error:20074002:BIO routines:FILE_CTRL:system lib:/SourceCache/OpenSSL098
/OpenSSL098-50/src/crypto/bio/bss_file.c:358:

Much better than clobbering any existing file that may have been laying around!

Add a comment:

Completely off-topic or spam comments will be removed at the discretion of the moderator.

You may preserve formatting (e.g. a code sample) by indenting with four spaces preceding the formatted line(s)

Name: Name is required
Email (will not be displayed publicly):
Comment:
Comment is required
keith, 2016-10-25
It would be nice if there was a helpfile that offered tips and tricks to windows server users istead of assuming everyone is on Linux. For instance, you won't get openssl (win) commands to work with the leading 'openssl'. Another common issue with Windows webserver which is hard to find info on is the decryption of the private key (sometimes they are just given to you to add to the server which you then have to decrypt), what -in / -out mean in the command string (file location)and making sure it matches the certifcate (I was given an apache cert in error).
margold, 2017-05-09
This was super helpful - really well explained and all in one place. Thanks!
Simphiwe Silingo, 2018-05-03
Can you please help with the following error: I have googled for help but could not find solution for this problem. Thanks in advance for everything.


openssl ca -config ca.cnf -in csr.pem -out signed.pem
Using configuration from ca2.cnf
Enter pass phrase for ./cakey.pem:
wrong number of fields on line 1 (looking for field 6, got 1, '' left)
Joshua Davies, 2018-05-10
Most likely, it's a malformed configuration file (ca2.cnf). If you post the contents of that here, I can probably take a pretty good guess where the problem is.
Jr, 2018-11-14
Hi Joshua

what are the drawbacks from creating a CA and signing like theses steps and your exampled way?
dst.lbl.gov/~boverhof/openssl_certs.html
Malx, 2019-01-03
Note that I got the
    Error: offset too large
error for the asn1parse command when the output certificate included the text representation as well.
I needed the -notext option on the openssl ca command.
Norseman, 2019-08-23
If i have a .pem file with multiple certificates in the chain in it, how would i refer to the 2nd certificate in the chain? By default openssl prints info for the first cert.
Josh, 2019-08-26
Usually when I see multiple certificates in a single file, they're in PKCS #12 (.pfx) format, which there's a specific command for in openssl. If you do have just a file with multiple "-- BEGIN CERTIFICATE --" "-- END CERTIFICATE --" entries, you can actually just split them up with a text editor to refer to each individually. Where did the PEM file you're dealing with come from?
Amit, 2021-04-23
Thanks to the Author of this article..
It's explained in a very detailed way to generate/exploring/verifying self-signed certificate
Hrutalee Surve, 2022-08-09
I've read your post and you've appropriately explained the SSL. I'd like to share more information on SSL. If your users are looking for more information, maybe my article would help them out. Would you mind if i share the article?
Clive, 2022-09-12
Hi Joshua,

Found my way to this article while trying to diagnose an error/warning relating to a self-signed certificate I'm using on an in-house development server. It's been a while since I last used the host and now both Firefox and Edge are throwing up an "Error Code: SSL_ERROR_BAD_CERT_DOMAIN"...

After an inevitable search for clues based on the error message, I amended my CSR statement to:-

openssl req -new -addext "subjectAltName = DNS:nc2.{mydomain}.net" -key private-key.pem -out signing_request.csr

Obviously this didn't work... Although it's a bit sketchy on my part, the evidence I'm seeing sort-of points to the fact that maybe I haven't given my CSR the right set of optional parameters to satisfy a heightened bar of requirements being set by e.g. Firefox and Edge.

I don't think it is relevant, but I'm using the Texadactyl CA, "DIYCA", hosted at GitHub, which I've found to be excellent. Thought perhaps you might consider revisiting this with a thought to exploring this new security requirements in a little more detail.
My Book

I'm the author of the book "Implementing SSL/TLS Using Cryptography and PKI". Like the title says, this is a from-the-ground-up examination of the SSL protocol that provides security, integrity and privacy to most application-level internet protocols, most notably HTTP. I include the source code to a complete working SSL implementation, including the most popular cryptographic algorithms (DES, 3DES, RC4, AES, RSA, DSA, Diffie-Hellman, HMAC, MD5, SHA-1, SHA-256, and ECC), and show how they all fit together to provide transport-layer security.

My Picture

Joshua Davies

Past Posts