Certificate Authority on SmartOS

Certificate Authority on SmartOS

Running your own Certificate Authority is truly the best option if you're managing a large number of systems which need to have secured, authenticated communication flowing between them.  While it can be a fair amount of work to setup and maintain, it is much cheaper than signing each of your leaf certificates with an external Certificate Authority, and much more secure than using self-signed certificates.

It is recommended that you read these other blog posts before starting, as they will cover some of the more general knowledge of X.509 which will help us proceed with setting up a CA.

Also, I recommend checking out Jamie Nguyen's guide on setting up an OpenSSL Certificate Authority which is focused on creating an RSA based certificate authority.  This guide will create both RSA and ECDSA based certificate authorities.

Why both?

Because of its size and performance properties, ECDSA is likely to become the dominate cryptosystem, however, it currently isn't nearly as widely supported as RSA is.  As well, ECDSA and RSA have different performance properties under different operations, so in some circumstances, RSA is superior in performance to ECDSA, and will likely remain so.

We will prefer to use our ECDSA chain of trust, with the RSA chain being used as a backup, and both being used where a service supports using both.  Since most of the effort of operating a Certificate Authority is in securely distributing the root certificates in the first place, why not just distribute two keys instead of one?

Step 0: Have a Plan

Most of the work of running a Certificate Authority comes down to management and compartmentalization.  Spending a few minutes to plan this out can really help.  Before we go too much further, let's take a quick look at possible ways to configure a Certificate Authority.

Direct Certificate Authority

A direct certificate authority or a root-leaf certificate authority uses a single self-signed long-lived root certificate generated on a secured system that directly signs and revokes certificates used by services.  This is the simplest form of Certificate Authority, but unfortunately also the least secure and least flexible.

Since service CSRs are signed directly by the root certificate, each CSR must be transferred to and each Certificate must be transferred from the system containing the root key, which could be prohibitively inconvenient.  If your security methodologies fail and the root certificate private key is compromised, then the entire trust network fails.


  • Simple to administer
  • Only requires a dedicated system for the Root Certificate


  • Inflexible
  • Relatively inconvenient
  • Potentially insecure


  • Setup a self-signed root certificate
  • Distribute the root certificate
  • Fulfill leaf CSRs with the root key

Delegated Certificate Authority

A delegated, intermediate, or a root-branch-leaf certificate authority uses a single self-signed long-lived root certificate generated on a secured (and preferably air-gaped) system that signs medium-lived intermediate certificates, delegating authority them to sign and revoke short-lived service certificates.  This is the most common approach, and is only slightly more complicated than the above method, but allows for much more robust fulfillment of CSRs, and overall quite flexible, which is why it's often highly recommended.

In this approach, end entity CSRs are signed by intermediate keys, which can be kept much closer to the action, in their own dedicated limited-access VM for instance.  The intermediate certificates are in turn created by the root key, which can still be kept on an air-gapped system and used only to issue new intermediate certificates.


  • More flexible than a direct certificate authority
  • Convenient
  • Secure


  • Slightly more complicated than a direct certificate authority


  • Setup a self-signed root certificate
  • Setup an intermediate certificate
  • Distribute the root certificate
  • Fulfill leaf CSRs with the intermediate key

We will be using the Delegated Certificate Authority approach.

Cross-Signed Certificate Authority

There are certain conditions where the flexibility provided by the delegated certificate authority isn't sufficient:

  • A new public Certificate Authority is beginning operation and doesn't yet have a widely distributed root certificate.  This is the case with the Lets Encrypt public certificate authority, which has cross-signed one of their intermediate certificates.
  • Two private Certificate Authorities wish to use the PKI to authorize (or revoke authorization for) certificates from each other.  In such a case, intermediate certificates from each certificate authority could be signed by the other to authorize them.  While this may seem like an awesome idea, it's an excessive amount of work, and mainly included here for completeness.


  • The most flexible configuration
  • Secure


  • By far the most complicated
  • Signing intermediate keys and providing bundles for leaf keys is not simple
  • The overall management cost is prohibitive

The major complexity here is that certificates need to be issued from multiple intermediate keys or root keys, and must be presented as a combined bundle to the end recipient.  This isn't really that much more complicated than the Delegated Certificate Authority described above, but there is really very little point in planning something like this, as this is usually developed temporarily as a new CA is coming on-line.

Root Certificate

Our root certificate will serve as the backbone of trust within our Certificate Authority.  This is our planned schedule for the naming and use of our root certificate:

NameDeployed OnValid FromValid ToKey-size
Cospix Root 2040 ECC2016-02-292016-01-012040-01-01384-bit
Cospix Root 2040 RSA2016-02-292016-01-012040-01-013072-bit
Cospix Root 2060 ECC2036-01-012036-01-012060-01-01unknown

According to the appropriate key size section in our previous article, an RSA key of approximately 1536-bit could be compromised by 2040.  A minimum RSA key-size of 3072-bit is recommended to ensure adequate security through 2040.  Due to the relative immaturity of attacks on ECDSA cryptosystems as well as its advantageous performance characteristics, we opt for using a 384-bit ECDSA root certificate (instead of the 3072-bit RSA comparable 256-bit certificate).

These root certificates have the job of anchoring the entire trust network, but the root keys only have three jobs:

  • Issue Intermediate Certificates
  • Issue Certificate Revocation Lists
  • Stay Available and Secure

I can't stress that last one enough.  You need to ensure that your root key will be secure, hence our triple redundancy and heavy use of physical safes and encryption.  Persistence in the LiveCD environment is attained by using Removable USB flash drives.

We are also planning for the replacement of our 2040 key with a 2060 key in 2036.  This is done so that we have four years of overlap to distribute our 2060 key.

The certificate is included on our container images.

Intermediate Certificate

Our intermediate certificate acts as the delegated agent of the root, signing leaf certificates:

NameValid FromValid ToKey-size
Cospix Intermediate 2020 ECC2016-01-012020-01-01256-bit
Cospix Intermediate 2020 RSA2016-01-012020-01-012048-bit
Cospix Intermediate 2022 ECC2018-01-012022-01-01256-bit
Cospix Intermediate 2024 ECC2020-01-012024-01-01256-bit

The Intermediate Certificates are signed with a 4-year lifespan.  Additionally, they are replaced every 2 years with new Intermediate Certificates.  This is so that end certificates signed with the old intermediate keys remain valid.  Additionally, this gives us the option of upgrading the strength of our intermediate keys if there are indications that a given key-size is no longer secure.

These keys reside on dedicated VMs (specifically a SmartOS Zone) with highly restricted (but still online) access; they are the primary certificate signing keys in the Certificate Authority.

These certificates are normally restrained to a path length of one so that they can only be used to issue service certificates and not certificates that could be used in an additional CA.  The root key is NOT restrained like this though, so if we need to perform cross-signing, we can generate a new cross-signing key (with a constrained path length of 2) for another organization or sub-organization if need be.

Some thoughts about security

There are three basic attacks that you need to be concerned with when running a Certificate Authority:

Having your private key read by an unauthorized party

If an unauthorized party gains read access to a private key, they can issue unauthorized certificates and revoke authorized certificates under that key.  While this is definitely a problem at the intermediate level, it's an absolute disaster on the root level, where the recourse of revoking and re-issuing is MUCH more complicated.  This is why we insist on encrypting our keys wherever possible, as an attacker must first defeat the encryption on any key that they have managed to acquire before making use of it.  Brute-forcing technologies have made that easier today than ever before.

Having your private key deleted by an unauthorized party

While not quite as bad as our first point, if a private key is deleted anywhere along the chain, the area under that point is effectively immutable by your organization.  This means that no additional keys or revocation lists can be issued.  While this isn't a horrible deal for an intermediate key, it's a big problem for a root key.

Having your root certificate intercepted by an unauthorized party during deployment

This attack is the easiest to detect and guard against, but shouldn't be discounted because of this ease.  If an attacker can add additional public keys into your root key distributions, they can gain the same trust that you have.  This is easy to detect because there will either be additional keys or certificates issued under your own chain of trust will fail to validate.

We will discuss these later on as the methods of combating them become relevant.

Step 1: Setup the Intermediate Keys & CSRs

While this may seem like it's being done out of order, we actually want to start setting up our intermediate certificate before we set up our root certificate.  This is because we'll be able to create and transfer the intermediate CSR and the OpenSSL configuration into our air-gapped laptop on a USB drive, perform our operations, and then transfer our newly issued certificates back out via the same USB drive.

We will be running our CA's intermediate certificate from a minimally exposed SmartOS Zone hosted on a secure hypervisor.  For the rest of this example, we'll be using a fresh base-64-lts instance with a limit of 128 MB of memory and 1-2GB quota.  Networking is optional and depends on your context.

Notice: The global zone this runs under should be physically and logically secure, since anyone with access to the global zone can read, alter, or destroy any records within that zone.  Additionally, you will want to be careful about backing this zone up, since anyone with access to the backups could potentially gain access to your private key.

It's probably best practice to spin up a new Zone per generation of an intermediate certificate.  In our case, we're going to use one Zone to host both RSA and ECDSA intermediate certificates.  Once your zone has been initialized, login and continue

Create File & Directory Structure

Since this zone will be dedicated to our CA, we're going to set up shop in the system OpenSSL directory, located at /opt/local/etc/openssl/.  Since the only purpose of this zone is handling this intermediate authority, we might as well symlink from the root home directory.

# ln -sf /opt/local/etc/openssl ~
# cd ~/openssl
# mkdir -p ecc/certs rsa/certs
# chown 700 ecc rsa
# touch ecc/index.txt rsa/index.txt
# echo 00000001 > ecc/serial
# echo 00000001 > rsa/serial
# echo 00000001 > ecc/crlnumber
# echo 00000001 > rsa/crlnumber

Replace ~/openssl/openssl.cnf with the following configuration file.  Be sure to change your Distinguished Name defaults as appropriate:

Generate Keys

Unless you will be automating the production of certificates at this level, these keys should be encrypted for storage.  While the highest performing 256-bit ECDSA curve is secp256k1, we will be using the prime256v1 curve instead.

# openssl ecparam -name prime256v1 -genkey | openssl ec -aes256 -out ./ecc/private.key

Note: The use of a pipe here means the unencrypted key is never written to disk.  Since ZFS is a copy-on-write filesystem, that could leave unencrypted data available for a through attacker.  While this is a serious concern, it can only be exploited by someone with administrator or physical access to your hypervisor.  That's beyond the scope of adversary we're protecting against with this specific certificate.

We will also create the RSA private key.

# openssl genrsa -aes256 -out ./rsa/private.key 2048

Generate CSRs

We will now generate some CSRs to pass to our root key.  For both ECC and RSA.

# openssl req -new -key ecc/private.key -subj "/C=US/O=Cospix LLC/CN=Cospix Intermediate 2020 ECC"
# openssl req -new -key rsa/private.key -subj "/C=US/O=Cospix LLC/CN=Cospix Intermediate 2020 RSA"

Copy these Certificate Requests (I'm calling mine cospix_intermediate_ecc.csr and cospix_intermediate_rsa.csr) and a copy of openssl.cnf to a USB drive you're going to use to transfer CSRs and Certificates between your air-gapped laptop and your intermediate CA as described in the next section.

Note: While nothing sensitive is being transferred with this drive, we should be mindful that no third-party alters any of the CSRs before they're transferred to your air-gapped laptop, as that could potentially lead to your root key signing an unauthorized third-party key.  The easiest way to do this is to visually compare the CSR data between your terminal screens.

If you saved your CSRs into files, you could also run the following commands to compare the public key data.  You are specifically concerned about the section titled Subject Public Key Info.

# openssl req -in cospix_intermediate_ecc.csr -noout -text
# openssl req -in cospix_intermediate_rsa.csr -noout -text

Step 2: Signing with your Root key

All operations involving your root key should be done on an unplugged, fully air-gapped laptop (that means all wireless functionality disabled) booted onto a LiveCD that supports ZFS and with USB drives that are normally kept physically secured that will store your root private key.  Mine are labeled and normally kept in safe deposit boxes when not in use.

Turn on the air-gapped laptop but do not insert CA drives into the laptop yet.  Wait for it to boot up before continuing.

Initialize CA Pool

Note: If you have already initialized your CA pool, you can skip this subsection.

Insert all your CA drives into your laptop. Determine their identifiers using rmformat (mine were c5t0d0 c6t0d0 c7t0d0 and c8t0d0.)  If OpenIndiana mounted them for you, unmount them before continuing.

Run the following command (with your drive identifiers):

# zpool create -O checksum=edonr -O compression=lz4 -O copies=3 ca \
mirror c5t0d0 c6t0d0 c7t0d0 c8t0d0

While the Edonr checksum is incredibly high performance and quite resistant to attack, it's not cryptographically secure, which is okay, since we're going to be signing a cryptographic manifest file with our private key later on anyways.  I prefer explicitly specifying lz4 as my compression algorithm, but you can use 'on' just as easily here.  By configuring a four-way mirror with copies=3, we establish 12 total copies of all of our data, making this a very robust and secure way of storing small volumes of data (Our root CAs shouldn't take much space at all, especially stored in this pool).

Mounting CA Pool

Note: Only follow this section if you skipped the previous section.

ZFS will update all drives in a mirror if it sees any of them out of sync (ie, lagging behind of changes seen in the others).  This means that if an attacker compromises one of your USB drives by mounting and changing anything within the filesystem, those compromises will be pushed to ALL of your USB drives.

This becomes a non-issue once ZFS encryption comes to Illumos, but in the meantime, we'll employ the following strategy:

If at any point something fails, run zpool export ca, remove the drive and place it in our 'failed' pile.  Skip to the next good drive and return to step 1.

  1. Insert one of your good USB CA storage drives.
  2. Import your CA pool using zpool import ca.  It should bring the pool up in a degraded state (since only one vdev will be available).
  3. Scrub the pool using zpool scrub ca.  This should only take a few moments and verify if there are any innocent data corruption issues.  Verify using zpool status ca.
  4. The following steps should be done once per organization using this CA pool.  Switch directories into each organization's dataset and perform the following actions:
  5. Verify the fingerprint of the root certificate, comparing with your hand-written copy you should have made before.
  6. Verify the signature file against the sum file.
  7. Verify the signature file against the dataset.

Commands are as follows:

# openssl x509 -in ecc/public.crt -noout -fingerprint
# openssl x509 -pubkey -in ecc/public.crt > /tmp/public.key
# openssl dgst -sha512 -verify /tmp/public.key -signature ca.sig ca.sum
# sha512sum -c ca.sum

If you find that all stored root CAs check out, you can safely plug all USB drives back in again.  ZFS will resilver any accidental or malicious corruption for us.

Mount Transfer Drive

I find that OpenIndiana takes a while but finally mounts my transfer drive.  If OpenIndiana doesn't automatically mount your drive when you plug it in, you'll want to use rmformat to identify it and the following commands to manually mount it:

# mkdir /media/tmp
# mount -F pcfs /dev/dsk/identifier /media/tmp

You may need to try p1 instead of p0.

Initialize Root Certificate Authority

Note: Only follow this section if you need to create a new root CA.

For organization, I prefer configuring a few datasets below the pool root.  This lets us easily manage multiple CA records if we need to, (note the structure of organization/certificate_name):

# zfs create -p ca/cospix/root_2040

We can now start creating the required directory structure:

# cd /ca/cospix/root_2040
# chmod 700 .
# mkdir -p ecc/certs rsa/certs
# touch ecc/index.txt rsa/index.txt
# echo 0001 > ecc/serial
# echo 0001 > rsa/serial
# echo 0001 > ecc/crlnumber
# echo 0001 > rsa/crlnumber

Copy the OpenSSL configuration file from the ferry drive into our working directory, we will be changing the following directives (update both instances):

dir              = /opt/local/etc/openssl/ecc
dir              = /opt/local/etc/openssl/rsa
unique_subject   = no

Will change to:

dir              = ./ecc
dir              = ./rsa
unique_subject   = yes

The keys we create here should always be encrypted for storage.  First, we will always be performing this step with manual intervention.  Second, we cannot guarantee that our media won't fall into adversarial hands.  This situation is perfect for the two-factor approach of needing to possess the USB drives while also knowing the password to the keys.

Note: ZFS is a copy-on-write filesystem.  You should NEVER write unencrypted private keys to this dataset because there's no easy guaranteed way to overwrite it.  Always use a pipe when generating ECC keys.

The highest performing 384-bit ECDSA curve is secp384r1, which is the curve we'll use here.  We will also create the RSA private key:

# openssl ecparam -name secp384r1 -genkey | openssl ec -aes256 -out ecc/private.key
# openssl genrsa -aes256 -out rsa/private.key 3072

Your root private keys will now generate CSRs that will be self-signed via openssl ca.  We will be using ca instead of req or x509 so that we can specify an absolute start date and absolute end date via command line, something we don't have the option to do with the alternative sub-commands.

# openssl req -config openssl.cnf -new -key ecc/private.key -out \
/tmp/cospix_root_ecc.csr -subj "/C=US/O=Cospix LLC/CN=Cospix Global Root 2040 ECC"
Enter pass phrase for ecc/private.key:

And now run that CSR through the openssl ca command.

# openssl ca -config openssl.cnf -name ecc -selfsign -notext -extensions v3ext_root \
-in /tmp/cospix_root_ecc.csr -out ecc/public.crt \
-startdate 20160101000000Z -enddate 20400101000000Z
Using configuration from openssl.cnf
Enter pass phrase for ./ecc/private.key:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 4096 (0x1000)
            Not Before: Jan  1 00:00:00 2016 GMT
            Not After : Jan  1 00:00:00 2040 GMT
            countryName               = US
            organizationName          = Cospix LLC
            commonName                = Cospix Global Root 2040 ECC
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
            X509v3 Authority Key Identifier: 

            X509v3 Basic Constraints: critical
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
Certificate is to be certified until Jan  1 00:00:00 2040 GMT (8627 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

Do the same with the RSA CSR.

# openssl req -config openssl.cnf -new -key rsa/private.key -out \
/tmp/cospix_root_rsa.csr -subj "/C=US/O=Cospix LLC/CN=Cospix Global Root 2040 RSA"
Enter pass phrase for rsa/private.key:


# openssl ca -config openssl.cnf -name rsa -selfsign -notext -extensions v3ext_root \
-in /tmp/cospix_root_rsa.csr -out rsa/public.crt \
-startdate 20160101000000Z -enddate 20400101000000Z

At this point, you should have public certificates in ecc/public.crt and rsa/public.crt.  Copy both of these files to your transfer drive (as cospix_root_ecc.crt and cospix_root_rsa.crt respectively).  They will serve as your global trust anchors for your entire Certificate Authority.

Record (by hand) the certificate fingerprints for verification purposes:

# openssl x509 -in ecc/public.crt -noout -fingerprint
SHA1 Fingerprint=E4:0A:1A:5B:64:5F:DF:26:EA:F3:C8:DD:E1:86:28:2E:C0:90:FC:3E
# openssl x509 -in rsa/public.crt -noout -fingerprint
SHA1 Fingerprint=F5:D7:69:DF:EE:1F:99:AF:EE:DC:2E:EA:EC:FC:2F:BC:3D:82:E7:C4

It's good to maintain paper records of this number, ensuring that nobody has snuck a forged root certificate into anywhere it doesn't belong (that'd be anywhere).

Issue Intermediate Certificate

First, you should confirm that the public key info of the CSRs as read from your transfer drive on your air-gapped laptop match those of your intermediate certificate environment.  Run the following commands and compare their output to your intermediate certificate terminal.

# openssl req -noout -text -in /media/Drive/cospix_intermediate_ecc.csr
# openssl req -noout -text -in /media/Drive/cospix_intermediate_rsa.csr

Assuming they match, issue the following command to read the CSR directly from the USB transfer drive, and issue a certificate right back onto it.

# openssl ca -config openssl.cnf -name ecc -notext -extensions v3ext_intermediate \
-in /media/tmp/cospix_intermediate_ecc.csr -out /media/tmp/cospix_intermediate_ecc.crt \
-startdate 20160101000000Z -enddate 20200101000000Z
Using configuration from openssl.cnf
Enter pass phrase for ./ecc/private.key:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 4097 (0x1001)
            Not Before: Jan  1 00:00:00 2016 GMT
            Not After : Jan  1 00:00:00 2020 GMT
            countryName               = US
            organizationName          = Cospix LLC
            commonName                = Cospix Global Intermediate 2020 ECC
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
            X509v3 Authority Key Identifier: 

            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Key Usage: critical
                Digital Signature, Certificate Sign, CRL Sign
Certificate is to be certified until Jan  1 00:00:00 2020 GMT (1322 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 do the same for the RSA certificate.

# openssl ca -config openssl.cnf -name rsa -notext -extensions v3ext_intermediate \
-in /media/tmp/cospix_intermediate_rsa.csr -out /media/tmp/cospix_intermediate_rsa.crt \
-startdate 20160101000000Z -enddate 20200101000000Z

If all of your work on this Certificate Authority root is complete, generate or update the ca.sum file.  This file will allow you to verify file integrity when next working on these files.

# sha512sum openssl.cnf */* */*/* > ca.sum

Next, sign it with the root private key to ensure that none of the CA files are either accidentally or maliciously compromised:

# openssl dgst -sha512 -sign ecc/private.key -out ca.sig ca.sum

Finally, snapshot the dataset with the current date.

# zfs snapshot ca/cospix/root_2040@r20160226

If you are done using your USB drives, be sure to zfs scrub them and ensure it completes successfully before exporting the ca pool.

# zpool scrub ca
# zpool status ca
  pool: ca
 state: ONLINE
  scan: scrub repaired 0 in 0h0m with 0 errors on ...
# cd /
# zpool export ca

You may now shutdown your laptop and physically secure your storage media.

Installing Intermediate Certificates

Copy the cospix_intermediate_ecc.crt file on your USB drive to ~/openssl/ecc/public.crt on  your CA zone and the cospix_intermediate_rsa.crt file on your USB drive to ~/openssl/rsa/public.crt on your CA zone.  You can additionally copy the root certificates as well, as ecc/root.crt and rsa/root.crt

Use the following command to verify your certificate chain:

# openssl verify -CAfile ecc/root.crt ecc/public.crt
ecc/public.crt: OK
# openssl verify -CAfile rsa/root.crt rsa/public.crt
rsa/public.crt: OK

We can also fingerprint our root certificates to ensure they're match the fingerprint we wrote down earlier:

# openssl x509 -in ecc/root.crt -noout -fingerprint
SHA1 Fingerprint=E4:0A:1A:5B:64:5F:DF:26:EA:F3:C8:DD:E1:86:28:2E:C0:90:FC:3E
# openssl x509 -in rsa/root.crt -noout -fingerprint
SHA1 Fingerprint=F5:D7:69:DF:EE:1F:99:AF:EE:DC:2E:EA:EC:FC:2F:BC:3D:82:E7:C4

Step 3: Distributing Root Certificates

Now that we've gone through all of this effort and verified that the certificate chain of trust is valid up through the intermediate certificate layer, we can distribute our root certificate.  This mainly involves placing it into the proper trust store for each platform you intend on using it with.

Unfortunately, this isn't nearly as straight forward as it could be.  One of the core weaknesses of the x509 trust model is that any of the trusted root certificates can potentially generate valid certification for any service.  There have been several high profile attacks that have utilized this approach, and several high profile attempts at moving away from the weakness of this trust model, but for now, none of them have become widely adopted enough to wrest domination away from the standard trust model.

Because of the uncertainly, this section will remain effectively blank.

Step 4: Fulfilling CSRs

I'm going to assume that you have used my previous guide to produce a CSR in the path ~/csr.pem.

First, we should read the CSR to see if there are any requested extensions:

# openssl req -text -noout -in ./csr.pem

OpenSSL will not transfer Requested Extensions between Certificate Signing Requests and Certificates, so we will have to manually establish our own.  If you'd like to declare extensions, I find that the -extfile parameter is the easiest way to do this with OpenSSL.  While this does mean you will need to set up an extension file for every certificate (or certificate pair) you sign, it's a whole lot easier to follow than alternatives I've seen.


basicConstraints        = critical, CA:false
keyUsage                = critical, digitalSignature, nonRepudiation, keyEncipherment, keyAgreement
extendedKeyUsage        = serverAuth, timeStamping
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always, issuer:always
subjectAltName          = DNS:one.example.net,DNS:two.example.net

Copy this template to another filename and edit it for your needs.

Notice: As of summer 2017, using the Common Name in the subject to identify a server resource is now depreciated.  You will now always want to specify one or more server hostnames using the subjectAltName extension.

To use, run the following command:

# openssl ca -notext -in csr.pem -out cert.pem -extfile filename.ext

This command will (optionally) ask you for a password to decrypt the intermediate private key and prompt you to actually sign and file the certificate.

If there are no extensions, I've also included some generic extensions in the above-linked openssl.cnf file:

  • v3ext_user: Useful for user or email authentication
  • v3ext_server: Useful for server authentication

To sign a server certificate (placed into cert.pem), run the following command:

# openssl ca -notext -in csr.pem -out cert.pem -extensions v3ext_server

If you want to issue an RSA certificate, include -name rsa in the above commands.

You will need to include the intermediate certificate along with the newly issued certificate when returning them to the requesting party.  They can be concatenated together.

$ cat ~/openssl/(ecc or rsa)/public.crt >> cert.pem