Skip to content

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 JCA PrivateKey.

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 as X-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=true trusts 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: a 3xx answer 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