Class AnkaSecureKeyManagementSdk

java.lang.Object
co.ankatech.ankasecure.keymanagement.AnkaSecureKeyManagementSdk

public class AnkaSecureKeyManagementSdk extends Object
Factory / entry point for the AnkaSecure© Key Management SDK.

The Key Management SDK automates the key management of one tenant as an administrative workload: keys and their versions, rotation, destruction requests, the workload's own quorum requests, exchange contexts, exchanges, capability grants, constraint policies and internal cryptographic use cases, with read-only lookups of applications, actors and counterparty types. It authenticates workloads only: authenticateWorkload(WorkloadCredential) uses the OAuth 2.0 client_credentials grant (RFC 6749 section 4.4) with a signed or federated client_assertion and a DPoP proof (RFC 9449), and returns an immutable AutoCloseable AuthenticatedKeyManagementSdk. People sign in on the Admin Console; approving or rejecting a quorum request, tenant policies and key backends are console operations and are not part of this SDK.

The tenant is the one the workload's token names in its tenantId claim: a tenant workload acts only in its own tenant, so no method of the SDK takes a tenant.

Secret handling

A workload never sends a shared secret: it proves its identity with a key or an external token (WorkloadCredential). Access tokens are held only in memory as char[] and are never persisted. No credential, assertion, proof or token ever appears in a log line, exception message, or toString().

Transport security

TLS certificate verification is on by default (standard OkHttp validation). A trust-all toggle is available only via the kmsdk.tls.insecure=true configuration property; enabling it emits a WARN log naming the property and is intended solely for development against self-signed endpoints.

Every request goes to the configured origin (kmsdk.scheme, kmsdk.host, kmsdk.port) under that one TLS trust: the authorization server metadata, the token request with its client assertion, and every Key Management request. The metadata's token_endpoint must be the platform token endpoint on that same origin, or the workload is refused before any credential is sent; the client assertion's audience is the configured origin followed by /api, and every DPoP proof's htu names the configured origin. No request of the SDK follows a redirect: a 3xx answer is an error naming the status (KeyManagementSdkException), so neither the client assertion nor the access token is ever sent anywhere the SDK was not configured to reach.

Basic usage


 Properties props = new Properties();
 props.setProperty("kmsdk.scheme", "https");
 props.setProperty("kmsdk.host", "api.example.com");
 props.setProperty("kmsdk.port", "443");

 AnkaSecureKeyManagementSdk factory = new AnkaSecureKeyManagementSdk(props);
 WorkloadCredential credential = WorkloadCredential.privateKeyJwt(
         UUID.fromString("9b2f6c1e-3d4a-4c8b-a1e7-5f0d2c9b8a71"), Path.of("/etc/acme/workload-key.jwk"));
 try (AuthenticatedKeyManagementSdk sdk = factory.authenticateWorkload(credential)) {
     PageAdminKeyResponse keys = sdk.keys().list(KeyListQuery.firstPage());
 }
 
  • Constructor Details

    • AnkaSecureKeyManagementSdk

      public AnkaSecureKeyManagementSdk(Properties config)
      Constructs the factory from configuration properties.

      Recognized keys:

      • kmsdk.scheme — required: https or http. This constructor accepts either; authenticateWorkload(WorkloadCredential) then refuses plain http, before any request, unless the host is a loopback host or kmsdk.tls.insecure is true (development)
      • kmsdk.host — required: the host of the platform's public edge
      • kmsdk.port (default: the scheme's own port, 443 for https, 80 for http)
      • kmsdk.connectTimeoutMs (default 10000)
      • kmsdk.readTimeoutMs (default 30000)
      • kmsdk.writeTimeoutMs (default 30000)
      • kmsdk.tls.insecure (default false) — trust-all toggle; emits a WARN
      • kmsdk.auth.issuer (default https://auth.ankatech.co) — the platform issuer identifier the Auth API's metadata must name before a workload authenticates

      No origin is ever assumed: a configuration without a scheme or a host is refused, naming each missing key. A port or a timeout that is not a number in its range is refused naming its key, never replaced by its default. A refusal names keys, never their values.

      Parameters:
      config - configuration properties; must not be null
      Throws:
      NullPointerException - if config is null
      KeyManagementSdkException - if kmsdk.scheme or kmsdk.host is missing, the scheme is neither https nor http, or a port or timeout is malformed or out of range
  • Method Details

    • withCorrelationIdSupplier

      public AnkaSecureKeyManagementSdk withCorrelationIdSupplier(Supplier<String> supplier)
      Registers an optional correlation-id supplier for cross-service request tracing. When set, every SDK instance subsequently produced by this factory attaches an OkHttp interceptor to both its auth and key management HTTP clients that stamps the X-Request-ID header on each outbound request, using the value returned by supplier.get() at request time.

      The supplier is invoked per request, so it may return a fresh correlation id for each call. If it returns null or a blank value, no header is added. Passing null clears any previously registered supplier. The correlation id is a tracing token, not a secret; the SDK never logs it.

      Parameters:
      supplier - the correlation-id source, or null to disable correlation stamping
      Returns:
      this factory, for fluent chaining
    • authenticateWorkload

      public AuthenticatedKeyManagementSdk authenticateWorkload(WorkloadCredential credential)
      Authenticates an administrative workload and returns an immutable SDK instance whose every request is DPoP-bound (RFC 9449).

      The SDK first reads the Auth API's authorization server metadata (RFC 8414) and checks that it names the configured platform issuer (kmsdk.auth.issuer), lists ES256 DPoP proofs and publishes the platform token endpoint of the configured origin; a deployment that does not issue DPoP-bound workload tokens, or a document naming another origin, is reported here, before any credential is sent. Every DPoP proof names the configured origin, and the private_key_jwt audience is that origin followed by /api. The SDK then generates an EC P-256 key in memory for this instance (never written anywhere) and requests a token with grant_type=client_credentials, client_id = the workload id, the credential's client_assertion and a DPoP proof for that token endpoint. No client_secret or password is ever sent.

      Every later request carries Authorization: DPoP <token> and a new proof bound to the request's method, path and token. The token (five minutes) is renewed when it is within 60 seconds of its expiry, once for all threads waiting on it. The instance acts in the tenant the token names.

      A refused token request raises a KeyManagementSdkException carrying the HTTP status, the OAuth error (invalid_client for a refused credential, invalid_dpop_proof for a refused proof, invalid_request for a deployment that does not issue workload tokens) and, on a 503, the server's Retry-After. No other credential is tried.

      
       UUID workloadId = UUID.fromString("9b2f6c1e-3d4a-4c8b-a1e7-5f0d2c9b8a71");
       try (AuthenticatedKeyManagementSdk sdk = factory.authenticateWorkload(
               WorkloadCredential.federatedTokenFile(workloadId, Path.of("/var/run/secrets/tokens/ankasecure")))) {
           sdk.keys().get("payments-signing-key");
       } catch (KeyManagementSdkException e) {
           if ("invalid_client".equals(e.getOAuthError())) {
               // the workload, its credential or its federation binding was refused
           }
       }
       
      Parameters:
      credential - how the workload proves its identity; must not be null
      Returns:
      an authenticated SDK instance bound to the workload's tenant; never null
      Throws:
      KeyManagementSdkException - if the private key file is readable by others (POSIX) or holds no usable key, the metadata cannot be read or does not admit DPoP-bound workload tokens, the token request is refused, or the server is unreachable
      NullPointerException - if credential is null