1. Article purpose[edit | edit source]
The main purpose of this article is to explain how to use the secrets stored in an external Hardware Security Module (HSM) to sign a software image such as TF-A BL2 or even a FIP. This allows the user to perform a Trusted boot by establishing a Chain of Trust (CoT).
2. OSTL implementation[edit | edit source]
In the OSTL ecosystem, the Signing tool is used to sign the platform Trusted Firmware-A BL2 software that is loaded by the ROM code. The Trusted Firmware-A FIP contains the firmware images loaded by the Trusted Firmware-A BL2 and their configuration files. The cert_create tool [1] handles the signature and the certificate chain generation for the binaries contained in the FIP. Both tools support the use of an external HSM.
3. Use case[edit | edit source]
This article is useful for people who want to sign firmware without having to manipulate a plain private key at some point. One may not want such secrets to be accessible at all. This can be done by interfacing an HSM where the secrets are securely stored with a signing tool.
4. Prerequisites[edit | edit source]
Following this article, it is mandatory that:
- The HSM you are using can be accessed using PKCS#11 APIs.
- You are running an OpenSSL version that supports engines. OpenSSL engines are deprecated [2] since OpenSSL version 3.0 but are still implemented. Therefore, most OpenSSL versions above 0.9.6 should fit to perform the operation.
- You have generated a key pair and stored the private key in the HSM.
![]() |
Some packages like OpenSSL are already included in the OSTL SDK. |
5. Perform a binary signature[edit | edit source]
First, check the openSSL version:
openssl version OpenSSL 3.2.2 4 Jun 2024 (Library: OpenSSL 3.2.2 4 Jun 2024)
Check that the PKCS#11 engine is correctly set:
openssl engine pkcs11 -t (pkcs11) pkcs11 engine [ available ]
Create a custom openSSL configuration file containing:
openssl_conf = openssl_conf
[openssl_conf]
engines = engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = <path_to_libpkcs11.so> #Usually /usr/lib/x86_64-linux-gnu/engines-xxx/libpkcs11.so
MODULE_PATH = <path_to_HSM_module_library>
and use it as openSSL configuration file:
export OPENSSL_CONF=<path_to_your_custom_config_file>
5.1. Perform a signature using only openSSL[edit | edit source]
Simply use the PKCS#11 URL of the private key that you want to use to perform the signature:
openssl dgst -engine pkcs11 -sign "<PKCS11_URL_OF_THE_PRIVATE_KEY>" -keyform engine -out test_signed.bin test.txt engine "pkcs11" set.
Note that this does not generate the certificates necessary to implement the CoT.
5.2. Perform Trusted Firmware-A FIP signature with PKCS#11[edit | edit source]
At this stage, the process is exactly the same as the one described in the TF-A BL2 Trusted Board Boot article. Simply specify the PKCS#11 URLs in the key arguments instead of the paths to *.pem files.
![]() |
The PKCS#11 URLs must be enclosed with the double quote (") character. For example: ROT_KEY="\"<PKCS11_URL_OF_THE_PRIVATE_KEY>\"" |
6. Example with a software HSM[edit | edit source]
6.1. Additional prerequisites[edit | edit source]
This section shows an example of software HSM. The SoftHSMv2 [3] is used, which is a software implementation of a generic cryptographic device with a PKCS#11 interface. To set up the environment:
- Make sure the following packages are installed:
- pkcs11-tools provided in the opensc package
- p11tool provided in the gnutls-bin package
- libengine-pkcs11-openssl
- Install SoftHSMV2.
6.2. Setup[edit | edit source]
The fields enclosed <this_way> are supposed to be replaced by your absolute paths.
- First, create a working directory to store the configuration files and tokens:
cd $HOME mkdir -p softhsm/tokens cd softhsm
- Copy the SoftHSMv2 default configuration file:
cp /etc/softhsm2.conf .
- Customize the token directory path in this configuration file:
Insert "directories.tokendir = <path_to>/softhsm/tokens" in softhsm2.conf
- Export the SOFTHSM2_CONF environment variable:
export SOFTHSM2_CONF=<path_to>/softhsm/softhsm2.conf
- Display the slots:
softhsm2-util --show-slots Available slots: Slot 0 Slot info: Description: SoftHSM slot ID 0x0 Manufacturer ID: SoftHSM project Hardware version: 2.6 Firmware version: 2.6 Token present: yes Token info: Manufacturer ID: SoftHSM project Model: SoftHSM v2 Hardware version: 2.6 Firmware version: 2.6 Serial number: Initialized: no User PIN init.: no Label:
- Initialize the token with a given label. SO PIN is the administrator PIN, User PIN is used to access the slot:
softhsm2-util --init-token --slot 0 --label "TFASignFIP" === SO PIN (4-255 characters) === Please enter SO PIN: **** #0000 Please reenter SO PIN: **** === User PIN (4-255 characters) === Please enter user PIN: **** #1234 Please reenter user PIN: **** The token has been initialized and is reassigned to slot 1861710895
- Check the module:
pkcs11-tool --show-info --module /usr/local/lib/softhsm/libsofthsm2.so Cryptoki version 2.40 Manufacturer SoftHSM Library Implementation of PKCS11 (ver 2.6) Using slot 0 with a present token (0x6ef7742f) #1861710895
![]() |
To ease manipulations, you can create an alias to pkcs11-tool --module /usr/local/lib/softhsm/libsofthsm2.so |
alias p11-softhsm="pkcs11-tool --module /usr/local/lib/softhsm/libsofthsm2.so"
- List all the available slots:
p11-softhsm --list-slots Available slots: Slot 0 (0x6ef7742f): SoftHSM slot ID 0x6ef7742f token label : TFASignFIP token manufacturer : SoftHSM project token model : SoftHSM v2 token flags : login required, rng, token initialized, PIN initialized, other flags=0x20 hardware version : 2.6 firmware version : 2.6 serial num : 1eb49a0ceef7742f pin min/max : 4/255 Slot 1 (0x1): SoftHSM slot ID 0x1 token state: uninitialized
- Generate a keypair using pkcs11-tools:
p11-softhsm --login --login-type user --keypairgen --id 0 --key-type EC:secp256r1 --slot 1861710895 Logging in to "TFASignFIP". Please enter User PIN: #Enter User PIN Key pair generated: Private Key Object; EC label: ID: 00 Usage: decrypt, sign, unwrap, derive Access: sensitive, always sensitive, never extractable, local Public Key Object; EC EC_POINT 256 bits EC_POINT: 044104510cedc7dabaa4b3a7f9953069065d3f9b3978d772e3c04069c34f4198a5bda64e16ec685c9dadbe91c5f35a76362d8c955a4167e78e3f1365e98ab05f7884a4 EC_PARAMS: 06082a8648ce3d030107 label: ID: 00 Usage: encrypt, verify, wrap, derive Access: local
- List and get the private key URL with p11tool:
p11tool --provider=/usr/local/lib/softhsm/libsofthsm2.so --login --set-pin=1234 --list-all Object 0: URL: pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;type=private #This is the URI Type: Private key (EC/ECDSA-SECP256R1) Label: Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; CKA_NEVER_EXTRACTABLE; CKA_SENSITIVE; ID: 00 Object 1: URL: pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;type=public Type: Public key (EC/ECDSA-SECP256R1) Label: Flags: CKA_WRAP/UNWRAP; ID: 00
- Create a custom openSSL configuration file (for example openssl-pkcs11.conf) with the content described here, and add:
MODULE_PATH = /usr/local/lib/softhsm/libsofthsm2.so
- Export the OPENSSL_CONF environment variable:
export OPENSSL_CONF=<path_to>/softhsm/openssl-pkcs11.conf
- Sign a binary using the PKCS11 URL:
openssl dgst -engine pkcs11 -sign "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;object=tfasign;type=private" -keyform engine -out test_signed.bin test.txt engine "pkcs11" set.
![]() |
At this stage, and similarly to the procedure above, the PKCS11 URLs can be used to generate the CoT certificates |
7. Troubleshooting[edit | edit source]
- If you cannot enumerate slots as shown below, it means that the openSSL configuration file is incorrect or that the OPENSSL_CONF variable has not been defined. In this case, please check it.
openssl dgst -engine pkcs11 -sign "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;object=tfasign;type=private" -keyform engine -out signature.bin test.txt engine "pkcs11" set. Failed to enumerate slots Failed to enumerate slots PKCS11_get_private_key returned NULL cannot load key file from engine 140310533596480:error:80067065:pkcs11 engine:ctx_load_privkey:object not found:eng_back.c:858: 140310533596480:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:../crypto/engine/eng_pkey.c:77: unable to load key file
- If you cannot find the key, it is likely that your SOFTHSM2_CONF variable has not been set or that you configuration file is not correct.
openssl dgst -engine pkcs11 -sign "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=1eb49a0ceef7742f;token=TFASignFIP;id=%00;object=tfasign;type=private" -keyform engine -out signature.bin test.txt engine "pkcs11" set. Specified object not found Specified object not found PKCS11_get_private_key returned NULL cannot load key file from engine 140107714262336:error:80067065:pkcs11 engine:ctx_load_privkey:object not found:eng_back.c:858: 140107714262336:error:26096080:engine routines:ENGINE_load_private_key:failed loading private key:../crypto/engine/eng_pkey.c:77: unable to load key file Segmentation fault
8. References[edit | edit source]