Backend Keys API

The Backend Keys API creates, reads and revokes keys of the backend components (PowerAuth Server and the diw-gateway services diw-issuer, diw-connector, diw-verifier) and signs with them. See Backend Key Storage for the use cases and API General Principles for the shared conventions.

Target design; the endpoints are not implemented yet.

Callers are authenticated as backend callers, one client per component. No user authorization, no encryption. Binary values are standard Base64. Each client uses only the keys of its own domains.

Error Handling

Generic error codes (validation, authentication, HSM, PowerAuth Server, internal) are listed once in Possible Error Codes. Each endpoint below shows its error responses as Response 4xx and Response 5xx tabs, with a sample problem body and the possible codes.

Backend Types

Type JSON type Constraints Meaning
KeyId string lowercase UUID Key id chosen by the keystore.
BackendDomain string 1 to 48 characters, lowercase words joined by - A configured domain (keystore.backend.domains.<domain>).
BackendKeyType string ECDSA_P256, ECDSA_P384, MLDSA_65, MLDSA_87; only the domain’s configured types ECDSA_P256 → ES256, ECDSA_P384 → ES384, MLDSA_65/MLDSA_87 → pure ML-DSA (PKCS#11 3.2).
OwnerId string 1 to 128 characters of A-Z a-z 0-9 . _ : - Owner of a wrapped key (for PowerAuth: the activation id).

Wrapped backend key. A custom storage representation, header..iv.ciphertext.tag, with exactly this protected header, parsed strictly:

{"ver":"1","typ":"backend-key","alg":"dir","enc":"A256GCM","dom":"<domain>","kty":"<keyType>","gen":<n>,"kid":"<keyId>"}

The owner id is the JWE AAD (RFC 7516, section 5.1, step 14): the GCM additional data is ASCII(BASE64URL(header) || '.' || BASE64URL(ownerId)), so the key opens only for its domain, owner, key type and generation. Because the JWE Compact Serialization cannot carry a JWE AAD, the stored five-segment string is not a compact JWE: it is authenticated by the rules of the JWE JSON Serialization, and the keystore takes the JWE AAD from the request’s ownerId. A standard JOSE library opens it as a flattened JSON Serialization with aad set to BASE64URL(ownerId), never as a compact JWE. The owner is in the additional data rather than in the plaintext (as in the user wrapped key) so that a key presented for another owner fails the GCM tag check in the HSM before anything is decrypted. A component stores the wrapped key unchanged and never parses it. Wrapped user keys and wrapped backend keys are not interchangeable: each endpoint refuses the other kind.

Key Services

post /v1/backend/keys Create a Backend Key

Creates a key of keyType in one of the calling component’s domains. The domain’s configuration fixes the storage mode:

  • RESIDENT: a persistent, non-extractable key on keystore-<env>-backend, plus a database row. The row becomes ACTIVE only after the HSM has generated the key; if the row cannot be stored, the HSM object is destroyed.
  • WRAPPED: a temporary key, returned as wrappedKey and bound to ownerId. The keystore keeps nothing.

Callers: PowerAuth Server (master keys when an application is created, activation server keys at each activation) and the diw-gateway services (their signing keys).

Request

Request Headers
Content-Type: application/json
Authorization: Bearer <token>

The backend caller token of the client.

Request Body
{
  "domain": "powerauth-activation-ecdsa",
  "keyType": "ECDSA_P384",
  "ownerId": "2a7d9c14-5e0b-4c1e-9d0a-6f3b8e21c7a4"
}
Request Params
Attribute Type Description
domain* BackendDomain A configured domain.
keyType* BackendKeyType Accepted by the domain (else 400).
ownerId OwnerId Required for a WRAPPED domain, refused for a RESIDENT one (400).
name String The key’s name for the key inventory, for example credential-signing. Refused for a WRAPPED domain (400).

The name is 1 to 64 characters: lowercase letters and digits, in words joined by hyphens. It is stored with the key, returned by the read endpoint and written to the request log.

Response

A wrapped key; a resident key’s response has no wrappedKey.

{
  "keyId": "7c1e4b2a-…",
  "publicKey": "MHYw…",
  "wrappedKey": "eyJ2…"
}
Description of Response Params
Attribute Type Description
keyId KeyId The key id; for WRAPPED also the header’s kid.
publicKey String X.509 SubjectPublicKeyInfo, Base64.
wrappedKey String Only for WRAPPED: the wrapped backend key. The component stores it; PowerAuth Server stores it with the activation.

The request is invalid or refers to an unknown object.

{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_request"
}

Possible error codes:

  • invalid_request - The request fails a schema, pattern, encoding or transport rule (see Common Conventions).

The backend caller is not authenticated.

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "code": "backend_unauthenticated"
}

Possible error codes:

  • backend_unauthenticated - A missing, malformed or unknown bearer token. The response carries WWW-Authenticate: Bearer.

The caller is not authorized for this request.

{
  "type": "about:blank",
  "title": "Forbidden",
  "status": 403,
  "code": "backend_forbidden"
}

Possible error codes:

  • backend_forbidden - The domain is not one of the calling component’s domains.

An internal error occurred.

{
  "type": "about:blank",
  "title": "Internal Server Error",
  "status": 500,
  "code": "internal_error"
}

Possible error codes:

  • internal_error - A token or key configuration fault, or an unexpected error; no detail.

A dependency is unavailable; retry after the number of seconds in Retry-After.

{
  "type": "about:blank",
  "title": "Service Unavailable",
  "status": 503,
  "code": "hsm_unavailable"
}

Possible error codes:

  • hsm_unavailable - The HSM is unavailable.

get /v1/backend/keys/{keyId} Read a Backend Key

Reads a resident key, for example to get its public key. The HSM is not called.

Request

Request Headers
Authorization: Bearer <token>

The backend caller token of the client.

Path Params
Param Type Description
keyId* KeyId The key id.

Response

{
  "keyId": "7c1e4b2a-…",
  "domain": "diw-issuer-ecdsa",
  "name": "credential-signing",
  "keyType": "ECDSA_P256",
  "storage": "RESIDENT",
  "state": "ACTIVE",
  "publicKey": "MFkw…",
  "createdAt": "2026-11-10T10:00:00Z"
}
Description of Response Params
Attribute Type Description
keyId KeyId The key id.
domain BackendDomain The key’s domain.
name String Only when given.
keyType BackendKeyType ECDSA_P256, ECDSA_P384, MLDSA_65 or MLDSA_87.
storage String RESIDENT.
state String ACTIVE or REVOKED.
publicKey String As in the creation response.
createdAt String Creation time (date-time).
revokedAt String Revocation time (date-time), only when revoked.

The request is invalid or refers to an unknown object.

{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_request"
}

Possible error codes:

  • invalid_request - The request fails a schema, pattern, encoding or transport rule (see Common Conventions).
  • key_not_found - An unknown key, a key of another domain, or the id of a wrapped key.

The backend caller is not authenticated.

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "code": "backend_unauthenticated"
}

Possible error codes:

  • backend_unauthenticated - A missing, malformed or unknown bearer token. The response carries WWW-Authenticate: Bearer.

An internal error occurred.

{
  "type": "about:blank",
  "title": "Internal Server Error",
  "status": 500,
  "code": "internal_error"
}

Possible error codes:

  • internal_error - A token or key configuration fault, or an unexpected error; no detail.

post /v1/backend/keys/{keyId}/revocation Revoke a Backend Key

Destroys the resident key’s HSM object and marks the key REVOKED, atomically: if the destroy fails, the key stays ACTIVE, and an object that is already gone counts as destroyed. Revocation is permanent and idempotent: a repeated call returns the same response without calling the HSM.

A wrapped key cannot be revoked; the component simply deletes it.

Request

Request Headers
Content-Type: application/json
Authorization: Bearer <token>

The backend caller token of the client.

Path Params
Param Type Description
keyId* KeyId The key id.
Request Body

Exactly the empty object.

{}

Response

No body.

The request is invalid or refers to an unknown object.

{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_request"
}

Possible error codes:

  • invalid_request - The request fails a schema, pattern, encoding or transport rule (see Common Conventions).
  • key_not_found - An unknown key, a key of another domain, or the id of a wrapped key.

The backend caller is not authenticated.

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "code": "backend_unauthenticated"
}

Possible error codes:

  • backend_unauthenticated - A missing, malformed or unknown bearer token. The response carries WWW-Authenticate: Bearer.

An internal error occurred.

{
  "type": "about:blank",
  "title": "Internal Server Error",
  "status": 500,
  "code": "internal_error"
}

Possible error codes:

  • internal_error - A token or key configuration fault, or an unexpected error; no detail.

A dependency is unavailable; retry after the number of seconds in Retry-After.

{
  "type": "about:blank",
  "title": "Service Unavailable",
  "status": 503,
  "code": "hsm_unavailable"
}

Possible error codes:

  • hsm_unavailable - The HSM is unavailable.

Signing Services

post /v1/backend/signatures Sign with Backend Keys

Signs each item’s signing input with the item’s key. The response holds every signature or none. Use several items for a hybrid signature over the same data.

Every item is checked before the first HSM call:

  • a resident key must exist in the item’s domain and be ACTIVE;
  • a wrapped key must be well-formed, and its header must name the item’s domain, an accepted key type and an accepted generation;
  • the signing input must have the right length.

The owner id is checked by the GCM tag inside the HSM. If any item fails, no signature is returned; items already signed inside the HSM are discarded.

Callers: PowerAuth Server (activation-code signatures, signData, signJwt) and the diw-gateway components (credential JWTs, access tokens, request objects, certificates and CSRs).

Request

Request Headers
Content-Type: application/json
Authorization: Bearer <token>

The backend caller token of the client.

Request Body

The whole body must fit the 96 KB limit. Sample (hybrid, resident master keys; a wrapped key’s item has wrappedKey and ownerId):

{
  "items": [
    {"domain": "powerauth-master-ecdsa", "keyId": "785f…", "signingInput": "0dR2…(64)"},
    {"domain": "powerauth-master-mldsa", "keyId": "3f6a…", "signingInput": "YWN0…(the data)"}
  ]
}
Request Params
Attribute Type Description
items* Object[] 1 to 4 items, the signatures to make.
items[].domain* BackendDomain The domain of the item’s key. Items may name different domains, for example a hybrid signature with wrapped P-384 and ML-DSA keys.
items[].keyId KeyId Only in a RESIDENT domain; not with wrappedKey or ownerId.
items[].wrappedKey String Only in a WRAPPED domain, with ownerId; at most 16,384 characters, then parsed strictly.
items[].ownerId OwnerId Required with wrappedKey; the owner the key was created for.
items[].signingInput* String Standard Base64, 4 to 21,848 characters. See below.

The decoded signingInput is:

  • for ECDSA_P256: the 32-byte SHA-256 digest of the data, signed as is;
  • for ECDSA_P384: the 48-byte SHA-384 digest of the data, signed as is;
  • for ML-DSA: the data itself, 1 to 16,384 bytes, signed with pure ML-DSA.

Response

{
  "signatures": [
    {"keyType": "ECDSA_P384", "signature": "8kR3…(128)"},
    {"keyType": "MLDSA_87", "signature": "PxYD…(6172)"}
  ]
}
Description of Response Params
Attribute Type Description
signatures Object[] One object per item, in request order.
signatures[].keyType BackendKeyType The key type of the key that signed. See below.
signatures[].signature String Standard Base64, always raw: ECDSA R‖S, converted to DER by the component where it needs it, or the ML-DSA signature.

The keystore takes keyType from the key itself (a resident key’s record, or a wrapped key’s authenticated kty), never from the request. The client must check it against the type it expects and refuse any other; PowerAuth Server reports ERR0022.

The request is invalid or refers to an unknown object.

{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "code": "invalid_request"
}

Possible error codes:

  • invalid_request - The request fails a schema, pattern, encoding or transport rule (see Common Conventions).
  • key_not_found - An unknown key, a key of another domain, or the id of a wrapped key.

The backend caller is not authenticated.

{
  "type": "about:blank",
  "title": "Unauthorized",
  "status": 401,
  "code": "backend_unauthenticated"
}

Possible error codes:

  • backend_unauthenticated - A missing, malformed or unknown bearer token. The response carries WWW-Authenticate: Bearer.

The caller is not authorized for this request.

{
  "type": "about:blank",
  "title": "Forbidden",
  "status": 403,
  "code": "backend_forbidden"
}

Possible error codes:

  • backend_forbidden - The domain is not one of the calling component’s domains.

The request conflicts with the state of the unit, the key or the wrapped key.

{
  "type": "about:blank",
  "title": "Conflict",
  "status": 409,
  "code": "key_revoked"
}

Possible error codes:

  • key_revoked - A resident key of the request is revoked.
  • wrapped_key_invalid - A wrapped key is tampered, of another domain, owner, key type or generation, or malformed.

An internal error occurred.

{
  "type": "about:blank",
  "title": "Internal Server Error",
  "status": 500,
  "code": "internal_error"
}

Possible error codes:

  • internal_error - A token or key configuration fault, or an unexpected error; no detail.

A dependency is unavailable; retry after the number of seconds in Retry-After.

{
  "type": "about:blank",
  "title": "Service Unavailable",
  "status": 503,
  "code": "hsm_unavailable"
}

Possible error codes:

  • hsm_unavailable - The HSM is unavailable.
Last updated on Nov 11, 2026 (21:37) Edit on Github Send Feedback

develop

Secure Keystore