Offline root CA using YubiHSM

OpenSSL PKI Tutorial at readthedocs. YubiHSM does not have many of the features of “big” HSMs, such as using Shamir-separated keys for backups or a large built-in transaction log, but they are quite enough for 99% of cases.

I recommend having at least two HSMs – primary and backup. The backup, containing copies of all keys and users, should be placed in a safe and periodically checked to see if it is still there. Use basic.

A bunch of useful information about the work of YubiHSM is on Yubico websiteI strongly recommend that you read it before implementation.

First we need LiveUSB with Linux. I took Debian. Communication with YubiHSM is done through the SDK and everything you need can be downloaded from here: YubiHSM 2 libraries and tools. YubiHSM supports udev hotplug, so you also need to include a file with rules for udev. The SDK itself runs through a helper daemon yubihsm-connector. It is waiting for a client connection. yubihsm-shell. Default yubihsm-connector listens only on, you can open it to the network by configuring certificate authentication, but this is not necessary in our case.

So, we put the necessary software, rules for udev, and restart everything that is needed:

sudo apt updat
sudo apt install -y libcurl4 opensc-pkcs11 libengine-pkcs11-openssl opensc libssl-dev pkg-config git libp11-3
tar xzvf yubihsm2-sdk-2022-06-debian11-amd64.tar.gz
cd yubihsm2-sdk/
sudo dpkg -i *.deb
cd ..
sudo install -o root -g root -m 0644 yubihsm-connector /etc/udev/rules.d
sudo systemctl restart udev
sudo systemctl restart yubihsm-connector

Note that libp11 must be at least version 0.4.10.

And here is the content of the yubihsm-connector file with the rules for udev:

# This udev file should be used with udev 188 and newer
ACTION!="add|change", GOTO="yubihsm2_connector_end"

# Yubico YubiHSM 2
# The OWNER attribute here has to match the uid of the process running the Connector
SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0030", OWNER="yubihsm-connector"


After that, you can insert YubiHSM into a USB port, find it in dmesg and try to connect to it using yubihsm-shell.

yubihsm> connect
Session keepalive set up to run every 15 seconds
yubihsm> session open 1 password
Created session 0

Users are numbered, the login is the user number. The default administrative user 1 password is “password”. To reset YubiHSM to factory settings (deleting all users and keys, and re-creating administrative user 1), you need to insert the key into the USB port and press your finger against the metal contact for 10 seconds: factory reset.

In yubihsm-shell (documentation here) you can issue various HSM commands, but they are low enough to work with YubiHSM through PKCS11, you need to set the YUBIHSM_PKCS11_CONF environment variable with the configuration file. This variable is used by the PKCS11 Yubico library, which in turn is used by libp11 and OpenSSL.

Here is the minimal configuration file:

connector =

We put the file, for example, in /etc/pki/yubihsm_pkcs11.conf and set the environment variable:

export YUBIHSM_PKCS11_CONF=/etc/pki/yubihsm_pkcs11.conf

Now you should work not only yubihsm-shellbut also PKCS11 libraries.

Now we need to configure OpenSSL. Actually, all that needs to be done is in a separate configuration file (copy the base one and edit it, throwing out the excess), indicate that the keys should not be kept in a file, but use the PKCS11 library. Here are the relevant parts of the OpenSSL configuration file:

openssl_conf = openssl_init

engines = engine_section

## Engine
pkcs11 = pkcs11_section

engine_id = pkcs11
MODULE_PATH = /usr/lib/x86_64-linux-gnu/pkcs11/
init = 0

Save this file as yubihsm.conf, now you can call openssl with this configuration like so:

openssl ca -config yubihsm.conf -engine pkcs11 -keyform engine ...

We are done with the technical part, let’s move on to the administrative part.

As I mentioned above, there are users in YubiHSM, they are not the same users as in your operating system. They are numbered and different rights can be assigned to them. In Yubico terms, rights are divided into capabilities and delegations. If you’re familiar with the Windows security model, capabilities are like privileges and delegations are a DACL on objects in an HSM.

I do not want to discuss what options for the role model of CA users are possible, this is the topic of a separate article. Microsoft has quite a few detailed guides on this and other topics around CA and PKI: Public Key Infrastructure Design Guidance. I found them very helpful. In my case, I made three roles for users:

  • administrator, can do anything;

  • operator, can perform operations on existing keys, but cannot do anything else, such as delete them or create new ones;

  • auditor, can view, export and clear YubiHSM built-in log.

In your situation, the arrangement of roles may differ, but when designing, I recommend considering two factors:

  • a complex role structure requires operational maturity;

  • if you do everything from the admin, you can accidentally delete the key, and YubiHSM is not a flash drive with a file system, I don’t know how to restore them later and I don’t think so.

We decided to have two users in each role, in case one of them gets fired or leaves. Creating a user comes down to running yubihsm-shell with the put-authentication-key command, but for convenience, I wrote a mini script: yubihsm-adduser.

So we got to the most interesting – to create keys. Keys can be created in two ways – via PKCS11 or directly in yubihsm-shell, we chose the second one because it is easier to manage rights. There can be several keys in YubiHSM, you can refer to a specific one using object number.

Since we live in the 21st century with flying hoverboards and blockchain, we have decided not to use RSA. The reasons are described in more detail in the article. fuck rsa. If you are a Luddite, you can use RSA (including with PSS), but remember that long keys will slow down, and 2048 bits is not enough for a root CA that should last 20 years. This is how you can create a P384 EC key:

yubihsm-shell --authkey 1 --password password --action generate-asymmetric-key --object-id 1 --label "Root CA key"  --domains 1 --capabilities exportable-under-wrap:sign-ecdsa --algorithm ecp384

Again, to make it easier, I wrote a small exemplary scriptwhich creates two keys – EC (for healthy people) and RSA (for smokers and Luddites) and the necessary users according to the scheme above, all this for two HSMs.

Now that the private keys have been created, we can create a root certificate based on them. This will already be done by OpenSSL. with our config:

openssl req -new -sha256 -config yubihsm.conf -engine pkcs11 -keyform engine -key id_0001 -out root.pem -x509 -days 7300 -extensions root_ca_extensions -subj "CN=Enterprise Offline Root CA"

Notice how to reference the key in YubiHSM. It is available by item number, but the number must be four digits, with leading zeros if necessary. This applies to all operations using OpenSSL: it only operates on the concept of a pin, and therefore you need to glue the username and pin together, for example, if you have an administrative user 1 with a password q1w2e3r4, then in terms of OpenSSL the pin will be 0001q1w2e3r4.

Here is the “root_ca_extensions” section from the yubihsm.conf configuration file that we referenced when creating the key:

[ root_ca_extensions ]
# Extensions for a typical CA (`man x509v3_config`).
# call with -extensions root_ca_extensions
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

Root CA is ready! Its key is on the HSM and its certificate is in the root.pem file.

Then you can create the rest of the infrastructure, releasing online CA, CRL, and the like. These issues are described in detail in other manuals, so I will not repeat myself, I will write only commands for signing CSR and CRL.

So sign the CSR from the intermediate CA and issue the certificate:

openssl ca -config yubihsm.conf -engine pkcs11 -keyform engine -keyfile id_0001 -days 1095 -md sha256 -in inermediate.csr -extensions intermediate_ca_extensions

Here is the “intermediate_ca_extensions” section from the config:

[ intermediate_ca_extensions ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
# call with -extensions intermediate_ca_extensions
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
crlDistributionPoints = URI:

Note that the intermediate CA certificate references the CRL. Of course, this CRL needs to be updated periodically in order for the validation of the intermediate certificate (and all chains) to succeed. Since our CA is disconnected from the network, we need to periodically turn it on, create a CRL, and copy it to the desired site. This is how you create a CRL:

openssl ca -config yubihsm.conf -engine pkcs11 -keyform engine -keyfile id_0001 -gencrl -out root.crl

Subcommand work openssl ca requires the “ca” section in the configuration, but it can be left as default.

That’s all, I’ll be happy to answer questions.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *