← Week 1: HSM fundamentals + PKCS#11

Day 2: PKCS#11 — The Interface Between Software and Hardware

Phase 5 · August 27, 2026

← Week 1: HSM fundamentals + PKCS#11

Agenda (2–3 hours)

  • Read (45 min): PKCS#11 v2.40 spec overview (slots, tokens, objects, sessions)
  • Study (60 min): The PKCS#11 object model; mechanism list; C_Sign flow
  • Write (30 min): Map PKCS#11 concepts to your provisioning service workflow
← Week 1: HSM fundamentals + PKCS#11

What Is PKCS#11?

PKCS#11 (also called Cryptoki — Cryptographic Token Interface) is:

  • A C API standard for communicating with cryptographic tokens (HSMs, smart cards, USB tokens)
  • Originally developed by RSA Security; now maintained by OASIS
  • The lingua franca of HSM access: CloudHSM, SoftHSM2, nCipher, Thales — all speak PKCS#11
  • Used by OpenSSL (via PKCS#11 engine), Java (via SunPKCS11 provider), and Rust (via cryptoki)

The key idea: your application never touches the key bytes.
Instead, it holds a handle to a key object inside the HSM.

← Week 1: HSM fundamentals + PKCS#11

PKCS#11 Object Model

Library (.so / .dll)
└── Slot 0 (physical or virtual slot)
    └── Token (the HSM or its logical partition)
        ├── Session (your connection to the token)
        │   ├── Object handles (CK_OBJECT_HANDLE — opaque integers)
        │   │   ├── CKO_PRIVATE_KEY  (your CA private key — never exported)
        │   │   ├── CKO_PUBLIC_KEY   (matching public key — can be exported)
        │   │   └── CKO_CERTIFICATE  (X.509 cert stored in HSM)
        │   └── Operations (sign, encrypt, derive, ...)
        └── (another session)

A CK_OBJECT_HANDLE is just an integer — it references the key inside the HSM.
You pass the handle to C_Sign; you never get the private key bytes back.

← Week 1: HSM fundamentals + PKCS#11

PKCS#11 Session Lifecycle

// 1. Initialize the library
C_Initialize(NULL);

// 2. Open a session on a slot (read-write session)
CK_SESSION_HANDLE hSession;
C_OpenSession(slotID, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &hSession);

// 3. Login (normal user = CKU_USER)
C_Login(hSession, CKU_USER, pin, pinLen);

// 4. Perform operations (generate key, sign, etc.)
// ...

// 5. Logout + close session
C_Logout(hSession);
C_CloseSession(hSession);
C_Finalize(NULL);

Two user types: CKU_USER (normal operations) and CKU_SO (Security Officer — admin operations like initializing the token or changing the user PIN).

← Week 1: HSM fundamentals + PKCS#11

Signing with PKCS#11

// Step 1: Find the private key by label or CKA_ID
CK_OBJECT_HANDLE hPrivKey = find_key_by_label(hSession, "ca-signing-key");

// Step 2: Initialize a signing operation
CK_MECHANISM mechanism = { CKM_ECDSA, NULL_PTR, 0 };
C_SignInit(hSession, &mechanism, hPrivKey);

// Step 3: Sign
CK_BYTE digest[32];  // SHA-256 of the data to sign
sha256(data, dataLen, digest);

CK_BYTE signature[64];
CK_ULONG sigLen = sizeof(signature);
C_Sign(hSession, digest, sizeof(digest), signature, &sigLen);

// signature contains the ECDSA signature — key never left the HSM

For CA signing: the CA software (SPIRE, EJBCA, your code) calls C_Sign with the CSR digest.
The HSM performs the signing. The private key never touches RAM.

← Week 1: HSM fundamentals + PKCS#11

Key PKCS#11 Mechanisms

A mechanism describes the cryptographic operation to perform:

Mechanism CKM constant Use
ECDSA (hash separately) CKM_ECDSA Sign pre-hashed data
ECDSA with SHA-256 CKM_ECDSA_SHA256 Sign + hash in one call
RSA PKCS v1.5 CKM_RSA_PKCS Legacy RSA signing
RSA PSS CKM_RSA_PKCS_PSS RSA signing (FIPS-compliant)
AES-GCM CKM_AES_GCM Symmetric encryption
EC key generation CKM_EC_KEY_PAIR_GEN Generate ECDSA key pair
AES key wrapping CKM_AES_KEY_WRAP Wrap key for backup

For your provisioning service: you need CKM_EC_KEY_PAIR_GEN + CKM_ECDSA_SHA256.

← Week 1: HSM fundamentals + PKCS#11

PKCS#11 in Rust: cryptoki

The cryptoki crate is a safe Rust wrapper around the PKCS#11 C API:

[dependencies]
cryptoki = "0.7"
use cryptoki::context::{CInitializeArgs, Pkcs11};
use cryptoki::session::UserType;
use cryptoki::types::AuthPin;

let pkcs11 = Pkcs11::new("/usr/lib/softhsm/libsofthsm2.so")?;
pkcs11.initialize(CInitializeArgs::OsThreads)?;

let slots = pkcs11.get_slots_with_token()?;
let slot = slots.first().expect("no slot");

let session = pkcs11.open_rw_session(*slot)?;
session.login(UserType::User, Some(&AuthPin::new("1234".into())))?;

// session is now authenticated — ready for key operations
← Week 1: HSM fundamentals + PKCS#11

Key Attributes: What Goes Into a PKCS#11 Object

When you generate or store a key, you set attributes that control its behavior:

use cryptoki::object::Attribute;

let key_attrs = vec![
    Attribute::Token(true),       // Persist across sessions (not ephemeral)
    Attribute::Private(true),     // Only visible after login
    Attribute::Sensitive(true),   // Value cannot be exported
    Attribute::Extractable(false),// Cannot be wrapped/exported even for backup
    Attribute::Sign(true),        // Permit signing operations
    Attribute::Label("ca-key".into()),
];

CKA_EXTRACTABLE = false + CKA_SENSITIVE = true means:
the key material can never leave the HSM under any circumstances.

This is the setting for CA private keys.

← Week 1: HSM fundamentals + PKCS#11

Challenge Assignment

Before Day 3 (hands-on SoftHSM2), answer these questions in writing:

  1. What is the difference between CKA_EXTRACTABLE = false and CKA_SENSITIVE = true?
    When would you set one but not the other?

  2. What is the difference between a session object and a token object in PKCS#11?
    Which type should your CA key be, and why?

  3. Map this PKCS#11 call sequence to your provisioning service:
    C_GenerateKeyPairC_Sign → return cert
    What is the PKCS#11 analogue of "the CA signs a leaf certificate"?

  4. What happens if the PKCS#11 session drops mid-signing?
    Is the partial signature visible? Does the caller need to retry?

Write answers in your notes or in acm-pca-design.md — these questions will resurface in Day 15 (failure modes).

← Week 1: HSM fundamentals + PKCS#11

Resources

  • PKCS#11 v2.40 spec: docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html
  • cryptoki Rust crate: docs.rs/cryptoki
  • SoftHSM2 README: github.com/opendnssec/SoftHSMv2
  • OpenSC PKCS#11 tools: github.com/OpenSC/OpenSC/wiki/PKCS11-Tool