Skip to main content
The HSM Work project supports three approaches to key management: direct PKCS#11 operations via pkcs11-tool, the hls-hsm C++ class API, and the pkcs11-daemon vault backed by SQLite. Choose the approach that fits your use case.

PKCS#11 key management

The PKCS#11 interface is exposed through SoftHSM2 and accessed with pkcs11-tool. Before generating keys you must initialize a token.

Initialize a token

softhsm2-util --init-token --free --label FirmwareHSM
softhsm2-util --show-slots
--free selects the first available slot. After initialization, note the slot ID printed by --show-slots — you will use it in all subsequent commands as <SLOT_ID>.

Generate key pairs

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

List objects

pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so \
  --slot <SLOT_ID> --login --pin 1234 --list-objects --verbose

Key IDs and labels

--id is a numeric byte identifier (e.g., 10 in hex is 0x0a) used to reference a key object programmatically. --label is a human-readable name associated with the object. Both values must match when signing or reading an object:
  • --id 10 identifies the key with ID 0x0a
  • --label FirmwareKey is descriptive and used for display
When exporting or signing, reference the key by --id. Labels are not guaranteed to be unique across tokens.

Delete a token

To remove a token entirely and re-initialize:
softhsm2-util --delete-token --token FirmwareHSM
softhsm2-util --init-token --free --label FirmwareHSM
Deleting a token destroys all keys stored in it. This operation is irreversible.

hls-hsm C++ API

The hls-hsm library (hls-hsm/include/hsm.h) provides a C++ interface for key generation and retrieval.

Supported key types

enum class KeyType { RSA, AES };

Initialize

HSM hsm;
hsm.init();

Generate keys

// RSA 2048-bit key — returns key ID string
std::string rsa_id = hsm.generate_rsa_key(2048);

// AES 256-bit key — returns key ID string
std::string aes_id = hsm.generate_aes_key(256);

Retrieve a key

Key k = hsm.get_key(rsa_id);
// k.id   — string identifier
// k.type — KeyType::RSA or KeyType::AES
// k.data — raw key bytes

Key naming conventions

Key IDs are derived from the algorithm and bit size:
Key typeID formatExample
RSARSA_<bits>RSA_2048
AESAES_<bits>AES_256

Random bytes

std::vector<uint8_t> rand = hsm.get_random_bytes(32);

Daemon vault

The pkcs11-daemon manages keys in a local SQLite database (vault.db) via the Vault class.

Schema

CREATE TABLE IF NOT EXISTS keys(
  label TEXT PRIMARY KEY,
  type  TEXT,
  data  TEXT
)

Initialization and default keys

At startup the daemon generates and stores two default keys:
Vault vault("vault.db");
vault.init();

// RSA key generated with OpenSSL
std::string rsa = Crypto::generate_rsa(); // RSA 2048, PEM
vault.store_key("default-rsa", "rsa", rsa);

// Post-quantum key (stub — see post-quantum guide)
std::string pq = Crypto::pqc_generate();
vault.store_key("default-pq", "dilithium", pq);

Storing and retrieving keys

// Store a key
vault.store_key("my-key", "rsa", pem_string);

// Retrieve a key by label
std::string data = vault.get_key("my-key");
The label column is the primary key, so storing a key with an existing label overwrites it.
Crypto::pqc_generate() currently returns an empty string. The default-pq entry is created in the vault but holds no key material until the liboqs Dilithium integration is complete. See the post-quantum cryptography guide.

Build docs developers (and LLMs) love