Skip to main content
This guide walks you through the full HSM Work sign and verify workflow using SoftHSM2 and pkcs11-tool. By the end you will have a working token, a 2048-bit RSA key pair, a signed file, and a verified signature.
These steps target Debian/Ubuntu systems. Package names and library paths may differ on other distributions.
1

Install prerequisites

Install SoftHSM2, OpenSC, OpenSSL, CMake, and the required development libraries:
sudo apt-get update
sudo apt-get install softhsm2 opensc libsofthsm2-dev openssl cmake build-essential libsqlite3-dev
libsofthsm2-dev provides the shared library at /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so, which is referenced by all pkcs11-tool commands in this guide. Verify the path after installation with dpkg -L libsofthsm2.
2

Initialize the SoftHSM token

Create a new SoftHSM2 token with the label FirmwareHSM:
softhsm2-util --init-token --free --label FirmwareHSM
The command prompts for a Security Officer (SO) PIN and a User PIN. For development use 1234 as the User PIN.After initialization, list all slots to find the slot ID assigned to your new token:
softhsm2-util --show-slots
Example output:
Available slots:
Slot 0x04D163DC (ID: 1)
    Token initialized
    Label:          FirmwareHSM
    ...
The slot ID is assigned at token creation and changes if you re-initialize the token. Record the hex slot ID (e.g., 0x04D163DC) and substitute it for <SLOT_ID> in all subsequent commands.
3

Generate the RSA key pair

Create a 2048-bit RSA key pair on the token using key ID 10 and label FirmwareKey:
pkcs11-tool \
  --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so \
  --slot <SLOT_ID> \
  --login \
  --pin 1234 \
  --keypairgen \
  --key-type rsa:2048 \
  --id 10 \
  --label FirmwareKey
A successful run prints:
Key pair generated:
Private Key Object; RSA
  label:      FirmwareKey
  ID:         0a
  Usage:      decrypt, sign, unwrap
Public Key Object; RSA 2048 bits
  label:      FirmwareKey
  ID:         0a
  Usage:      encrypt, verify, wrap
Key ID 10 (hex 0a) is used throughout this guide and matches the default in hsm_sign_verify.sh. If you use a different ID, update the --id flag in all subsequent commands and in the script’s KEYID variable.
4

Sign data

Create a test file, hash it with SHA-256, and sign the hash with your private key:
echo "HSM firmware simulation test" > data.txt
openssl dgst -sha256 -binary data.txt > data.sha256
pkcs11-tool \
  --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so \
  --slot <SLOT_ID> \
  --login \
  --pin 1234 \
  --sign \
  --mechanism RSA-PKCS \
  --input-file data.sha256 \
  --output-file signature.bin \
  --id 10
The RSA-PKCS mechanism signs a pre-hashed digest. The input to --input-file must be the raw binary hash, not the original file. This matches the behaviour of hsm_sign_verify.sh.
5

Export the public key and verify the signature

Export the public key from the token in DER format, convert it to PEM, then verify the signature:
pkcs11-tool \
  --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so \
  --slot <SLOT_ID> \
  --login --pin 1234 \
  --read-object --type pubkey --id 10 \
  --output-file pubkey.der
openssl rsa -pubin -inform DER -in pubkey.der -out pubkey.pem
openssl pkeyutl \
  -verify \
  -pubin \
  -inkey pubkey.pem \
  -in data.sha256 \
  -sigfile signature.bin \
  -pkeyopt digest:sha256
A successful verification prints:
Signature Verified Successfully
If verification fails with RSA operation error, confirm that data.sha256 has not been modified since signing and that you are using the public key matching key ID 10 from the same slot.

Alternative: use the sign and verify script

The repository includes hsm_sign_verify.sh, which automates Steps 4 and 5 for a given file. The script uses the slot ID, PIN, and key ID defined at the top of the file:
# Default values in hsm_sign_verify.sh
SLOT=80451484
PIN=1234
KEYID=10
MODULE="/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"
Update SLOT to match your token’s slot ID, then run:
bash hsm_sign_verify.sh data.sha256
The script signs the input file to <file>.sig, exports the public key to pubkey.pem, and runs openssl pkeyutl -verify. On success it prints:
File signed and verified successfully (if no errors shown)
The script is a convenient wrapper for CI pipelines and build systems. Edit the SLOT, PIN, and KEYID variables at the top of the script to match your token configuration rather than passing flags each time.

Next steps

  • Build the hls-hsm C++ library with CMake and integrate the HSM class into your application — see hls-hsm library.
  • Start the pkcs11-daemon for persistent key vault management and post-quantum key support — see pkcs11-daemon.

Build docs developers (and LLMs) love