Key Management SDK Security Best Practices
SDK Version: 3.4.0
Audience: Integrators who run the Key Management SDK in production
The SDK holds a credential that can create, rotate and retire the keys of a tenant. This page covers how to keep that credential, the secrets the SDK returns, and the refusals it raises.
1. Custody of the workload credential
1.1 Prefer a federated token, then a private key file
- Federated token (
WorkloadCredential.federatedTokenFile/federated/federatedTokenCommand): the platform where the product runs mints a short-lived token for it (a Kubernetes service-account token, a cloud workload identity). Nothing long-lived is stored next to the product. - Private key (
WorkloadCredential.privateKeyJwt): the private key stays with the product, and the platform holds only the public half. Keep the file readable by the product's account only: on POSIX the SDK refuses a key file a group or other users can read, and on Windows it warns when another principal can read it. A key held in an HSM or a keystore can be passed as a JCAPrivateKey.
There is no client secret and no password: nothing the platform stores can sign in as the workload.
1.2 Rotate the workload key before it expires
A registered public key is accepted for twelve months by default and never for more than twenty-four. Register the successor on the workload's Keys tab, switch the product to it, then revoke the old key. Suspending or revoking the workload, or revoking a key, ends every token issued under it.
1.3 Keep the workload's scopes to what the product calls
Assign only the scopes of the operations the product runs (Prerequisites § Scopes). A workload is never admitted to tenant general policies, key backends, BYOK or key custody, and it can never approve a quorum request: the operations whose damage would be hardest to undo stay with people.
1.4 Keep the two planes on two credentials
The Key Management SDK runs as an administrative workload; the Runtime SDK runs as a runtime actor. Do not give one principal both: a leaked runtime credential then cannot create or destroy keys, and a leaked workload credential cannot decrypt data.
2. Secrets the SDK returns
2.1 The internal cryptographic use case client secret
internalCryptoUseCases().create(...) returns the new use case's client secret once, in a zeroizable SecretChars holder inside InternalCryptoUseCaseProvisioning. The platform keeps only a hash of it.
- Store it where the runtime reads it, inside a try-with-resources block, and let the block close the holder.
- If it is lost, rotate the credential; it cannot be read again.
- Never log it, print it or put it in an exception message. The SDK never does: the holder's
toString()prints[REDACTED].
2.2 Keystore passwords
Pass a keystore password as SecretChars. The SDK sends it as the raw bytes of its multipart part, never writes it to a file, and wipes it after the request.
3. Destruction is approved by people
A destruction request always opens a quorum request that a person other than the requester approves in the Admin Console; the server then destroys the key after the cooling-off period. A compromised workload credential can therefore request a destruction but never carry one out, and the request is visible and cancellable before anything is lost. See Key Destruction.
4. Correlation, audit and transport
- Correlation.
withCorrelationIdSupplier(...)sends your request id asX-Request-ID, so a platform audit row and your own log line can be joined. - Audit. Every operation is recorded in the tenant's audit trail under the workload's name; give the workload a name that identifies the product.
- TLS. Certificate verification is on by default.
kmsdk.tls.insecure=truetrusts every certificate and logs a warning; use it only against a development endpoint with a self-signed certificate. - Where requests go. The SDK sends every request, including the token request that carries the client assertion, to the host you configure (
kmsdk.scheme,kmsdk.host,kmsdk.port) and verifies it with the same TLS trust. The token endpoint the deployment publishes in its authorization server metadata must be on that same origin, or the SDK refuses to sign in; the assertion's audience is that origin plus/api, and every DPoP proof names it. The SDK never follows a redirect: a3xxanswer to any request is an error naming the status.
5. Error handling
Every refusal is one typed exception, KeyManagementSdkException, carrying the HTTP status and the RFC 9457 problem members and never a credential, a token or a response body.
| Status | Meaning | What to do |
|---|---|---|
400 | The request is malformed or a value is out of range | Fix the request; getProblemDetail() names the member |
401 | The token was refused | The SDK renews its token itself; a persistent 401 means the workload or its key was suspended or revoked |
403 | The workload lacks the scope, or the operation is not admitted for a workload | Assign the scope, or do the operation in the Admin Console |
404 | No such resource in the workload's tenant | Check the identifier; another tenant's resource also answers 404 |
409 | The resource's state does not admit the operation, or the Key ID was used before (…/key-id-retired) | Read the resource's state; choose a new Key ID |
422 | The transition is not valid for the resource's current status | Read the status first |
503 | A dependency is unavailable | Retry after getRetryAfter() when present |
Classify on getProblemType(), never on the message text, and do not show the problem detail to end users of your product without reviewing it.
6. Checklist
- The workload credential is a federated token or an owner-only private key file.
- The workload holds only the scopes the product calls.
- The use case client secret is stored inside a try-with-resources block and never logged.
- TLS verification is on in every environment but local development.
- Refusals are classified on the problem
type.
Version: 3.4.0
Last updated: 2026-10-26