Craton HSM

Installation Guide

Prerequisites

RequirementVersionNotes
Rust1.75+2021 edition; install via rustup
protoc3.x+Required only for building the gRPC daemon
CMake3.x+Required by aws-lc-rs (rustls TLS dependency)
GitanyTo clone the repository

Platform-specific setup

Linux (Debian/Ubuntu)

sudo apt-get install -y protobuf-compiler cmake build-essential

macOS

brew install protobuf cmake

Windows

winget install Google.Protobuf Kitware.CMake
# Or: choco install protoc cmake

Build from Source

# Clone
git clone https://github.com/craton-co/craton-hsm-core.git
cd craton-hsm-core

# Build core PKCS#11 library only
cargo build --release

# Build everything (library + daemon + admin CLI + spy wrapper)
cargo build --release --workspace

Build artifacts

ArtifactLinuxWindowsDescription
PKCS#11 librarytarget/release/libcraton_hsm.sotarget/release/craton_hsm.dllLoad via dlopen/LoadLibrary
gRPC daemontarget/release/craton-hsm-daemontarget/release/craton-hsm-daemon.exeStandalone network server
Admin CLItarget/release/craton-hsm-admintarget/release/craton-hsm-admin.exeToken/key/PIN management
Spy wrappertarget/release/libpkcs11_spy.sotarget/release/pkcs11_spy.dllPKCS#11 call interceptor

Run Tests

# Full suite (617+ tests — MUST use single thread due to global PKCS#11 state)
cargo test -- --test-threads=1

# With aws-lc-rs backend
cargo test --features awslc-backend -- --test-threads=1

# Individual suites
cargo test --test crypto_vectors -- --test-threads=1
cargo test --test crypto_vectors_phase2 -- --test-threads=1
cargo test --test pkcs11_compliance -- --test-threads=1
cargo test --test multipart_sign_verify -- --test-threads=1
cargo test --test multipart_encrypt_decrypt -- --test-threads=1
cargo test --test supplementary_functions -- --test-threads=1
cargo test --test drbg -- --test-threads=1

Run Benchmarks

# Full benchmark suite (criterion, ~5 minutes)
cargo bench

# Specific group
cargo bench -- rsa_sign
cargo bench -- aes_gcm
cargo bench -- ml_dsa

# Reports generated in target/criterion/

Configuration

Craton HSM reads configuration from craton_hsm.toml (or the path in CRATON_HSM_CONFIG).

[token]
label = "My HSM Token"
storage_path = "craton_hsm_store"       # redb database directory
max_sessions = 100
max_rw_sessions = 10

[security]
pin_min_length = 8
pin_max_length = 64
max_failed_logins = 10               # lockout threshold
pbkdf2_iterations = 600000           # PIN hashing cost

[algorithms]
allow_weak_rsa = false               # block RSA < 2048
allow_sha1_signing = false           # block SHA-1 in signatures
enable_pqc = true                    # ML-DSA, ML-KEM, SLH-DSA

[audit]
enabled = true
log_path = "craton_hsm_audit.jsonl"
log_level = "all"                    # "all" | "crypto" | "auth" | "admin" | "none"

[daemon]
bind = "127.0.0.1:5696"
tls_cert = "tls.crt"                # PEM certificate
tls_key = "tls.key"                  # PEM private key
tls_client_ca = "clients-ca.crt"     # Required for a non-loopback bind (mTLS)

All fields have sensible defaults. A missing config file is not an error — Craton HSM starts with built-in defaults.

Docker

# Build the image (from repository root)
docker build -t craton_hsm:latest -f deploy/Dockerfile .

# Run with default config
docker run -p 5696:5696 craton_hsm:latest

# Run with custom config and TLS
docker run -p 5696:5696 \
  -v /path/to/craton_hsm.toml:/etc/craton_hsm/craton_hsm.toml:ro \
  -v /path/to/tls:/etc/craton_hsm/tls:ro \
  craton_hsm:latest

# Run admin CLI inside the container
docker exec <container> /usr/local/bin/craton-hsm-admin status

The image is based on gcr.io/distroless/cc-debian12:nonroot — no shell, no package manager, runs as non-root (UID 65534).

Kubernetes (Helm)

# Install the Helm chart
helm install my-hsm deploy/helm/craton_hsm/ \
  --set image.repository=your-registry/craton_hsm \
  --set image.tag=latest

# With TLS (create the secret first)
kubectl create secret tls craton_hsm-tls \
  --cert=tls.crt --key=tls.key
helm install my-hsm deploy/helm/craton_hsm/ \
  --set tls.enabled=true \
  --set tls.secretName=craton_hsm-tls

# With persistent storage
helm install my-hsm deploy/helm/craton_hsm/ \
  --set persistence.enabled=true \
  --set persistence.size=5Gi

# Verify
helm test my-hsm
kubectl logs deploy/my-hsm-craton_hsm

Helm values

ParameterDefaultDescription
image.repositorycraton_hsmContainer image
image.tag0.10.0Image tag
daemon.bind0.0.0.0:5696gRPC listen address
daemon.replicas1Replica count
tls.enabledfalseEnable TLS
tls.secretNamecraton_hsm-tlsK8s TLS secret name
persistence.enabledfalseEnable PVC for key store
persistence.size1GiPVC size
config.tokenLabelCraton HSM TokenPKCS#11 token label
config.enablePqctrueEnable post-quantum algorithms

Using as a PKCS#11 Provider

Generic

# Set the module path for any PKCS#11 consumer
export PKCS11_MODULE_PATH=/path/to/libcraton_hsm.so

Java SunPKCS11

Java includes a built-in PKCS#11 provider (SunPKCS11) that works with any compliant module.

SunPKCS11 config file (pkcs11.cfg):

name = Craton HSM
library = /path/to/libcraton_hsm.so
slot = 0

keytool (JDK command-line key management):

# List token info
keytool -providerClass sun.security.pkcs11.SunPKCS11 \
        -providerArg pkcs11.cfg \
        -keystore NONE -storetype PKCS11 \
        -list -storepass 1234

# Generate RSA key pair
keytool -providerClass sun.security.pkcs11.SunPKCS11 \
        -providerArg pkcs11.cfg \
        -keystore NONE -storetype PKCS11 \
        -genkeypair -alias "my-key" -keyalg RSA -keysize 2048 \
        -sigalg SHA256withRSA -dname "CN=My App" \
        -storepass 1234

# Generate EC P-256 key pair
keytool -providerClass sun.security.pkcs11.SunPKCS11 \
        -providerArg pkcs11.cfg \
        -keystore NONE -storetype PKCS11 \
        -genkeypair -alias "ec-key" -keyalg EC -groupname secp256r1 \
        -sigalg SHA256withECDSA -dname "CN=My EC Key" \
        -storepass 1234

# Generate CSR (exercises signing through PKCS#11)
keytool -providerClass sun.security.pkcs11.SunPKCS11 \
        -providerArg pkcs11.cfg \
        -keystore NONE -storetype PKCS11 \
        -certreq -alias "my-key" -sigalg SHA256withRSA \
        -file request.csr -storepass 1234

Programmatic Java access (KeyStore + Signature API):

import java.security.*;
import javax.crypto.*;

// Load SunPKCS11 provider
Provider provider = Security.getProvider("SunPKCS11")
    .configure("/path/to/pkcs11.cfg");
Security.addProvider(provider);

// Open KeyStore
KeyStore ks = KeyStore.getInstance("PKCS11", provider);
ks.load(null, "1234".toCharArray());

// Generate RSA key pair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", provider);
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();

// Sign data
Signature signer = Signature.getInstance("SHA256withRSA", provider);
signer.initSign(kp.getPrivate());
signer.update("data to sign".getBytes());
byte[] sig = signer.sign();

// Verify signature
Signature verifier = Signature.getInstance("SHA256withRSA", provider);
verifier.initVerify(kp.getPublic());
verifier.update("data to sign".getBytes());
boolean valid = verifier.verify(sig);  // true

Supported Java algorithms via Craton HSM:

Java AlgorithmPKCS#11 Mechanism
SHA256withRSACKM_SHA256_RSA_PKCS
SHA384withRSACKM_SHA384_RSA_PKCS
SHA512withRSACKM_SHA512_RSA_PKCS
SHA256withECDSACKM_ECDSA_SHA256
SHA384withECDSACKM_ECDSA_SHA384
SHA-256CKM_SHA256
SHA-384CKM_SHA384
SHA-512CKM_SHA512

Known limitations:

  • Post-quantum algorithms (ML-DSA, ML-KEM, SLH-DSA) are not supported by SunPKCS11
  • Token must be initialized with a user PIN before Java can access it
  • The slot in pkcs11.cfg must match a configured slot index (default: 0; configurable via slot_count in craton_hsm.toml)

Troubleshooting:

ErrorCauseFix
PKCS11Exception: CKR_TOKEN_NOT_PRESENTToken not initializedRun pkcs11-tool --module ... --init-token or craton-hsm-admin token init first
PKCS11Exception: CKR_PIN_INCORRECTWrong PIN in -storepassUse the user PIN, not the SO PIN
java.security.ProviderExceptionLibrary path wrong in configVerify library path in pkcs11.cfg points to actual .so/.dylib
CKR_CRYPTOKI_NOT_INITIALIZEDFork after init (e.g., Tomcat)Ensure C_Initialize is called in the child process

Running interop tests (Linux/macOS only — these shell scripts are not supported on Windows):

bash tests/interop/java_sunpkcs11.sh

OpenSSL & pkcs11-tool

Craton HSM is compatible with the most common PKCS#11 CLI tools. The most reliable is pkcs11-tool from the OpenSC project.

Prerequisites:

# Debian/Ubuntu
sudo apt-get install opensc gnutls-bin

# macOS
brew install opensc

# Verify
pkcs11-tool --version

pkcs11-tool — Token initialization and key management:

MODULE=/path/to/libcraton_hsm.so

# List available slots
pkcs11-tool --module $MODULE --list-slots

# List supported mechanisms
pkcs11-tool --module $MODULE --list-mechanisms

# Initialize token
pkcs11-tool --module $MODULE --init-token --label "MyToken" --so-pin 12345678

# Set user PIN
pkcs11-tool --module $MODULE --init-pin --pin 1234 --so-pin 12345678

# Generate RSA-2048 key pair
pkcs11-tool --module $MODULE --keypairgen --key-type RSA:2048 \
    --label "my-rsa-key" --id 01 --login --pin 1234

# Generate EC P-256 key pair
pkcs11-tool --module $MODULE --keypairgen --key-type EC:secp256r1 \
    --label "my-ec-key" --id 02 --login --pin 1234

# List all objects
pkcs11-tool --module $MODULE --list-objects --login --pin 1234

pkcs11-tool — Sign and verify:

# Create test data (32 bytes = SHA-256 hash length)
dd if=/dev/urandom of=data.bin bs=32 count=1

# RSA sign (SHA256-RSA-PKCS)
pkcs11-tool --module $MODULE --sign --mechanism SHA256-RSA-PKCS \
    --id 01 --login --pin 1234 \
    --input-file data.bin --output-file sig.bin

# RSA verify
pkcs11-tool --module $MODULE --verify --mechanism SHA256-RSA-PKCS \
    --id 01 --login --pin 1234 \
    --input-file data.bin --signature-file sig.bin

# ECDSA sign (raw hash, no wrapping)
pkcs11-tool --module $MODULE --sign --mechanism ECDSA \
    --id 02 --login --pin 1234 \
    --input-file data.bin --output-file ec_sig.bin

# SHA-256 digest
pkcs11-tool --module $MODULE --hash --mechanism SHA256 \
    --input-file data.bin --output-file digest.bin

p11tool (GnuTLS):

# List tokens
p11tool --provider=$MODULE --list-tokens

# List all objects (requires login for private objects)
p11tool --provider=$MODULE --list-all --login --set-pin=1234

# List mechanisms
p11tool --provider=$MODULE --list-mechanisms

OpenSSL 3.x pkcs11-provider:

OpenSSL 3.x supports PKCS#11 tokens via the pkcs11-provider module. This requires the provider to be installed separately.

# Install pkcs11-provider (Debian/Ubuntu)
sudo apt-get install openssl-pkcs11-provider

# Or build from source: https://github.com/latchset/pkcs11-provider

Create an OpenSSL config file (openssl_pkcs11.cnf):

openssl_conf = openssl_init

[openssl_init]
providers = provider_sect

[provider_sect]
default = default_sect
pkcs11 = pkcs11_sect

[default_sect]
activate = 1

[pkcs11_sect]
module = /usr/lib/x86_64-linux-gnu/ossl-modules/pkcs11.so
pkcs11-module-path = /path/to/libcraton_hsm.so
pkcs11-module-token-pin = 1234
activate = 1
# List objects in token via OpenSSL
OPENSSL_CONF=openssl_pkcs11.cnf openssl storeutl \
    "pkcs11:token=MyToken;pin-value=1234"

# Sign with a key from the token
OPENSSL_CONF=openssl_pkcs11.cnf openssl pkeyutl -sign \
    -provider pkcs11 -inkey "pkcs11:token=MyToken;object=my-rsa-key" \
    -in data.bin -out sig.bin

# List loaded providers
OPENSSL_CONF=openssl_pkcs11.cnf openssl list -providers

OpenSSL 1.1.x engine_pkcs11 (legacy):

# Install (Debian/Ubuntu)
sudo apt-get install libengine-pkcs11-openssl

# Sign using the engine
openssl pkeyutl -engine pkcs11 -sign \
    -keyform engine \
    -inkey "pkcs11:token=MyToken;object=my-rsa-key;pin-value=1234" \
    -in data.bin -out sig.bin

SSH Agent

# Add PKCS#11 module to SSH agent
ssh-add -s /path/to/libcraton_hsm.so
# Enter user PIN when prompted

# List keys from the HSM
ssh-add -L

# Remove PKCS#11 module
ssh-add -e /path/to/libcraton_hsm.so

Known limitations:

  • SSH only uses RSA and ECDSA keys from the token (not Ed25519 via PKCS#11)
  • The token must be initialized and contain at least one key pair
  • Some SSH clients require the key to have CKA_SIGN = true

OpenSSL / pkcs11-tool troubleshooting:

ErrorCauseFix
No slot with a token was foundToken not initializedRun pkcs11-tool --init-token
CKR_PIN_INCORRECTWrong PINUse user PIN for operations, SO PIN for admin
CKR_MECHANISM_INVALIDUnsupported mechanismCheck --list-mechanisms for available mechanisms
error loading pkcs11 moduleWrong library pathVerify --module path; use absolute path
CKR_CRYPTOKI_NOT_INITIALIZEDFork or re-execCall C_Initialize before operations
Database is lockedAnother process has the DB openStop other Craton HSM instances or use craton-hsm-daemon

Running interop tests (Linux/macOS only — these shell scripts are not supported on Windows):

bash tests/interop/openssl_pkcs11.sh

Using the Admin CLI

# Initialize token (prompts for SO PIN)
craton-hsm-admin token init --label "Production HSM"

# Check status
craton-hsm-admin status
craton-hsm-admin status --json

# Key management
craton-hsm-admin key list
craton-hsm-admin key import --file private.pem --label "App Key" --type RSA
craton-hsm-admin key delete --handle 42

# PIN management
craton-hsm-admin pin change --user-type USER
craton-hsm-admin pin reset    # SO resets user PIN

# Audit log
craton-hsm-admin audit dump --last 100
craton-hsm-admin audit dump --last 50 --json

Using the Spy Wrapper

The spy wrapper intercepts all PKCS#11 calls for debugging. It works with any PKCS#11 library, not just Craton HSM.

# Configure the spy
export PKCS11_SPY_TARGET=/path/to/libcraton_hsm.so   # real library
export PKCS11_SPY_LOG=spy.jsonl                     # log file

# Point your application to the spy instead
export PKCS11_MODULE_PATH=/path/to/libpkcs11_spy.so

# All PKCS#11 calls are logged as JSON lines:
# {"timestamp":"2025-01-15T10:30:01Z","function":"C_Sign","args":"mechanism=CKM_ECDSA","rv":"CKR_OK","duration_ms":2}

Troubleshooting

SymptomCauseFix
CKR_GENERAL_ERROR on all callsFIPS POST failedCheck binary integrity; rebuild
CKR_PIN_LOCKEDToo many failed login attemptsSO resets via craton-hsm-admin pin reset
CKR_SESSION_COUNTSession limit reachedClose unused sessions; increase max_sessions
CKR_CRYPTOKI_NOT_INITIALIZEDC_Initialize not calledCall C_Initialize(NULL) first
CKR_TOKEN_NOT_PRESENTToken not initializedRun craton-hsm-admin token init
Daemon fails to startprotoc missing at build timeInstall protobuf-compiler
Daemon TLS errorInvalid cert/key pathCheck paths in [daemon] config