If you're running your own server, you're probably not shelling out $400 to get an “official” Certificate Authority (CA) to sign your key. Here's a quick note to myself about how to create, sign, and install your own CA and key. Depending on your application, you can use either the GnuTLS or OpenSSL toolchain.

GnuTLS

Following the GnuTLS manual, create a certificate authority with certtool, adjusting the cn as you see fit:

$ certtool --generate-privkey > x509-ca-key.pem
$ echo 'cn = GnuTLS test CA' > ca.tmpl
$ echo 'ca' >> ca.tmpl
$ echo 'cert_signing_key' >> ca.tmpl
$ certtool --generate-self-signed --load-privkey x509-ca-key.pem \
    --template ca.tmpl --outfile x509-ca.pem

Now generate the unencrypted server key.

$ certtool --generate-privkey > x509-server-key.pem

And sign the key with your CA, adjusting the cn as you see fit, and changing dns_name to match your fully qualified host name.

$ echo 'organization = GnuTLS test server' > server.tmpl
$ echo 'cn = test.gnutls.org' >> server.tmpl
$ echo 'tls_www_server' >> server.tmpl
$ echo 'encryption_key' >> server.tmpl
$ echo 'signing_key' >> server.tmpl
$ echo 'dns_name = test.gnutls.org' >> server.tmpl
$ certtool --generate-certificate --load-privkey x509-server-key.pem \
    --load-ca-certificate x509-ca.pem --load-ca-privkey x509-ca-key.pem \
    --template server.tmpl --outfile x509-server.pem

You can also print certificates with certtool.

$ certtool --infile x509-server.pem --certificate-info

You can add alternate hostnames using subject alternative names. Just add more dns_name entries to your template:

$ echo 'dns_name = other.gnutls.org' >> server.tmpl
$ certtool --generate-certificate …

You can verify a certificate if you can supply the whole certificate chain.

$ certtool --verify-chain --infile x509-server.pem --infile x509-ca.pem

With versions of GnuTLS since 2.99.0 (released 2011-04-09), you can verify against the global list of trusted CAs.

$ certtool --verify --load-ca-certificate /etc/ssl/certs/ca-certificates.crt --infile x509-server.pem

OpenSSL

Use openssl's genpkey to generate an unencrypted key.

$ openssl genpkey -algorithm RSA -out key.pem

An unencrypted key is less secure, but it allows the web server to be restarted (e.g. after rebooting) without you being there to enter the decryption key. Make sure key.pem is only readable by root.

Use req to generate certificate signing request.

$ openssl req -new -key key.pem -out req.pem

-new prompts you for new relevant field values. You can also specify the values on the command line or in an configuration file (override the default with -config filename).

Use x509 to sign the certificate.

$ openssl x509 -req -days 365 -in req.pem -signkey key.pem -out cert.pem

You should keep your certificate signing request around so you can re-sign your key later on (since your initial signature will eventually expire).

You can add subject alternative names to the request using the openssl.cnf config file:

$ cp /etc/ssl/openssl.cnf openssl.cnf
$ emacs openssl.cnf
$ cat openssl.cnf
…
[ req ]
…
req_extensions = v3_req
…
[ v3_req ]
…
subjectAltName = DNS:test.gnutls.org,DNS:other.gnutls.org
…
$ openssl req -new -config openssl.cnf -key key.pem -out req.pem

You can also print certificates with x509.

$ openssl x509 -in cert.pem -noout -text

You can verify a certificate if you can supply the whole certificate chain with verify.

$ openssl verify cert.pem

PEM

We've been throwing around a lot of files with a .pem extension. These files contain Base64 encoded DER certificates enclosed between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----. The .crt files requested by update-ca-certificates (discussed below) are generally binary DER files, but can also be base64 encoded.

Downloading certificates

You can download certificates from remote servers using OpenSSL's simple client:

$ openssl s_client -showcerts -connect host:port

The PEM certificates will be printed to your terminal.

System wide certificates

The situation is confusing because there is currently no standardized way to track trusted certificates. Here are some things to keep in mind:

ca-certificates

Gentoo uses Debian's ca-certificates package to manage trusted CAs. The certificates are stored under /etc/ssl/certs in three forms:

  1. As a <filename>.pem symlink to the external certificate (if it was already base64 encoded) or an encoded version (if the external certificate is binary).
  2. As a symlink to 1 named with the certificate's hashed subject name, key identifier (if present), and serial number.
  3. As an entry in ca-certificates.crt, which is a concatenated list of all the trusted certificates.

You can update these system certificates using update-ca-certificates, from the the ca-certificates package:

# update-ca-certificates

On Fedora-based systems, update-ca-certificates also updates the system NSS storage at /etc/pki/nssdb (more on NSS in a second).

update-ca-certificates looks in a number of places to find trusted CAs. To add a new CA, place the certificate in /usr/local/share/ca-certificates/ as a single file ending in .crt and run update-ca-certificates.

GnuTLS

In GnuTLS, you set the list of trusted CAs using gnutls_certificate_set_x509_trust_file. By convention this function is pointed to the /etc/ssl/certs/ca-certificates.crt file mentioned above.

OpenSSL

OpenSSL loads trusted CAs with SSL_CTX_load_verify_locations, which loads certificates from a file (like gnutls_certificate_set_x509_trust_file) or from a directory (usually /etc/ssl/certs/). For efficiency, many applications prefer the directory approach, as certificates can then be loaded on an as-needed basis using their hashed subject name, key identifier (if present), and serial number. If you place a new CA this directoy, you'll want to run c_rehash to generate the hashed symlinks:

# c_rehash /etc/ssl/certs

OpenSSL doesn't currently provide a man page for c_rehash, but there's one attached to Debian bug 215618.

update-ca-certificates (mentioned above) uses c_rehash internally to generate hashed symlinks for OpenSSL.

NSS

NSS is the crypto library used by Firefox and Chromium that doesn't use the /etc/ssl/certs framework discussed above. It looks in a number of places depending on the package type, but the system-wide sql:/etc/pki/nssdb. User applications will also look in sql:~/.pki/nssdb. You manage the databases with certutil. You can install certutil on Gentoo by emerging dev-libs/nss with the utils USE flag enabled. Add your certificate with something like:

$ certutil -A -n jdoe -t "C,," -d "sql:${HOME}/.pki/nssdb" -i /etc/ssl/certs/cacert.org.pem

This adds a certicate (-A) to the sql:${HOME}/.pki/nssdb database (-d). The certificate is named jdoe (-n), and you can use this name to manage the certificate later (e.g. to delete it). -t "C,," sets SSL trust to C, but does not grant email or object signing trust. C means you trust this certificate to issue SSL server certificates (but not client certificates, which would be T), and that you don't trust it for direct SSL authentication or email signing (which would be u).

Unfortunately, many applications using NSS have a compiled-in list of trusted CAs, so you don't have complete control from a sysadmin perspective unless you tweak that file at compile time.