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:
HSM_module_library>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_
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:
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: 00p11tool --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/
- 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]