Vendor-specific operational details for running sq-pkcs11 against an
Entrust nShield HSM (FIPS 140-3 Security World). The main
README covers the portable PKCS#11 / OpenPGP surface; this
file collects the nShield-specific paths, behaviours, and recipes that
don't belong there.
export PKCS11_MODULE_PATH=/opt/nfast/toolkits/pkcs11/libcknfast.soOr pass -m /opt/nfast/toolkits/pkcs11/libcknfast.so per invocation.
The Linux client must have the nfast group membership and the
hardserver running for the module to initialise.
The integration with sq-pkcs11 is purely PKCS#11 — but provisioning,
Security World administration, and operational verification all run
through nShield's own command-line tools:
| Tool | Used for |
|---|---|
generatekey pkcs11 … |
provision keys (FIPS-mode runs require ACS auth) |
createocs / ppmk |
create Operator Card Sets / softcards |
nfkminfo |
inspect Security World, list keys, dump key metadata |
cklist (in /opt/nfast/bin) |
dump full PKCS#11 attribute set per object, including vendor CKA_NFKM_* |
preload |
run the K/N OCS quorum ceremony and hand off an authenticated session to sq-pkcs11 (the recommended path for K>1 logins) |
For Operator Card Sets with K > 1 (a quorum of cards), wrap the
sq-pkcs11 invocation in nShield's preload utility. preload runs
the interactive ceremony — slot polling, "X of K cards read" progress,
per-card passphrase prompts — and then exec()s the child process with
the preloaded OCS available through the PKCS#11 module:
preload -c openssl-release-primary -- \
sq-pkcs11 cert-export \
--key-label openssl-release-primary \
--userid "OpenSSL Release Key <openssl-security@openssl.org>" \
...From sq-pkcs11's side the session looks already-authenticated — no
--pin-file is needed, and there is no --ocs flag. The PKCS#11
module picks up the preloaded cardset and treats the session as
logged-in; with most nShield setups the standard C_Login is bypassed
internally.
Why preload rather than calling C_LoginBegin / C_LoginNext /
C_LoginEnd ourselves:
preloadis the canonical nShield UX for K/N ceremonies — mature slot-state display, ESN feedback, card-swap handling.- Works in HSM Pool and load-sharing topologies, where the
C_LoginBegintrio does not. - Removes a vendor-specific
dlopenof nShield-only symbols fromsq-pkcs11, keeping the binary portable to other PKCS#11 modules.
Environment requirements for the child:
CKNFAST_LOADSHARING=1should be set (it is the default in modern Security World installs; verify on legacy hosts). Without it, the PKCS#11 module won't see the preloaded OCS andsq-pkcs11will observe an un-authenticated session.- Cards must be inserted into readers connected to the nShield host before the quorum prompts, exactly as today.
For K = 1 OCS, plain C_Login still works — use --pin-file <path>
(or the SQ_PKCS11_PIN env var) directly, without preload.
Visible via cklist -n --cka-label=<LABEL> on every nShield-resident
key. The ones that matter operationally:
| Attribute | Meaning |
|---|---|
CKA_NFKM_APPNAME |
Always pkcs11 for keys generated by generatekey pkcs11. |
CKA_NFKM_ID |
The Security World ident — bridge from PKCS#11 to nfkminfo. |
CKA_UNIQUE_ID |
<NFKM_ID>-NNNNNNNN per object handle. |
CKA_NFKM_HASH |
20-byte SHA-1 of the key blob — useful for cross-checks. |
Things you might expect but won't find:
| Attribute | Status on nShield |
|---|---|
CKA_START_DATE |
Always 0000 00 00 — never populated by generatekey. |
CKA_END_DATE |
Same — empty. |
CKA_KEY_GEN_MECHANISM |
Always CK_UNAVAILABLE_INFORMATION. |
| Any vendor "key creation time" attribute | Does not exist. |
The key generation timestamp is held only in the Security World blob
and exposed only via nfkminfo text output — see the next section.
The OpenPGP fingerprint is derived from key material and a creation
timestamp; see the README's --creation-time discussion. If you'd
like that timestamp to align with the actual nShield key-generation
event (rather than picking an arbitrary fixed value), extract it from
nfkminfo:
$ nfkminfo -k pkcs11 uab282744a02232accec4ccef07cde5cdeb6b1cd5b
Key AppName pkcs11 Ident uab282744a02232accec4ccef07cde5cdeb6b1cd5b
...
protection Module
gentime 2026-04-30 15:55:33
...
A small helper that maps a CKA_LABEL to its RFC 3339 gentime, ready
to feed to --creation-time:
#!/usr/bin/env bash
# hsm-gentime <CKA_LABEL>
# Prints e.g. 2026-04-30T15:55:33Z
set -euo pipefail
label=$1
# Step 1: CKA_LABEL → CKA_NFKM_ID
ident=$(/opt/nfast/bin/cklist -n --cka-label="$label" \
| awk -F'"' '/CKA_NFKM_ID/ { print $2; exit }')
# Step 2: ident → gentime (whitespace-separated date and time fields)
/opt/nfast/bin/nfkminfo -k pkcs11 "$ident" \
| awk '/^[ \t]*gentime/ { printf "%sT%sZ\n", $2, $3 }'Usage:
KEY_TIME=$(./hsm-gentime openssl-release-primary)
./sq-pkcs11 cert-export --creation-time "$KEY_TIME" ...
./sq-pkcs11 sign --creation-time "$KEY_TIME" ...The gentime value is whatever the host clock reported when
generatekey ran — there is no timezone marker stored in the key
blob. The script above appends Z (UTC) on the assumption that the
HSM client was running in UTC at provisioning time, which is the
safest convention for shared release infrastructure.
If your client was on local time during generatekey, translate
explicitly before feeding the value to sq-pkcs11:
# host was on Europe/Berlin during generatekey:
TZ=UTC date -d "2026-04-30 15:55:33 Europe/Berlin" +"%Y-%m-%dT%H:%M:%SZ"Once you've extracted and converted the timestamp the first time,
document it and treat it as fixed forever — exactly like any other
manually-chosen --creation-time value. The Security World is the
source of truth, but for verifiers all that matters is that the
OpenPGP certificate consistently embeds the same value.
sq-pkcs11 automatically handles the case where two or more nShield
modules participate in one Security World: every module-protected key
appears identically on each module's accelerator slot, and the tool
picks the first reachable slot for module-protected operations
(LoginMode::None). No action is required from the operator.
For OCS-protected keys (whether single-card via --pin-file or K/N
quorum via preload), the OCS slot must be addressed explicitly via
--key-uri pkcs11:token=<ocs-name>;... since multiple slots may carry
the same OCS state — see the README's authentication section for the
URI patterns.
The algorithm subset in sq-pkcs11 (RSA-3072 / RSA-4096 / ECDSA P-256
/ P-384 / P-521 with SHA-256/384/512) was chosen specifically to
match what nShield permits in FIPS 140-3 strict mode. Notable
exclusions imposed by that mode:
- Ed25519 — not approved before nShield firmware V13.7, and even then deployment-dependent. The tool does not advertise Ed25519 support.
- SHA-1 in any signing role — forbidden. All certificate self-signatures use SHA-256 or stronger.
- RSA-PKCS#1 with arbitrary hash — forbidden. We use
CKM_RSA_PKCSwith a pre-computedDigestInfo(built insrc/signer.rs::digest_info_prefix), not the all-in-oneCKM_SHA*_RSA_PKCSmechanisms.
tests/nshield/README.md— provisioning instructions for the unattended integration test suite.