/ 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 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 running 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.

Single Key Certificate Authority

A single key or a root-leaf certificate authority uses a single self-signed long-lived key-pair (root) generated on a secured (and preferably air-gaped) system that directly signs and revokes service (leaf) keys. This is the simplest form of Certificate Authority, but unfortunately also the least secure and least flexible.

Since leaf CSRs are signed directly by the root certificate, each CSR must be transferred to and each Certificate must be transferred from the air-gapped root key system, which could prove inconvenient. If your security methodologies fail and the root certificate private key is compromised, then the entire trust network fails (as is the case with the compromise of any anchoring key).

  • Pros:
  • Simple to administer
  • Only requires a dedicated system for the Root Certificate
  • Cons:
  • Inflexible
  • Relatively inconvenient
  • Potentially insecure
  • Todo:
  • Setup a root key-pair
  • Fulfill leaf CSRs with the root key

Delegated Key Certificate Authority

A delegate key, intermediate key, or a root-branch-leaf certificate authority uses a single self-signed long-lived key-pair (root) generated on a secured (and preferably air-gaped) system that signs medium-lived intermediate (branch) keys, authorizing them to sign and revoke short-lived service (leaf) keys. This is the recommended approach, and is only slightly more complicated than the above method, but allows for much more robust fulfillment of CSRs, and overall quite flexible.

In this approach, leaf CSRs are signed by branch keys, which can be kept much closer to the action, in their own dedicated limited-access VM for instance. The branch 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 branch certificates.

  • Pros:
  • Somewhat more flexible than a single key
  • Convenient
  • Secure
  • Cons:
  • Not quite as simple as a single key
  • Todo:
  • Setup a root key-pair
  • Setup a branch key-pair
  • Fulfill leaf CSRs with the branch key

We will be using the delegated key approach for our Certificate Authority.

Cross-Signed Certificate Authority

There are certain conditions where the flexibility provided by the linear delegated key certificate authority strategy isn't enough:

  • 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. Really, this is the only valid use of cross-signing.

  • 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 reference purposes.

  • Pros:

  • The most flexible configuration

  • Secure

  • Cons:

  • By far the most complicated

  • Signing branch 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 branch 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 Key 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 online.

Root Keys

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

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 performance characteristics, we opt for using a 384-bit ECDSA root key-pair (instead of the 3072-bit RSA comparable 256-bit key-pair).

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

  • Issue Branch Key Certificates
  • Issue Certificate Revocation Lists
  • Stay Available and Secure

I can't stress that last one enough. You need to ensure that your root keys 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 from this key-pair is included on our internal zone images.

Branch Keys

Our branch key-pairs act as the delegated agents of the root, signing leaf keys for the windows they are marked as active:

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

The Branch Keys are ECDSA keys with a 4-year lifespan. Additionally, they are replaced every 2 years with new Branch Certificates. This is so that certificates signed with the old keys remain valid. Additionally, this gives us the option of upgrading the strength of our branch 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 keys 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 on a branch key, it's an absolute disaster on a root key, 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 a branch key, it's a big problem for a root key.
  • Having your root public key intercepted by an unauthorized party: 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 Branch Keys & CSRs

While this may seem like it's being done out of order, we actually want to initialize our branch key-pair before we set up our root key-pair. This is because we'll be able to create and transfer the branch 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 branch key-pair 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 branch key-pair. In our case, we're going to use one Zone to host both RSA and ECDSA key-pairs. 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 branch 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 branch, 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 key-pair.

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

Generate CSRs

We will now generate some CSRs to pass to our root key.

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

Do the same with the RSA key-pair

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

Copy these Certificate Requests (I'm calling mine cospix_branch_ecc.csr and cospix_branch_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 branch 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_branch_ecc.csr -noout -text
# openssl req -in cospix_branch_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 key-pair. 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.

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

We will also create the RSA key-pair.

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

Your root key-pairs will now issue 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)
        Validity
            Not Before: Jan  1 00:00:00 2016 GMT
            Not After : Jan  1 00:00:00 2040 GMT
        Subject:
            countryName               = US
            organizationName          = Cospix LLC
            commonName                = Cospix Global Root 2040 ECC
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                E4:0A:1A:5B:64:5F:DF:26:EA:F3:C8:DD:E1:86:28:2E:C0:90:FC:3E
            X509v3 Authority Key Identifier: 
                keyid:E4:0A:1A:5B:64:5F:DF:26:EA:F3:C8:DD:E1:86:28:2E:C0:90:FC:3E

            X509v3 Basic Constraints: critical
                CA:TRUE
            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

Perform the same set of actions for our RSA key-pair.

# 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:

And

# 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 Branch 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 branch certificate environment. Run the following commands and compare their output to your branch certificate terminal.

# openssl req -noout -text -in /media/Drive/cospix_branch_ecc.csr
# openssl req -noout -text -in /media/Drive/cospix_branch_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_branch \
-in /media/tmp/cospix_branch_ecc.csr -out /media/tmp/cospix_branch_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)
        Validity
            Not Before: Jan  1 00:00:00 2016 GMT
            Not After : Jan  1 00:00:00 2020 GMT
        Subject:
            countryName               = US
            organizationName          = Cospix LLC
            commonName                = Cospix Global Branch 2020 ECC
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                44:8B:3D:DD:65:B3:31:EB:77:D9:80:DE:AF:D8:B7:54:0A:5B:AA:2C
            X509v3 Authority Key Identifier: 
                keyid:E4:0A:1A:5B:64:5F:DF:26:EA:F3:C8:DD:E1:86:28:2E:C0:90:FC:3E

            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_branch \
-in /media/tmp/cospix_branch_rsa.csr -out /media/tmp/cospix_branch_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.

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

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

Installing Branch Certificates

Copy the cospix_branch_ecc.crt file on your USB drive to ~/openssl/ecc/public.crt on your CA zone and the cospix_branch_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: Fulfilling leaf 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.

template.ext

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 in subjectAltName.

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 branch 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 public branch 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