Signer Mode
The signer is the security-critical half of hoike’s split architecture. It holds private signing keys, reads revocation state from configured sources, and batch-produces ahu bundles — self-describing containers of pre-signed OCSP responses that edge nodes serve verbatim.
flowchart LR
CRL[CRL / Dogtag] -->|revocation state| S[Signer]
S -->|ahu bundle| G[Gossip / Export]
G --> E1[Edge 1]
G --> E2[Edge 2]
G --> EN[Edge N]
Enabling Signer Mode
Set mode = "signer" in the [server] section:
[server]
mode = "signer"
listen = "0.0.0.0:2560"
In signer mode, hoike does not serve OCSP responses to clients directly. Its job is to produce ahu bundles and distribute them to edge nodes (via gossip, manual copy, or scheduled fetch).
Revocation Sources
Each [[ca]] section declares a revocation source that the signer polls for certificate status. See Revocation Sources for the full reference.
CRL (implemented)
The CRL adapter reads a DER- or PEM-encoded CRL from a local path:
[[ca]]
label = "enterprise-issuing-01"
source = { type = "crl", path = "/var/lib/hoike/crls/enterprise.crl" }
The signer re-reads the CRL file on every batch cycle. An external process (e.g., cron + curl) is responsible for keeping the CRL file up to date.
Dogtag 389 DS syncrepl (requires --features dogtag-sync)
RFC 4533 Content Synchronization against the Dogtag 389 DS certificate repository. This is the positive issuance source — it enumerates every issued certificate, enabling authoritative-complete bundles where a miss is confirmed “never issued.”
[[ca]]
label = "dogtag-ca-01"
completeness = "authoritative-complete"
[ca.source]
type = "dogtag-sync"
ldap_url = "ldap://ds-iot.cert-lab.local:3389"
base_dn = "ou=certificateRepository,ou=ca,o=pki-iot-ca-CA"
bind_dn = "cn=Directory Manager"
bind_password_env = "HOIKE_LDAP_PASSWORD"
The adapter performs an initial full refresh, then uses the sync cookie for incremental updates. Certificate statuses are mapped: VALID→Good, REVOKED→Revoked (with reason/time), REVOKED_EXPIRED→Revoked, EXPIRED/INVALID→skipped.
Batch Production
The signer produces ahu bundles on a recurring schedule controlled by three parameters:
| Parameter | Default | Description |
|---|---|---|
batch_interval | 1h | How often the signer produces a new bundle |
validity | 24h | Response validity window (nextUpdate − thisUpdate) |
jitter | 2h | Random offset added to thisUpdate to prevent thundering-herd |
[[ca]]
label = "enterprise-issuing-01"
source = { type = "crl", path = "/var/lib/hoike/crls/enterprise.crl" }
batch_interval = "1h"
validity = "24h"
jitter = "2h"
Signer Outage Budget
The outage budget is the maximum time the signer can be offline before edge nodes begin serving expired responses:
outage_budget = validity − batch_interval
With defaults: 24h − 1h = 23 hours. This is the single most important number for capacity planning. If the signer goes down, edge nodes continue serving the last bundle until nextUpdate passes.
To increase the outage budget, increase validity — but longer validity means revocation information takes longer to propagate. This is a fundamental trade-off.
Jitter
The jitter parameter adds a random offset (up to the configured duration) to thisUpdate in each response. This prevents all responses in a bundle from expiring at exactly the same instant, which would cause a cache stampede at relying parties.
Signing Configuration
Signing Keys
hoike supports three key sources. The [ca.signing_key] table is required for signer/combined mode.
PKCS#8 file:
[ca.signing_key]
type = "file"
path = "/etc/hoike/ocsp-signing.key"
PKCS#11 HSM (requires --features pkcs11):
[ca.signing_key]
type = "pkcs11"
module = "/usr/lib/libCryptoki2_64.so" # Thales Luna
token_label = "hoike-partition"
key_label = "ocsp-signing"
pin_env = "HOIKE_HSM_PIN"
Omit pin and pin_env to prompt interactively at startup (recommended for production). Documented HSM module paths: Thales Luna, Entrust nShield, Utimaco CryptoServer, FutureX Vectera, Kryoptic (testing).
Demo key (testing only — produces a warning):
[ca.signing_key]
type = "demo"
Delegated Signing
When a delegated OCSP signing certificate is configured, it is embedded in every BasicOCSPResponse.certs per RFC 9919 §3.2.2. The ResponderID is computed from the certificate’s SPKI key hash (not the CA’s key).
[[ca]]
label = "enterprise-issuing-01"
responder_cert = "/etc/hoike/ocsp-responder.pem"
The responder certificate must have id-kp-OCSPSigning EKU and be issued by the CA.
Signature Algorithms
| Algorithm | Type | Notes |
|---|---|---|
ecdsa-p256 | Classical | Default; widely supported |
ml-dsa-44 | Post-quantum | NIST FIPS 204, security level 2 |
ml-dsa-65 | Post-quantum | NIST FIPS 204, security level 3 |
ml-dsa-87 | Post-quantum | NIST FIPS 204, security level 5 |
[[ca]]
label = "pqc-ready-ca"
sig_alg = "ml-dsa-65"
Note: Post-quantum algorithms produce significantly larger signatures. Verify that your relying parties support ML-DSA before switching.
Responder ID
The responder_id field controls how the responder identifies itself in OCSP responses:
responder_id = "by-key" # SubjectPublicKeyInfo hash (default, recommended)
CertID Compatibility
The certid_compat field controls which hash algorithms the signer uses for CertID matching:
| Value | Behavior |
|---|---|
dual | Index by both SHA-256 and SHA-1 hashes (default, widest compat) |
sha256 | SHA-256 only (RFC 9654 compliant, modern clients) |
sha1 | SHA-1 only (legacy clients only — not recommended) |
certid_compat = "dual"
PKCS#11 / HSM Support
PKCS#11 integration allows the signer to use hardware security modules (HSMs) for key storage and signing operations, including ML-DSA via CKM_ML_DSA. Configure the signing key as a PKCS#11 reference:
[ca.signing_key]
type = "pkcs11"
module = "/usr/lib/libCryptoki2_64.so" # Thales Luna
token_label = "hoike-partition"
key_label = "ocsp-signing"
pin_env = "HOIKE_HSM_PIN"
Omit pin and pin_env to prompt interactively at startup (recommended for production). Tested with Kryoptic; documented paths for Thales Luna, Entrust nShield, Utimaco CryptoServer, and FutureX Vectera.
Build with HSM support:
cargo build --release --features pkcs11
Dual-Algorithm Bundles
hoike can produce bundles containing both ECDSA and ML-DSA responses for the same certificates. Clients negotiate the preferred algorithm via RFC 6960 §4.4.7.1 PreferredSignatureAlgorithms.
hoike sign \
--ca enterprise-ca \
--crl ca.crl \
--signing-key ecdsa.key \
--sig-alg ecdsa-p256 \
--dual-alg ml-dsa-87 \
--pq-signing-key ml-dsa.key \
-o dual.ahu
One bundle, both algorithms, no flag day. A client that prefers ml-dsa-87 gets the ML-DSA response; all others get ECDSA.
Key Rotation
hoike monitors OCSP signing certificate expiry and can execute a renewal command automatically.
[ca.key_rotation]
renew_before_days = 7
check_interval_hours = 1
rotation_command = "/usr/local/bin/renew-ocsp-cert.sh"
The signer checks the responder certificate at each batch interval. When the certificate is within renew_before_days of expiry, hoike logs a warning and runs rotation_command. When the certificate has expired, hoike logs an error — responses signed with an expired certificate will be rejected by clients.
CMS Seal
Each ahu bundle is sealed with a CMS SignedData signature (RFC 5652) that binds the manifest, index, and data regions. The seal key must be distinct from the OCSP signing key.
seal_key = "/etc/hoike/seal-key.p8"
seal_cert = "/etc/hoike/seal-cert.pem"
Seal keys can be ECDSA P-256 or any ML-DSA variant. When seal_trust_anchors is configured in [storage], bundles without a valid seal are rejected on load.
Urgent Revocation
When urgent_revocation = true (the default), the signer produces an off-cycle delta bundle immediately upon detecting a newly revoked certificate — without waiting for the next scheduled batch_interval.
[[ca]]
urgent_revocation = true # default
This is critical for high-security deployments where the standard batch interval creates an unacceptable revocation propagation delay. The delta bundle is distributed to edges through the normal gossip or pull mechanism.
Set urgent_revocation = false only if your revocation SLA is satisfied by the regular batch interval.
Completeness
The completeness field declares the signer’s knowledge of the CA’s revocation state:
| Value | Meaning |
|---|---|
authoritative-complete | Signer has full revocation knowledge (direct CA access) |
partial | CRL-only; may miss certificates not yet on the CRL |
completeness = "authoritative-complete"
When set to authoritative-complete, the signer produces good responses for any serial number not found in the revocation source. When set to partial, unknown serials receive an unknown status, since the signer cannot confirm they are unrevoked.
Full Signer Example
[server]
mode = "signer"
listen = "127.0.0.1:2560"
[storage]
bundle_dir = "/var/lib/hoike/bundles"
state_db = "/var/lib/hoike/state"
[gossip]
enabled = true
bind = "0.0.0.0:7946"
identity_key = "/etc/hoike/gossip.key"
node_name = "signer-01"
[[ca]]
label = "enterprise-issuing-01"
source = { type = "crl", path = "/var/lib/hoike/crls/enterprise.crl" }
signing = "delegated"
responder_cert = "/etc/hoike/ocsp-responder.pem"
responder_key = "/etc/hoike/ocsp-responder.key"
sig_alg = "ecdsa-p256"
responder_id = "by-key"
certid_compat = "dual"
nonce_policy = "ignore"
validity = "24h"
batch_interval = "1h"
jitter = "2h"
completeness = "authoritative-complete"
urgent_revocation = true
Operational Considerations
-
Key protection: The signer is the only component that touches private keys. Run it on a hardened host with restricted network access. In high-security deployments, consider an air-gapped signer that exports bundles to removable media (see Air-Gap Deployments).
-
Monitoring: Watch
bundle_production_seconds(histogram) andbundle_next_update_seconds(gauge) metrics. Alert whenbundle_next_update_secondsdrops below your outage budget threshold. -
Backup: The
state_dbdirectory contains epoch high-water marks used for anti-rollback protection. Back it up — but never restore an old snapshot, as this could allow rollback attacks. -
Scaling: The signer does not need horizontal scaling — bundle production is inherently serial per CA. For multiple CAs, configure multiple
[[ca]]sections in a single signer (see Multi-CA Routing).