A Hardware Security Module (HSM) is an external device, such as USB plugin which can securely store keystores, and do other encrpyption work. I used a Nitrokey which uses open source software.
Create the key on the HSM
pkcs11-tool –keypairgen –key-type EC:prime256v1 –login –pin 12345678 –label “my_key3”
Create the certificate request using openssl
To use the opensc pkcs11 driver for an HSM you need to pass parameters to the driver.
A typical openssl command to create a certificate request, using a pre existing private key, is
OPENSSL_CONF=hw.config openssl req -new -x509 -engine pkcs11 -keyform engine -key slot_0-label_my_key3 -sha256 -out hw.pem -subj “/C=CB/O=HW/CN=HW”
The fields for the HSM device are:
- -engine pkcs11 . This tells openssl which exernal device to use. Use the command openssl engine -vvv -tt pkcs11 to display information about the pkcs11 engine
- -keyform engine it needs to be “engine” to use the HSM.
- -key xxxx where xxxx can be in the format
- n:m where n is the slot number (“where the HSM device is plugged into – the first device is usually 0) and m is the id of the certificate, public key, or private key within the device
- slot_n-id_m where where n is the slot number (“where the HSM device is plugged into – usually 0) and m is the id within the device
- id_m m is the id within the device (slot_n defaults to slot_0)
- slot_n-label_name where where n is the slot number (“where the hsm device is plugged into – usually 0) and name is the keyname within the HSM
- label_name where name is the keyname within the HSM (and slot_n defaults to slot_0)
Note the id and labels are strings, so the id value 0100 is different from the value 100.
The command pkcs11-tool -O gave
Using slot 0 with a present token (0x0) Public Key Object; RSA 2048 bits label: Private Key ID: 0200 Usage: encrypt, verify, wrap Public Key Object; RSA 2048 bits label: my_key ID: 0100 Usage: encrypt, verify, wrap Public Key Object; RSA 2048 bits label: my_key2 ID: 0101 Usage: encrypt, verify, wrap Public Key Object; RSA 2048 bits label: my_key3 ID: 0fea4632d386a7f2469eec44daeafa84a1dbd8e2 Usage: encrypt, verify, wrap
So I could use for -key
The code for this is here.
The contents of the openssl config file for HSM devices
I’ve displayed the bits you need for the “engine” or HSM processing.
openssl_conf = openssl_def [openssl_def] engines = engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so MODULE_PATH = /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so PIN = 648219 # init = 0
The documentation for the config file is https://www.openssl.org/docs/man1.1.1/man5/config.html .
The use of an HSM is driven from an “engine”.
The relevant statements in the config file are
- openssl_conf openssl searches the config file for this entry by default. It describes which other sections should always be processed. If you use openssl req …, openssl will also process the section [req]. My file says use the section called openssl_def.
- [openssl_def] this contains the names of sections to be processed. In this case it says process the engine_section.
- [engine_section] this lists all of the engine sections to be processed. For a pkcs11 engine process I have called the section pkcs11_section.
- [pkcs11_section] this lists the individual entries for the pkcs11 engine.
- The engine_id is optional
- The dynamic path is the code for the HSM. If specified, this must come first. For the default see openssl version -e or openssl version -a.
- You can pass parameters through the HSM. Use the openssl engine -vvv -tt pkcs11 to list them. Note they are in upper case.
- The MODULE_PATH in upper case is passed through to the device.
- The PIN in upper case is passed through to the HSM. Without this, you are prompted for the pin
- Using VERBOSE = EMPTY gives more information from the HSM device.
Problems using the config file.
The following command worked
OPENSSL_CONF=eccert.config openssl req -new -x509 -engine pkcs11 -keyform engine -key id_0101 -sha256 -out hw.pem -subj “/C=CB/O=HW/CN=HW”
The following command failed
openssl req -config eccert.config -new -x509 -engine pkcs11 -keyform engine -key id_0101 -sha256 -out hw.pem -subj “/C=CB/O=HW/CN=HW”
It looks like the config file is being processed twice, so you have to use the OPENSSL_CONF variable rather than the -config option.
The error messages are
- engine “pkcs11” set.
- Using configuration from eccert.config
- Error configuring OpenSSL modules
- engine routines:engine_list_add:conflicting engine id:../crypto/engine/eng_list.c :63. I think this is reporting it has already been processed and exists in the list of engines.
- engine routines:ENGINE_add:internal list error:../crypto/engine/eng_list.c :223:
- engine routines:dynamic_load:conflicting engine id:../crypto/engine/eng_dyn.c :502:
- engine routines:int_engine_configure:engine configuration error:../crypto/engine/eng_cnf.c :141: section=pkcs11_section, name=dynamic_path, value=/usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so These are the parts of my configuration file.
- configuration file routines:module_run:module initialization error:../crypto/conf/conf_mod.c: 174:module=engines, value=engine_section2, retcode=-1
Some other error messages
With no MODULE_PATH in the configuration file
- engine “pkcs11” set.
- No private keys found.
- PKCS11_get_private_key returned NULL
- cannot load Private Key from engine
- pkcs11 engine:ctx_load_privkey:object not found:eng_back.c:876:
- engine routines:ENGINE_load_private_key:failed loading private key:../crypto/engine/eng_pkey.c:78:
- unable to load Private Key