Status: ✅ Implemented (PKCS#11 Interface)
Version: 1.0 (November 2025)
Compliance: eIDAS-ready, FIPS 140-2 compatible
ThemisDB supports Hardware Security Modules (HSMs) for secure cryptographic key storage and signing operations. HSM integration ensures that private keys never leave the secure hardware, providing the highest level of security for PKI operations.
| HSM Type | PKCS#11 Support | Status | Use Case |
|---|---|---|---|
| SoftHSM2 | ✅ Yes | ✅ Tested | Development/Testing |
| Thales Luna HSM | ✅ Yes | Production | |
| Utimaco CryptoServer | ✅ Yes | Enterprise | |
| AWS CloudHSM | ✅ Yes (via PKCS#11) | Cloud | |
| YubiHSM 2 | ✅ Yes | Small deployments |
┌────────────────────────────────────────────────────┐
│ ThemisDB Application │
├────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ HSMProvider (C++ Wrapper) │ │
│ └────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌────────────────▼─────────────────────────────┐ │
│ │ PKCS#11 Interface (dlopen) │ │
│ └────────────────┬─────────────────────────────┘ │
└───────────────────┼──────────────────────────────┘
│
┌──────────────▼──────────────┐
│ PKCS#11 Library (.so/.dll) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Hardware Security Module │
│ (Physical or Cloud-based) │
└─────────────────────────────┘
Key Components:
- HSMProvider: C++ wrapper class for PKCS#11 operations
- PKCS#11 Library: Vendor-specific library (e.g., libsofthsm2.so)
- HSM Device: Physical hardware or cloud HSM service
SoftHSM2 emulates an HSM in software - perfect for development and CI/CD.
sudo apt-get update
sudo apt-get install softhsm2 openscbrew install softhsmDownload from: https://github.com/opendnssec/SoftHSMv2/releases
# Create tokens directory
mkdir -p ~/.config/softhsm2/tokens
# Initialize token in slot 0
softhsm2-util --init-token \
--slot 0 \
--label "themis-dev" \
--pin 1234 \
--so-pin 5678
# Verify token
softhsm2-util --show-slotsOutput:
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: 1234567890abcdef
Initialized: yes
User PIN init.: yes
Label: themis-dev
# Generate 2048-bit RSA key pair for signing
pkcs11-tool \
--module /usr/lib/softhsm/libsofthsm2.so \
--login \
--pin 1234 \
--keypairgen \
--key-type RSA:2048 \
--label "themis-signing-key" \
--id 01
# Verify key
pkcs11-tool \
--module /usr/lib/softhsm/libsofthsm2.so \
--login \
--pin 1234 \
--list-objectsExpected Output:
Using slot 0 with a present token (0x0)
Private Key Object; RSA
label: themis-signing-key
ID: 01
Usage: decrypt, sign
Access: sensitive, always sensitive, never extractable, local
Public Key Object; RSA 2048 bits
label: themis-signing-key
ID: 01
Usage: encrypt, verify
Access: local
# Extract public key
pkcs11-tool \
--module /usr/lib/softhsm/libsofthsm2.so \
--login \
--pin 1234 \
--read-object \
--type pubkey \
--label "themis-signing-key" \
--output-file themis-pub.key
# Create certificate request
openssl req -new \
-key <(pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
--login --pin 1234 \
--read-object --type privkey \
--label "themis-signing-key") \
-subj "/C=DE/ST=Bavaria/L=Munich/O=ThemisDB/CN=themis.example.com" \
-out themis.csr
# Self-sign certificate
openssl x509 -req -days 365 \
-in themis.csr \
-signkey themis-priv.key \
-out themis-cert.pemThemisDB supports three slot-selection strategies, evaluated in priority order:
| Priority | Mechanism | When to Use |
|---|---|---|
| 1 (highest) | Token label (token_label) |
Multiple HSMs or multiple tokens; most portable |
| 2 | Slot ID (slot_id) |
Known, stable slot numbering |
| 3 (default) | First available slot | Single-token setups; development |
Token labels are set when the token is initialized (see Initialize HSM Token above). Using the label instead of a slot ID avoids hard-coding slot numbers, which can change after device reconnects or firmware updates.
HSMConfig config;
config.library_path = "/usr/lib/softhsm/libsofthsm2.so";
config.token_label = "themis-dev"; // must match the label used in softhsm2-util --init-token
config.pin = "1234";
config.key_label = "themis-signing-key";Use when you have a fixed hardware slot assignment:
HSMConfig config;
config.library_path = "/usr/lib/softhsm/libsofthsm2.so";
config.slot_id = 2; // specific slot ID
config.pin = "1234";Note: If the configured
slot_idis not found in the enumerated slot list,initialize()falls back to the first available slot and logs a warning. CallgetLastError()to retrieve the diagnostic message.
The user PIN is supplied via HSMConfig::pin or the THEMIS_HSM_PIN environment
variable (environment variable takes effect only when pin is empty in config).
// Preferred: pass PIN in config (avoids process environment exposure)
config.pin = "1234";
// Alternative: set THEMIS_HSM_PIN before starting the process
// export THEMIS_HSM_PIN=1234PIN error behaviour:
| PKCS#11 Return Code | Meaning | HSMProvider action |
|---|---|---|
CKR_OK |
Login successful | Proceed to key discovery |
CKR_USER_ALREADY_LOGGED_IN |
Session already authenticated | Silently continue (normal for shared sessions) |
CKR_PIN_INCORRECT |
Wrong PIN | Skip session; fall back to stub; set getLastError() |
| Any other error | Unexpected login failure | Skip session; fall back to stub; set getLastError() |
When no PIN is configured, C_Login is skipped. HSMs that require authentication will
then reject key operations.
# Initialize a fresh token on a free slot (SoftHSM2 picks the slot number automatically)
softhsm2-util --init-token \
--free \
--label "themis-prod" \
--pin <user-pin> \
--so-pin <security-officer-pin>
# List tokens to verify
softhsm2-util --show-slots
# Generate signing key in the new token
pkcs11-tool \
--module /usr/lib/softhsm/libsofthsm2.so \
--login --pin <user-pin> \
--token-label "themis-prod" \
--keypairgen --key-type RSA:2048 \
--label "themis-signing-key" \
--id 01Then configure ThemisDB to find the token by label:
HSMConfig config;
config.library_path = "/usr/lib/softhsm/libsofthsm2.so";
config.token_label = "themis-prod";
config.pin = "<user-pin>";
config.key_label = "themis-signing-key";HSMProvider::initialize() always succeeds (returns true) but falls back to an
insecure software stub when the real PKCS#11 session could not be established.
Use isStubProvider() and getLastError() to detect this:
auto hsm = std::make_unique<HSMProvider>(config);
hsm->initialize();
if (hsm->isStubProvider()) {
std::cerr << "[SECURITY] HSM fallback active: "
<< hsm->getLastError() << std::endl;
// In production: abort – never proceed with stub in production!
}
std::cout << "Token info: " << hsm->getTokenInfo() << std::endl;
// Real HSM: "PKCS11 real session active (slot=0, label=themis-prod, pool=1)"
// Stub: "PKCS11 fallback stub"Common failure reasons and fixes:
| Error message | Cause | Fix |
|---|---|---|
library_path is not configured |
No PKCS#11 library set | Set config.library_path |
Failed to load PKCS#11 library: … |
Library file not found / not executable | Install HSM client library |
No PKCS#11 slots with token present found |
No token inserted / token not initialized | Initialize token with softhsm2-util --init-token |
Token with label '…' not found in any slot |
Label mismatch | Check softhsm2-util --show-slots output |
C_Login failed: PIN incorrect |
Wrong PIN | Verify PIN; check HSM lock status |
No private key found in any pool session |
Key not generated or wrong key_label |
Run pkcs11-tool --keypairgen with correct label |
| Variable | Description | Example |
|---|---|---|
THEMIS_HSM_LIBRARY |
PKCS#11 library path | /usr/lib/softhsm/libsofthsm2.so |
THEMIS_HSM_SLOT |
HSM slot ID | 0 |
THEMIS_HSM_PIN |
User PIN | 1234 |
THEMIS_HSM_KEY_LABEL |
Signing key label | themis-signing-key |
THEMIS_HSM_ALGORITHM |
Signature algorithm | RSA-SHA256 |
#include "security/hsm_provider.h"
using namespace themis::security;
// Configure HSM
HSMConfig config;
config.library_path = "/usr/lib/softhsm/libsofthsm2.so";
config.slot_id = 0;
config.pin = "1234";
config.key_label = "themis-signing-key";
config.signature_algorithm = "RSA-SHA256";
config.verbose = true;
// Create HSM provider
auto hsm = std::make_unique<HSMProvider>(config);
// Initialize
if (!hsm->initialize()) {
std::cerr << "HSM init failed: " << hsm->getLastError() << std::endl;
return 1;
}
// Sign data
std::vector<uint8_t> data = {'H', 'e', 'l', 'l', 'o'};
auto result = hsm->sign(data);
if (result.success) {
std::cout << "Signature: " << result.signature_b64 << std::endl;
std::cout << "Algorithm: " << result.algorithm << std::endl;
}
// Cleanup
hsm->finalize();# 1. Install Luna client
# Download from: https://cpl.thalesgroup.com/
# 2. Configure client
sudo /usr/safenet/lunaclient/bin/configurator
# 3. Register HSM
sudo /usr/safenet/lunaclient/bin/vtl addServer -n 192.168.1.100 -i myLunaHSM
# 4. Create partition
sudo /usr/safenet/lunaclient/bin/vtl createPartition -p themis-prod
# 5. Assign client
sudo /usr/safenet/lunaclient/bin/vtl assignPartition -p themis-prodThemisDB Configuration:
HSMConfig config;
config.library_path = "/usr/safenet/lunaclient/lib/libCryptoki2_64.so";
config.slot_id = 0; // Use vtl listPartitions to find slot
config.pin = getenv("THEMIS_HSM_PIN"); // From secure vault
config.key_label = "themis-prod-signing-key";
config.signature_algorithm = "RSA-SHA512"; // Higher security# 1. Install CloudHSM client
wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/EL7/cloudhsm-client-latest.el7.x86_64.rpm
sudo yum install cloudhsm-client-latest.el7.x86_64.rpm
# 2. Configure cluster
sudo /opt/cloudhsm/bin/configure -a <cluster-id>.cloudhsm.us-east-1.amazonaws.com
# 3. Start service
sudo systemctl start cloudhsm-client
# 4. Initialize Crypto User
/opt/cloudhsm/bin/cloudhsm_mgmt_util
loginHSM CO admin password123
createUser CU themis-crypto ThemisPass123!
quitThemisDB Configuration:
HSMConfig config;
config.library_path = "/opt/cloudhsm/lib/libcloudhsm_pkcs11.so";
config.slot_id = 1; // CloudHSM uses slot 1
config.pin = getenv("THEMIS_CLOUDHSM_PIN");
config.key_label = "themis-prod-key";class HSMProvider {
public:
explicit HSMProvider(HSMConfig config);
// Initialize HSM connection
bool initialize();
// Sign data (hashes internally)
HSMSignatureResult sign(const std::vector<uint8_t>& data,
const std::string& key_label = "");
// Sign pre-computed hash
HSMSignatureResult signHash(const std::vector<uint8_t>& hash,
const std::string& key_label = "");
// Verify signature
bool verify(const std::vector<uint8_t>& data,
const std::string& signature_b64,
const std::string& key_label = "");
// List available keys
std::vector<HSMKeyInfo> listKeys();
// Generate new key pair
bool generateKeyPair(const std::string& label,
uint32_t key_size = 2048,
bool extractable = false);
// Get token information
std::string getTokenInfo() const;
// Check if ready
bool isReady() const;
// Cleanup
void finalize();
};struct HSMSignatureResult {
bool success; // Operation success flag
std::string signature_b64; // Base64-encoded signature
std::string algorithm; // Algorithm used (e.g., RSA-SHA256)
std::string key_id; // HSM key identifier
std::string cert_serial; // Certificate serial (if available)
std::string error_message; // Error details on failure
uint64_t timestamp_ms; // Unix timestamp in milliseconds
};# Run HSM tests (requires SoftHSM2)
./build/tests/test_hsm_provider
# Expected output:
# [==========] Running 12 tests from 1 test suite.
# [----------] Global test environment set-up.
# [----------] 12 tests from HSMProviderTest
# [ RUN ] HSMProviderTest.ConstructorDoesNotThrow
# [ OK ] HSMProviderTest.ConstructorDoesNotThrow (0 ms)
# [ RUN ] HSMProviderTest.InitializeWithSoftHSM
# [ OK ] HSMProviderTest.InitializeWithSoftHSM (45 ms)
# ...
# [==========] 12 tests from 1 test suite ran. (234 ms total)
# [ PASSED ] 12 tests.#include "security/hsm_provider.h"
int main() {
HSMConfig config;
config.library_path = "/usr/lib/softhsm/libsofthsm2.so";
config.slot_id = 0;
config.pin = "1234";
config.key_label = "themis-signing-key";
auto hsm = std::make_unique<HSMProvider>(config);
if (!hsm->initialize()) {
std::cerr << "Init failed: " << hsm->getLastError() << std::endl;
return 1;
}
std::cout << "HSM Ready!\n" << hsm->getTokenInfo() << std::endl;
// Test signing
std::vector<uint8_t> test_data = {'T', 'E', 'S', 'T'};
auto sig = hsm->sign(test_data);
if (sig.success) {
std::cout << "Signature: " << sig.signature_b64 << std::endl;
// Verify
bool valid = hsm->verify(test_data, sig.signature_b64);
std::cout << "Verified: " << (valid ? "YES" : "NO") << std::endl;
}
return 0;
}Error: Failed to load PKCS#11 library: libsofthsm2.so: cannot open shared object file
Solution:
# Find library
find /usr -name "libsofthsm2.so" 2>/dev/null
# Update config with correct path
export THEMIS_HSM_LIBRARY="/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"Error: C_OpenSession failed with code 3 (CKR_SLOT_ID_INVALID)
Solution:
# Initialize token
softhsm2-util --init-token --slot 0 --label "themis-dev" --pin 1234 --so-pin 5678Error: C_Login failed with code 160 (CKR_PIN_INCORRECT)
Solution:
# Check PIN in config matches token PIN
# Reset PIN if needed:
softhsm2-util --init-token --slot 0 --label "themis-dev" --pin NEW_PIN --so-pin 5678Warning: HSM signing not fully implemented - returning stub signature
Solution:
# Generate key pair
pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
--login --pin 1234 \
--keypairgen --key-type RSA:2048 \
--label "themis-signing-key"Enable verbose logging:
HSMConfig config;
config.verbose = true; // Enable debug outputOr via environment:
export THEMIS_HSM_VERBOSE=1
export THEMIS_LOG_LEVEL=DEBUG❌ Don't: Hardcode PINs in code
config.pin = "1234"; // BAD✅ Do: Use environment variables or secure vaults
config.pin = getenv("THEMIS_HSM_PIN"); // GOOD✅ Always set keys as non-extractable:
pkcs11-tool --keypairgen --extractable=false✅ Use appropriate key sizes:
- Minimum: 2048 bits
- Recommended: 3072 bits
- High Security: 4096 bits
✅ Limit slot access via HSM ACLs ✅ Use separate partitions for different services ✅ Rotate PINs regularly ✅ Audit all HSM operations
✅ Backup key material (encrypted) ✅ Test disaster recovery procedures ✅ Use HSM clustering for high availability
For eIDAS-qualified signatures, HSM must be:
- FIPS 140-2 Level 3 certified (or higher)
- Operated by Qualified Trust Service Provider (QTSP)
- Supporting Qualified Certificates
Supported HSMs:
- Thales Luna SA-7 (FIPS 140-2 Level 3)
- Utimaco CryptoServer CP5 (Common Criteria EAL 4+)
- AWS CloudHSM (FIPS 140-2 Level 3)
HSM integration supports:
- Art. 32: State-of-the-art encryption
- Art. 25: Security by design (keys never exposed)
- Art. 5(1)(f): Integrity and confidentiality
| Operation | Throughput | Latency |
|---|---|---|
| Sign (RSA-2048) | ~200 ops/sec | ~5 ms |
| Sign (RSA-4096) | ~50 ops/sec | ~20 ms |
| Verify (RSA-2048) | ~2000 ops/sec | ~0.5 ms |
| Verify (RSA-4096) | ~800 ops/sec | ~1.2 ms |
Hardware HSM (Thales Luna): 10-100x faster
- PKCS#11 Specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/
- SoftHSM2 Documentation: https://wiki.opendnssec.org/display/SoftHSMDOCS/
- Thales Luna HSM: https://cpl.thalesgroup.com/encryption/hardware-security-modules
- AWS CloudHSM: https://docs.aws.amazon.com/cloudhsm/
- eIDAS Regulation: https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32014R0910
Last Updated: April 2026
Version: 1.0
Author: ThemisDB Development Team