Skip to content

Latest commit

 

History

History
724 lines (556 loc) · 20.1 KB

File metadata and controls

724 lines (556 loc) · 20.1 KB

HSM (Hardware Security Module) Integration Guide

Status: ✅ Implemented (PKCS#11 Interface)
Version: 1.0 (November 2025)
Compliance: eIDAS-ready, FIPS 140-2 compatible

📑 Table of Contents


Overview

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.

Supported HSMs

HSM Type PKCS#11 Support Status Use Case
SoftHSM2 ✅ Yes ✅ Tested Development/Testing
Thales Luna HSM ✅ Yes ⚠️ Compatible Production
Utimaco CryptoServer ✅ Yes ⚠️ Compatible Enterprise
AWS CloudHSM ✅ Yes (via PKCS#11) ⚠️ Compatible Cloud
YubiHSM 2 ✅ Yes ⚠️ Compatible Small deployments

Architecture

┌────────────────────────────────────────────────────┐
│                ThemisDB Application                 │
├────────────────────────────────────────────────────┤
│                                                     │
│  ┌──────────────────────────────────────────────┐ │
│  │         HSMProvider (C++ Wrapper)            │ │
│  └────────────────┬─────────────────────────────┘ │
│                   │                                 │
│  ┌────────────────▼─────────────────────────────┐ │
│  │       PKCS#11 Interface (dlopen)             │ │
│  └────────────────┬─────────────────────────────┘ │
└───────────────────┼──────────────────────────────┘
                    │
     ┌──────────────▼──────────────┐
     │  PKCS#11 Library (.so/.dll) │
     └──────────────┬──────────────┘
                    │
     ┌──────────────▼──────────────┐
     │   Hardware Security Module   │
     │  (Physical or Cloud-based)   │
     └─────────────────────────────┘

Key Components:

  1. HSMProvider: C++ wrapper class for PKCS#11 operations
  2. PKCS#11 Library: Vendor-specific library (e.g., libsofthsm2.so)
  3. HSM Device: Physical hardware or cloud HSM service

Installation & Setup

1. Install SoftHSM2 (Development/Testing)

SoftHSM2 emulates an HSM in software - perfect for development and CI/CD.

Ubuntu/Debian

sudo apt-get update
sudo apt-get install softhsm2 opensc

macOS

brew install softhsm

Windows

Download from: https://github.com/opendnssec/SoftHSMv2/releases

2. Initialize HSM Token

# 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-slots

Output:

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

3. Generate RSA Key Pair

# 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-objects

Expected 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

4. Generate Self-Signed Certificate (Optional)

# 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.pem

Token Initialization & Slot Configuration

Slot Selection

ThemisDB 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

By Token Label (recommended)

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";

By Slot ID

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_id is not found in the enumerated slot list, initialize() falls back to the first available slot and logs a warning. Call getLastError() to retrieve the diagnostic message.

PIN Handling

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=1234

PIN 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.

Initializing a New Token with softhsm2-util

# 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 01

Then 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";

Diagnosing Initialization Failures

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

Configuration

Environment Variables

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

C++ Configuration

#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();

Production Deployment

Thales Luna HSM

# 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-prod

ThemisDB 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

AWS CloudHSM

# 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!
quit

ThemisDB 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";

API Reference

HSMProvider Class

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();
};

HSMSignatureResult Structure

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
};

Testing

Unit Tests

# 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.

Integration Test

#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;
}

Troubleshooting

Common Issues

1. Library Not Found

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"

2. Token Not Initialized

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 5678

3. Wrong PIN

Error: 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 5678

4. Key Not Found

Warning: 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"

Debug Logging

Enable verbose logging:

HSMConfig config;
config.verbose = true;  // Enable debug output

Or via environment:

export THEMIS_HSM_VERBOSE=1
export THEMIS_LOG_LEVEL=DEBUG

Security Best Practices

1. PIN Management

Don't: Hardcode PINs in code

config.pin = "1234";  // BAD

Do: Use environment variables or secure vaults

config.pin = getenv("THEMIS_HSM_PIN");  // GOOD

2. Key Attributes

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

3. Access Control

Limit slot access via HSM ACLs ✅ Use separate partitions for different services ✅ Rotate PINs regularly ✅ Audit all HSM operations

4. Backup & DR

Backup key material (encrypted) ✅ Test disaster recovery procedures ✅ Use HSM clustering for high availability


Compliance

eIDAS Qualified Signatures

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)

GDPR Compliance

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

Performance

Benchmark Results (SoftHSM2)

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


References


Last Updated: April 2026
Version: 1.0
Author: ThemisDB Development Team