Home / Blog / Integration

PKCS#11 migration guide: connecting existing payment software to a post-quantum HSM

PKCS#11 interface diagram showing CQ1 integration with payment middleware

The PKCS#11 standard — formally Cryptoki, RSA Security's Cryptographic Token Interface Standard, now maintained by the OASIS PKCS11 Technical Committee — was designed in 1994 for smart card and HSM interoperability. It has outlasted every cryptographic algorithm family it was originally specified to support. RSA-512, DES, RC4 — all broken, all removed from baseline. The PKCS#11 interface itself persists.

For post-quantum migration, this longevity is both an advantage and a constraint. The advantage: your payment middleware almost certainly already has a PKCS#11 integration path for its existing HSM. Migrating to a PQC-capable HSM does not require rewriting your application stack from scratch — it requires extending the PKCS#11 integration to handle new mechanism types and larger key objects. The constraint: PKCS#11's data model for keys and mechanisms was designed around classical cryptographic objects, and post-quantum algorithms introduce structures (polynomial arrays, ciphertext vectors) that don't map cleanly to the classical object model without vendor extensions.

This guide covers the practical mechanics of connecting existing payment middleware to a CQ1 module over PKCS#11. We cover mechanism type definitions, key object attribute changes for Kyber and Dilithium, buffer sizing for the new ciphertext types, and the session management patterns that affect throughput under payment transaction load.

PKCS#11 architecture review: sessions, slots, and tokens

For readers who have integrated PKCS#11 before but may need a refresher on the relevant parts: a PKCS#11 module (the .so or .dll file that your application loads) exposes one or more slots. Each slot represents a physical or logical cryptographic device. CQ1 presents as a single PKCS#11 slot. Within the slot, the token is the logical key storage and execution environment.

Applications interact with the token through sessions. PKCS#11 sessions are the primary unit of parallelism: multiple sessions can be opened on the same token simultaneously, and operations on different sessions execute concurrently (where the hardware supports it). CQ1's session concurrency model: up to 256 concurrent sessions, with hardware-level queuing ensuring that operations that cannot execute simultaneously are serialized without application-visible deadlocking. For payment authorization servers handling multiple concurrent transaction threads, open one session per thread — do not share sessions between threads without external locking.

The sequence for a Kyber encapsulation operation under PKCS#11:

  1. C_OpenSession — establish a session on the CQ1 slot
  2. C_FindObjectsInit / C_FindObjects / C_FindObjectsFinal — locate the recipient's Kyber-1024 public key object by label or ID attribute
  3. Vendor-extended C_EncapsulateKey — pass the public key object handle, receive ciphertext and shared secret as output buffers
  4. C_CloseSession — or keep the session open for the next operation (session reuse is recommended for throughput)

Key object attributes for Kyber-1024

Classical RSA and EC keys in PKCS#11 are represented as CKO_PUBLIC_KEY or CKO_PRIVATE_KEY objects with algorithm-specific value attributes. For RSA, the public key object carries CKA_MODULUS and CKA_PUBLIC_EXPONENT. For EC, CKA_EC_PARAMS and CKA_EC_POINT. Kyber keys require the same object class framework but with new attribute definitions.

In the CQ1 PKCS#11 provider, Kyber-1024 key objects use the following attribute structure:

  • CKA_KEY_TYPE: CKK_KYBER (vendor-defined value 0x80000001)
  • CKA_KYBER_PARAMS: parameter set identifier — CKP_KYBER1024 for Kyber-1024 / ML-KEM-1024
  • Public key: CKA_VALUE containing the 1,568-byte Kyber-1024 public key (ek in FIPS 203 notation)
  • Private key: CKA_VALUE containing the 3,168-byte Kyber-1024 decapsulation key (dk), stored inside the CQ1's tamper-resistant boundary — not accessible via C_GetAttributeValue

The public key value (1,568 bytes) can be imported into the CQ1 token for a counterpart's public key using C_CreateObject with CKA_KEY_TYPE=CKK_KYBER, CKA_CLASS=CKO_PUBLIC_KEY, and CKA_VALUE set to the raw public key bytes. The CQ1 will validate that the imported value length matches the expected parameter set before creating the object.

Key generation for new Kyber-1024 key pairs uses C_GenerateKeyPair with mechanism CKM_KYBER_KEY_PAIR_GEN. The private key is generated within the CQ1's security boundary and never exported in plaintext. The public key object is returned with CKA_VALUE populated and CKA_SENSITIVE=FALSE (the public key is, by construction, public).

Buffer sizing: where migration breaks in practice

The most common cause of failed PKCS#11 PQC migrations in our integration testing is buffer sizing. Classical PKCS#11 integrations allocate fixed output buffers sized for classical ciphertext or signature output. When the mechanism changes to Kyber or Dilithium, the output sizes are significantly larger.

Key sizes that change between classical and PQC:

Object Classical size PQC size Note
Public key (storage) 32 bytes (EC P-256) 1,568 bytes (Kyber-1024) 49× larger
Encapsulation ciphertext 32 bytes (ECDH share) 1,568 bytes (Kyber-1024) Must be transmitted to decapsulator
Signature (storage/transport) 72 bytes (EC P-256) 3,293 bytes (Dilithium-3 / ML-DSA-65) 45× larger
Signing key (private) 32 bytes (EC P-256) 4,032 bytes (Dilithium-3) Never leaves CQ1

Any code that allocates a fixed-size buffer for C_Sign output, C_Encrypt output (in key wrapping use cases), or C_Decrypt ciphertext input must be audited and updated. The correct approach is the two-call PKCS#11 buffer size query pattern: call C_Sign with a NULL output pointer and a zero-length ulSignatureLen; the CQ1 driver will return CKR_OK with ulSignatureLen set to the required output buffer size. Allocate that size, then call C_Sign again with the populated buffer. This pattern is specified in the PKCS#11 v2.40 standard but is frequently implemented as a shortcut (hardcoded buffer) in classical integrations.

Zone working key migration: the payment-specific challenge

Payment HSM integrations use PKCS#11 (or proprietary APIs layered on top) to manage zone working keys (ZWKs), zone PIN keys (ZPKs), and master key encryption keys (MKEKs). The key hierarchy in a payment HSM is: MKEK protects ZWKs; ZWKs protect transaction-level data. When migrating to a PQC-capable HSM, the key hierarchy must be migrated carefully to avoid both continuity gaps and key exposure.

The migration path for ZWKs in a Kyber-based HSM architecture:

  1. Generate a new Kyber-1024 key pair on the CQ1 — this becomes the CQ1's key transport key for receiving ZWKs from the existing classical HSM
  2. Export existing ZWKs from the classical HSM wrapped under the CQ1's Kyber-1024 public key — the existing HSM generates a random 256-bit AES session key, wraps the ZWK with it (using C_WrapKey), then encapsulates the AES session key under Kyber-1024 using a software Kyber implementation (since the existing HSM doesn't have native Kyber support)
  3. Import the wrapped ZWK package into the CQ1 — the CQ1 uses its Kyber-1024 private key to decapsulate the AES session key, then decrypts and imports the ZWK
  4. Verify ZWK integrity on the CQ1 — perform a test MAC using the ZWK on a known test vector before putting the ZWK into production use

Step 2 requires a software Kyber implementation on the classical HSM or an adjacent server. For the initial migration, this is acceptable — the Kyber operation in step 2 is key transport, not ongoing transaction signing, and the risk window is limited to the migration ceremony. Once the ZWK is resident in the CQ1, all subsequent operations use hardware Kyber.

Session throughput tuning

The CQ1 supports up to 256 concurrent PKCS#11 sessions. For a payment authorization server, the optimal session configuration depends on thread count and operation mix:

  • Dedicate one PKCS#11 session per application thread. Do not share sessions between threads — PKCS#11 sessions are not thread-safe by default, and the CQ1 driver's session locking will serialize concurrent access from multiple threads into a single queue, eliminating concurrency benefit.
  • For Kyber encapsulation at high rates (>5,000 operations/second), use C_EncapsulateKey in batches where the protocol allows. The CQ1 driver supports a vendor-extended batch encapsulation function that amortizes the PKCS#11 function call overhead across multiple operations, reducing per-operation latency at the driver boundary by approximately 30% at batch size 8.
  • For Dilithium-3 signing operations, the signature size (3,293 bytes) means that output buffer allocation becomes a significant cost at high rates. Pre-allocate output buffers at session-open time rather than per-operation to avoid repeated heap allocation under load.

The CQ1 evaluation unit SDK ships with a PKCS#11 connection pool implementation in C and a JCA/JCE provider that wraps the native PKCS#11 interface. Both implement the session management patterns above. The JCA/JCE provider allows Java-based payment middleware to call CQ1 Kyber and Dilithium operations via the standard KeyPairGenerator, KeyAgreement, and Signature interfaces without any knowledge of PKCS#11 mechanics.

Diagnostic and audit log integration

Payment environments require HSM audit logging for key usage events — all key generation, key import/export, and cryptographic operations that use resident keys must be logged to a tamper-evident audit trail. CQ1's PKCS#11 driver emits a structured syslog stream for all key operations, compatible with the log format expected by most payment-sector SIEM systems. The log includes: session ID, timestamp (microsecond precision, sourced from the CQ1's internal oscillator synchronized via PPS if available), operation type, key object label/ID, and a status code.

The audit log integration requires configuring a syslog destination in the CQ1 management interface — accessible via the management Ethernet port on the PCIe host adapter. The management interface is separate from the PCIe data path that PKCS#11 operations traverse; this ensures that audit log delivery cannot be impeded by high-throughput transaction load on the main PCIe bus.

Cryptrig is not a system integrator. We do not offer PKCS#11 integration consulting services. The documentation above reflects what the CQ1 PKCS#11 driver exposes and what we have observed during evaluation unit integration support. Your specific payment software stack will have integration requirements that are best addressed by your internal integration engineers working with the CQ1 evaluation unit SDK.