Skip to main content

Key storage and HSMs

Every organization signs its tokens (OpenID Connect ID tokens, access tokens, SAML assertions, agent tokens, and signed authorization requests and responses) with its own key. An HSM (hardware security module) is a device that holds a private key and performs signatures without ever releasing the key. By default LumoAuth generates and stores that key itself, encrypted at rest with the platform encryption key. If your compliance regime says the private key must live in hardware you control, point the key at an external provider instead.

Configure under Organization portal → Security → Signing keys → Generate new key, or at /orgs/{org}/portal/signing-keys. The Generate signing key form asks for the Algorithm (RSA, ECDSA or RSA-PSS and its variant) and, under Key storage, the provider that holds the private key; selecting an external provider reveals its fields. Activation and rotation holds the Sign new tokens with this key switch and the Rotate after period. Finish with Generate key.

The Signing keys page with the key list and the Generate new key button

The Key storage section of the Generate signing key form with one card per provider

Providers​

ProviderWhere the private key livesSigning happensNotes
LocalLumoAuth database, encrypted with the platform encryption keyIn LumoAuthDefault; no external dependency
Azure Key VaultAzure Key Vault (software or HSM-backed)In Azure
AWS KMSAWS KMSIn AWS
Google Cloud KMSCloud KMSIn GCP
HashiCorp VaultVault transit engineIn Vault
AkeylessAkeyless DFCIn Akeyless
AWS CloudHSMYour CloudHSM cluster, via a KMS custom key storeIn the HSMFIPS 140-3 L3, single-tenant
Azure Managed HSMManaged HSM poolIn the HSMFIPS 140-3 L3, single-tenant
Thales CipherTrustCipherTrust Manager applianceIn the applianceKeys created unexportable
Fortanix DSMFortanix DSM / SmartKeyIn the HSMSaaS (US/EU/UK) or self-hosted
Entrust KeyControlKeyControl VaultIn the vaultKeys created non-extractable
Oracle OCI VaultOCI VaultIn OCIHSM or SOFTWARE protection mode
IBM Key Protect (also HPCS)Wrapped by your root key; see the trade-offIn LumoAuthEnvelope encryption, not remote signing
OpenBaoOpenBao transit engineIn OpenBaoVault-compatible API
PKCS#11 HSM (through a signing sidecar)Any PKCS#11 HSMIn the HSMGeneric escape hatch; see below

All nine algorithms are available (RS256/384/512, PS256/384/512, ES256/384/512), except Oracle OCI Vault which supports exactly the same set through its own algorithm names.

Rules that apply to every external provider​

  • Credentials are encrypted at rest and are never rendered back into the form.
  • Endpoints must be public HTTPS hosts. Every call runs through the SSRF guard and pins the resolved IP, so a hostile DNS answer cannot redirect the request to an internal address between the check and the connection.
  • Private keys are never imported. Keys are generated inside the provider and marked non-extractable wherever the API allows it. importKey() is deliberately refused by the HSM providers.
  • Only the public key is stored in LumoAuth, alongside the provider's key identifier. The JWKS (the list of public keys applications use to verify tokens) and verification therefore keep working even if the provider is briefly unreachable; signing does not.
  • Losing access revokes signing. Deleting or disabling the key at the provider immediately stops LumoAuth from issuing new tokens for that key — this is the point of the feature ("crypto-shredding"), so keep a second, non-primary key if you want a fallback.

Per-provider setup​

AWS CloudHSM (KMS custom key store)​

Runs the standard KMS API (CreateKey, GetPublicKey, Sign, Verify, ScheduleKeyDeletion) with CustomKeyStoreId and Origin: AWS_CLOUDHSM on every create, so key material is generated inside your CloudHSM cluster and never exists outside it. Requests are SigV4-signed.

FieldValue
Regione.g. us-east-1
Access key ID / secret access keyIAM principal with kms:CreateKey, kms:CreateAlias, kms:GetPublicKey, kms:Sign
Session tokenOnly for temporary STS credentials
Custom key store IDcks-…, must be CONNECTED

Deletion honours KMS's 7–30 day pending window.

Azure Managed HSM​

Wire-compatible with Azure Key Vault (REST API version 7.4) with three differences: the vault URL is https://{pool}.managedhsm.azure.net, the Entra token audience is https://managedhsm.azure.net/.default, and key types are the HSM-protected RSA-HSM / EC-HSM variants.

FieldValue
HSM pool namee.g. contoso-hsm (or paste the full vault_url)
Organization ID / client ID / client secretService principal holding the Managed HSM Crypto User local RBAC role

Thales CipherTrust Manager​

FieldValue
CipherTrust Manager URLhttps://ciphertrust.example.com
Username + password, or API keyCipherTrust 2.10+ supports the api_key grant; older appliances use the password grant
DomainOptional CipherTrust domain

LumoAuth creates keys with usageMask sign+verify and unexportable: true, and signs a pre-computed digest via /api/v1/crypto/sign. ECDSA signatures come back ASN.1/DER and are converted to the raw R‖S form JWS requires.

Fortanix DSM​

FieldValue
DSM endpointhttps://apps.smartkey.io (EU: eu.smartkey.io, UK: uk.smartkey.io) or your own
App API keyThe base64 app_id:secret shown in the DSM UI
Group IDOptional; defaults to the app's group

The app authenticates with Basic <api_key> to /sys/v1/session/auth and uses the returned bearer for the key operations. RSA padding is selected with DSM's mode object (PKCS1_V15 or PSS with MGF1).

Entrust KeyControl​

FieldValue
KeyControl Vault URLhttps://keycontrol.example.com
Username / passwordVault user with key create + sign rights
Vault IDOptional; scopes the login to one vault

Uses the KeyControl Vault REST API (/vault/1.0/Login/, CreateKey, Sign, Verify, DeleteKey) with non-extractable keys. If your deployment only exposes the KMIP edition, use the PKCS#11 sidecar with Entrust's PKCS#11 library instead.

Oracle OCI Vault​

Two endpoints from the OCI vault page are needed, because OCI splits management from crypto:

FieldValue
Management endpointhttps://…-management.kms.<region>.oraclecloud.com
Crypto endpointhttps://…-crypto.kms.<region>.oraclecloud.com
Tenancy / user OCID, fingerprint, private key (PEM)The API signing key
Compartment OCIDWhere the key is created
Protection modeHSM (default) or SOFTWARE

Requests carry an OCI Signature version="1" header over date (request-target) host content-length content-type x-content-sha256. Signing sends the digest with messageType: DIGEST. Deleting schedules deletion 7 days out (forced) or 30 days out.

OpenBao​

OpenBao is the Linux Foundation fork of HashiCorp Vault and keeps the transit engine byte-for-byte compatible, so the configuration is identical to the HashiCorp Vault provider. It exists as a separate choice so that audits and inventories can tell the two products apart.

FieldValue
OpenBao addresshttps://openbao.example.com
Token or AppRole role ID + secret ID
Mount pathDefault transit

IBM Key Protect / HPCS (wrap mode)​

Read this before choosing IBM Key Protect

This provider uses envelope encryption, not remote signing. The signing key is generated locally and the private key is wrapped by your root key; at sign time it is unwrapped into LumoAuth's memory. It is the only external provider where the private key is briefly present in the application process.

Key Protect standard keys and the HPCS key-management REST API can wrap and unwrap data keys with a root key held in a FIPS 140-2 Level 4 HSM, but they do not expose an asymmetric Sign API over REST. HPCS signing is only reachable through GREP11 (Enterprise PKCS#11 over gRPC), which this PHP service cannot speak without a native gRPC extension. So:

  1. The key pair is generated locally with OpenSSL at creation time.
  2. The private key PEM is wrapped through POST /api/v2/keys/{rootKeyId}/actions/wrap, and only the wrapped blob — additionally sealed with the platform encryption key — is stored.
  3. At sign time the blob is unwrapped through /actions/unwrap, held in memory for the lifetime of the PHP process only, and the signature is computed locally.

What you get compared with the Local provider: the key is protected by a customer-controlled root key with IBM's Activity Tracker audit trail, and disabling or deleting that root key immediately revokes LumoAuth's ability to sign. What you give up compared with a true HSM signing provider: the private key is briefly in LumoAuth memory.

If you need a pure-HSM signing path on IBM Cloud, use the PKCS#11 sidecar with the HPCS PKCS#11 library instead.

FieldValue
IAM API keyWriter/Manager on the Key Protect instance
Instance IDKey Protect / HPCS instance GUID (bluemix-instance header)
Regione.g. us-south → https://us-south.kms.cloud.ibm.com
EndpointOptional override, e.g. HPCS https://api.<region>.hs-crypto.cloud.ibm.com:<port>
Root key IDOptional; a root key is created when empty
Key ring IDOptional

Generic PKCS#11 via a signing sidecar​

PHP cannot load a PKCS#11 module, so any HSM with a PKCS#11 library — Entrust nShield, Thales Luna, Utimaco, YubiHSM 2, the AWS CloudHSM client, SoftHSM2 for development — is reached through a small sidecar that owns the PKCS#11 session. A reference implementation (Python, python-pkcs11, standard-library http.server) ships in scripts/pkcs11-sidecar/ with its own README, requirements.txt and a systemd unit.

LumoAuth ──HTTPS + Bearer (optionally mTLS)──► sidecar ──PKCS#11──► HSM

Sidecar HTTP contract​

Every call carries Authorization: Bearer <token> and speaks JSON. alg is always the JOSE algorithm (RS256…ES512).

MethodPathRequestSuccess
GET/v1/health–200 {"status":"ok","token_label":"…","slot":0,"mechanisms":[…]}
POST/v1/keys{"alg":"RS256","key_size":3072,"label":"lumoauth-ab12"}201 {"key_id":"…","public_key_pem":"-----BEGIN PUBLIC KEY-----…"}
POST/v1/keys/{id}/sign{"alg":"RS256","data_b64":"<base64 signing input>"}200 {"signature_b64":"…"}
POST/v1/keys/{id}/verify{"alg":"…","data_b64":"…","signature_b64":"…"}200 {"valid":true}
DELETE/v1/keys/{id}–204

Errors are {"error":"…"} with 400, 401, 404, 500 or 501.

Three details are load-bearing:

  1. data_b64 is the unhashed signing input. The sidecar chooses the PKCS#11 mechanism (CKM_SHA256_RSA_PKCS, CKM_SHA256_RSA_PKCS_PSS, CKM_ECDSA over a locally computed digest), so the HSM decides how the digest is produced.
  2. Signatures come back in JWS form — raw R‖S for ES* (which is what PKCS#11 returns natively) and MGF1/salt-length-equals-digest-length for PS*. LumoAuth tolerates a DER ECDSA signature and converts it, but a compliant sidecar should not send one.
  3. /v1/keys/{id}/verify is optional. Answer 501 (or 404) and LumoAuth verifies locally against the stored public key instead.

key_id is whatever the sidecar returns (the reference implementation uses the hex-encoded CKA_ID); LumoAuth stores it as the key's external key id.

Configuring the provider​

FieldValue
Sidecar base URLhttps://hsm-sidecar.internal:8443 — HTTPS only
Bearer tokenThe sidecar's shared secret
Client certificate / key pathOptional mTLS pair, absolute paths on the LumoAuth host
CA certificate pathOptional, pins the sidecar's issuer

A sidecar normally sits on a private network, which the default SSRF guard rejects. Set PKCS11_SIDECAR_ALLOW_PRIVATE=true on the LumoAuth host to allow it; HTTPS is still enforced, the URL must have a host and may not carry credentials. mTLS paths must be absolute and free of .., and the certificate and key must be supplied together.

Rotation, health and troubleshooting​

  • Rotate with Rotate key on the signing keys page. A rotation creates a new key at the provider; the old key stays usable for verification during the grace period, so tokens already issued keep validating.
  • Provider validation runs when you generate a key: the form checks the provider connection before anything is created. Reachability problems surface as connection_failed, bad credentials as authentication_failed, and a deleted or disabled key as key_not_found.
  • JWKS keeps serving the public key even while the provider is unreachable; only new signatures fail, so relying parties are not immediately affected.
  • If a provider's REST schema differs from what LumoAuth expects (a KMIP-only KeyControl edition, an older CipherTrust appliance), fall back to the PKCS#11 sidecar with the vendor's own PKCS#11 library.

Standards used on this page​

Plain nameStandard
JSON Web Key (JWK) and key sets (JWKS)RFC 7517

See the standards reference for what each one covers and how LumoAuth uses it.