Keycloak¶
In order to use Keycloak with NetHSM to securely store private keys in NetHSM while enabling Keycloak to sign access tokens and decrypt JWE, you need to install and configure this key provider as follows:
AI Disclosure¶
This provider was developed and reviewed with substantial AI assistance.
Prepare the NetHSM¶
Prepare the NetHSM as follows. Refer to section administration for detailed instructions.
If NetHSM uses a self-signed certificate, record its fingerprint:
echo | openssl s_client -connect $NETHSM_HOST 2>/dev/null \ | openssl x509 -fingerprint -sha256 -noout
Create an Operator user dedicated for Keycloak.
The Keycloak extension discovers keys but doesn’t create them. Create the desired keys manually:
The key ID must begin with the prefix the realm will claim. Choose an appropriate naming scheme such as
kc-<realm>-<purpose>-<year>(for examplekc-staff-sig-rs256-2026,kc-partners-sig-es256-2026,kc-customers-enc-oaep-2026). The ID becomes thekidthat Keycloak publishes. Future key rotation is performed by adding new IDs which is why we suggest to use the year as part of the key ID.The mechanisms determine which JOSE algorithms the key can serve:
NetHSM key type and mechanism
JOSE algorithms served
RSA+RSA_Signature_PKCS1RS256, RS384, RS512 — hash-agnostic, so one key covers all three
RSA+RSA_Signature_PSS_SHA256/_SHA384/_SHA512PS256 / PS384 / PS512 — only the algorithm the mechanism names
EC_P256/EC_P384/EC_P521+ECDSA_SignatureES256 / ES384 / ES512 — the curve determines the algorithm
Curve25519+EdDSA_SignatureEdDSA
RSA+RSA_Decryption_OAEP_SHA1/_SHA256RSA-OAEP / RSA-OAEP-256, for decrypting inbound JWE
Installation¶
Download the Keycloak NetHSM provider JAR file, save it in Keycloak’s provider folder e.g. /opt/keycloak/providers/
Rebuild the provider registry: /opt/keycloak/bin/kc.sh build
Configuration¶
Settings may be given either in conf/keycloak.conf or as environment variables. The
two forms are the same settings; environment variables take precedence, so non-secret
values can live in the file with only the passphrase injected at runtime.
Using conf/keycloak.conf¶
spi-keys--nethsm--url=https://nethsm.internal:8443/api/v1
spi-keys--nethsm--username=keycloak
spi-keys--nethsm--passphrase=<passphrase>
spi-keys--nethsm--tls-fingerprints=aabbcc...
Using environment variables¶
Preferred in container deployments, because the passphrase can be supplied from a Docker or Kubernetes secret instead of being written to a file.
KC_SPI_KEYS__NETHSM__URL=https://nethsm.internal:8443/api/v1
KC_SPI_KEYS__NETHSM__USERNAME=keycloak
KC_SPI_KEYS__NETHSM__PASSPHRASE="<passphrase>"
KC_SPI_KEYS__NETHSM__TLS_FINGERPRINTS=aabbcc...
Note
The environment variable name is derived mechanically from the configuration key:
prefix it with KC_, convert to upper case, and replace every - with _.
The doubled underscores follow from the key’s structure,
spi-<spi-name>--<provider-id>--<option>, which for this extension is SPI
keys, provider nethsm, and the option name.
See also all available configuration options.
Restart Keycloak¶
The connection profile is only read when the provider factory is initialized. Restart Keycloak.
Confirm the server configuration¶
Check the startup log before configuring any realm. This separates a connection or credential problem from a realm configuration problem.
On success:
NetHSM key provider ready: 1 instance(s), user 'keycloak',
discovery cache PT5M, on-unavailable fail
Otherwise:
Log message |
Cause |
|---|---|
No message, or a debug line stating the provider is inactive |
|
|
Wrong address, blocked by a firewall, certificate pin mismatch, or the NetHSM is Locked or unprovisioned |
|
Wrong username or passphrase |
Add the provider to a realm¶
In the admin console, go to Realm Settings → Keys → Providers, choose Add provider,
and select nethsm.
Field |
Meaning |
|---|---|
Key use |
|
Key ID prefix |
Every NetHSM key whose ID begins with this string is published by this realm |
Algorithm |
The algorithm these keys are published under |
Priority |
|
Enabled / Active |
Both must be on for a key that should sign |
One component publishes one algorithm, mirroring Keycloak’s built-in key providers. A realm that needs both RS256 and ES256 therefore has two components. Keys under the prefix that cannot serve the chosen algorithm are skipped, so a prefix may safely match a mixed set of keys.
Caution
Do not point two components at the same key with different algorithms. Both would
publish a JWKS entry under the same kid.
Set the priority above 100¶
Important
A newly created realm contains four generated key providers — rsa-generated,
rsa-enc-generated, hmac-generated and aes-generated — all with priority
100. The NetHSM component must have a higher priority, otherwise Keycloak
continues signing with a generated software key and the configuration appears to work.
A value of 200 for NetHSM is a reasonable default.
Select the algorithm for the realm¶
Set Realm Settings → Tokens → Default Signature Algorithm to the algorithm configured on the component. If the realm requests an algorithm the component does not publish, Keycloak falls back to a generated key without reporting an error.
Verify¶
List the published keys:
curl -s https://<keycloak-host>/realms/<realm>/protocol/openid-connect/certs
The kid values should be the NetHSM key IDs.
The conclusive check is an issued token. Request one and decode its header:
curl -s \
-d "client_id=<client>" \
-d "client_secret=<secret>" \
-d "grant_type=client_credentials" \
https://$NETHSM_HOST/realms/<realm>/protocol/openid-connect/token \
| python3 -c 'import sys, json, base64
header = json.load(sys.stdin)["access_token"].split(".")[0]
print(json.loads(base64.urlsafe_b64decode(header + "==")))'
If kid is the NetHSM key ID, the signature was produced inside the HSM.
You could also watch NetHSM’s log in parallel which should show an appropriate call.
Further Details¶
You find further details here.