Skip to main content
SoftHSM2 is a software implementation of a PKCS#11 cryptographic token. It provides an HSM-compatible interface without requiring physical hardware, making it suitable for development, CI, and integration testing. In HSM Work, SoftHSM2 serves as the PKCS#11 backend exercised by hsm_test.c. The test dynamically loads the SoftHSM2 shared library and drives the standard PKCS#11 C API to enumerate slots, authenticate, and list key objects on the token.

Token setup

1

Install SoftHSM2

# Ubuntu/Debian
apt-get install softhsm2

# Verify the library is present
ls /usr/lib/softhsm/libsofthsm2.so
2

Initialise the token

softhsm2-util --init-token --slot 0 \
  --label "MyToken" \
  --so-pin 1234 \
  --pin 5678
This creates a token with label MyToken and sets the user PIN to 5678. These values match the constants in hsm_test.c:
hsm_test.c
#define TOKEN_LABEL "MyToken"
#define USER_PIN    "5678"
3

Verify the token appears

softhsm2-util --show-slots
You should see a slot entry with Token Label: MyToken and Initialized: yes.

Configuration

SoftHSM2 reads its token storage location from /etc/softhsm/softhsm2.conf (system-wide) or ~/.config/softhsm2/softhsm2.conf (per-user).
/etc/softhsm/softhsm2.conf
# Directory for token storage
directories.tokendir = /var/lib/softhsm/tokens/

# Log level: DEBUG, INFO, WARNING, ERROR
log.level = INFO

# Object store backend (db or file)
objectstore.backend = db
If the token directory does not exist, create it before initialising: mkdir -p /var/lib/softhsm/tokens.

PKCS#11 library path

The shared library path differs by distribution. Common locations:
DistributionPath
Ubuntu/Debian (x86-64)/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
Generic / compatibility symlink/usr/lib/softhsm/libsofthsm2.so
macOS (Homebrew)/usr/local/lib/softhsm/libsofthsm2.so
hsm_test.c uses the generic path:
hsm_test.c
#define PKCS11_LIB "/usr/lib/softhsm/libsofthsm2.so"
Update this constant if your system places the library at a different path (e.g., the x86-64 Debian path above).

Dynamic loading in hsm_test.c

The test avoids link-time binding by loading the library at runtime with dlopen / dlsym, then resolving each PKCS#11 function pointer by name.
hsm_test.c
void *lib_handle = dlopen(PKCS11_LIB, RTLD_NOW);
if (!lib_handle) {
    fprintf(stderr, "Failed to load PKCS#11 library: %s\n", dlerror());
    return EXIT_FAILURE;
}

// Resolve function pointers by name
CK_RV (*C_Initialize)(CK_VOID_PTR);
C_Initialize = dlsym(lib_handle, "C_Initialize");
This pattern lets the test run against any conforming PKCS#11 library by changing only the PKCS11_LIB define.

PKCS#11 functions used

The following PKCS#11 functions are called by hsm_test.c:
C_Initialize(NULL_PTR) initialises the Cryptoki library. It must be the first call made after loading the library. C_Finalize(NULL_PTR) releases all resources and must be the last call.
Returns the list of available slot IDs. The source uses a fixed-size array of 32 slots.
CK_SLOT_ID slots[32];
CK_ULONG slot_count = 32;
C_GetSlotList(CK_TRUE, slots, &slot_count);
// slot_count is updated to the actual number of slots found
Retrieves metadata for the token in a given slot, including its label. The test matches the label against TOKEN_LABEL ("MyToken") to identify the target slot.
CK_TOKEN_INFO info;
C_GetTokenInfo(slots[i], &info);
if (strncmp((char*)info.label, TOKEN_LABEL, strlen(TOKEN_LABEL)) == 0) {
    target_slot = slots[i];
}
Opens a read-only or read-write session on a slot.
CK_SESSION_HANDLE session;
C_OpenSession(target_slot, CKF_SERIAL_SESSION | CKF_RW_SESSION,
              NULL, NULL, &session);
// ... use session ...
C_CloseSession(session);
Authenticates as the normal user (CKU_USER) with the PIN. Required before accessing private key objects.
C_Login(session, CKU_USER,
        (CK_UTF8CHAR_PTR)USER_PIN, strlen(USER_PIN));
// ... access private objects ...
C_Logout(session);
Three-step pattern for searching objects on the token. C_FindObjectsInit sets the search template (empty template matches all objects). C_FindObjects fills an array of object handles in batches. C_FindObjectsFinal cleans up the search context.
C_FindObjectsInit(session, NULL, 0); // NULL template = match all

CK_OBJECT_HANDLE objects[64];
CK_ULONG found = 0;
C_FindObjects(session, objects, 64, &found);

C_FindObjectsFinal(session);
For each handle returned, the test reads the CKA_LABEL and CKA_ID attributes to display the object.

Token management with pkcs11-tool

pkcs11-tool (from the opensc package) provides a convenient CLI for interacting with PKCS#11 tokens without writing C code.
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --list-slots
Install opensc to get pkcs11-tool: apt-get install opensc.

Build docs developers (and LLMs) love