Skip to content

API Reference

Purpose: Complete API endpoint reference for migration and interoperability operations

Base Paths: the interoperability utilities are Core API operations under /api/v3/interoperability, and the PKCS#7 utilities under /api/v3/migration; importing key material is a control-plane operation and is reached on the Admin API, under /api/v3/admin/tenants/{tenantId}/keys/.

Version: 3.0.0

Authentication: JWT Bearer token required for all endpoints


Endpoint Overview

The migration, interoperability and import endpoints:

Endpoint Method Purpose Persistence Scope Required
/api/v3/admin/tenants/{tenantId}/keys/import-keystore POST Import PKCS#12/JKS keystore ✅ Persisted SCOPE_migration.importKeystore
/api/v3/admin/tenants/{tenantId}/keys/import-keystore/analyze POST Preview keystore import (per-alias purpose detection) ❌ Read-only SCOPE_migration.importKeystore
/api/v3/interoperability/verify POST Verify JWS with external key ❌ Stateless SCOPE_interoperability.verifySignature
/api/v3/interoperability/encrypt POST Encrypt with external key ❌ Stateless SCOPE_interoperability.encrypt
/api/v3/admin/tenants/{tenantId}/keys/{kid}/export GET Export the public material and metadata of a key ❌ Read-only SCOPE_admin.keys.read
/api/v3/admin/tenants/{tenantId}/keys/import-key POST Import key from JWK/DER ✅ Persisted SCOPE_migration.importKey
/api/v3/migration/analyze-pkcs7 POST Analyze a PKCS#7/CMS file (Base64, bounded) ❌ Read-only SCOPE_migration.analyzePkcs7
/api/v3/migration/stream/analyze-pkcs7 POST Analyze a PKCS#7/CMS file (multipart, no size limit) ❌ Read-only SCOPE_migration.analyzePkcs7
/api/v3/migration/convert-pkcs7-to-jose POST Convert PKCS#7/CMS to JOSE (Base64, bounded) ❌ Stateless SCOPE_migration.convertPkcs7
/api/v3/migration/stream/convert-pkcs7-to-jose POST Convert PKCS#7/CMS to JOSE (multipart, at most 100 MB) ❌ Stateless SCOPE_migration.convertPkcs7

PKCS#7 Analysis and Conversion

The four PKCS#7 operations are documented in full, with examples and the error contract, on PKCS#7 Conversion. This section lists the members of their contract.

Every signer and every recipient is carried. A SignedData with N signers converts to a JWS with one signature per signer, an EnvelopedData with M recipients to a JWE with one recipient entry per recipient, and a SignedAndEnvelopedData (RFC 2315 §11) to a JWE whose plaintext is a JWS with one signature per outer signer. One conversion accepts at most 16 signers and recipients together; above it the request is refused 422 ACTOR_LIMIT_EXCEEDED before any key is looked up.

Convert request members

Member Required Values Meaning
pkcs7Data yes (compact only) Base64 DER The PKCS#7 file. The streaming operation takes it as the file part, after the metadata part.
decryptionKid no a kid For an encrypted structure, the tenant key that decrypts. It must name one of the recipients (else 400 invalid-input; a kid the tenant does not hold is 404). When absent, the first recipient in encoded order whose tenant key admits decryption and encryption decrypts; there is no fallback to another recipient after a refusal or a decryption failure. Ignored for SignedData.
serialization no AUTO (default), COMPACT, JSON — exact case AUTO: the single-actor form for at most one signer and at most one recipient, General JSON otherwise. COMPACT: the single-actor form; more than one signer or recipient is 422 INCOMPATIBLE_SERIALIZATION. JSON: General JSON, also for one actor. Any other value is 400 invalid-input naming serialization.
targetFormat no AUTO (default), JWS, JWE, JWE_JWS_NESTED Must match the parsed structure, else 422 INCOMPATIBLE_TARGET_FORMAT.
algorithmPolicy.allowLegacyHash no boolean, default false Admits a SHA-1 or MD5 signer; otherwise 422 LEGACY_ALGORITHM_REJECTED.

These are the only members. Any other — at the root or inside algorithmPolicy, on both operations — is refused 400 invalid-input with a detail naming the member, never its value; that includes validateCertificates, validateTimestamp and algorithmPolicy.contentEncryptionUpgrade, which the request does not define. The CMS signature of every re-signed signer is always verified, and the content is always re-encrypted under A256GCM.

Convert response members (compact operation)

Member Meaning
joseToken The JOSE result as a string: the Compact serialization (RFC 7515 §7.1 / RFC 7516 §7.1), or the General JSON serialization document (RFC 7515 §7.2.1 / RFC 7516 §7.2.1) carrying one signature per signer and one recipient per recipient.
serialization COMPACT or JSON — what was produced.
structure JWS, JWE or JWE_JWS_NESTED.
decision signerCount, recipientCount, chosenFormat, chosenSerialization, reason.
signers[], recipients[] One entry per actor, mapping its originalIssuerDN and originalSerialNumber to the assignedKid of the tenant key used and the JOSE algorithm emitted for it — the alg in the token, read back from it (PS256 / ES* for a signer, RSA-OAEP-512 for an RSA recipient). A signer entry also carries certificateMatched; every entry carries its own warnings array.
algorithmTransformations[] One entry per signer, plus one for the content cipher of an encrypted structure: original (the PKCS#7 algorithm and its OID), transformed (the emitted alg, or A256GCM for the content) and reason.

The streaming operation returns the JOSE output itself — no metadata — as described in PKCS#7 Conversion.

Analyze response: recommendations

migrationPath (CONVERT_TO_{JWS,JWE,JWE_JWS}_{COMPACT,JSON}), requiredKeys[] (one entry per actor, purpose signing or encryption, named by issuerDN and serialNumber), decryptionRequirement (ONE_RECIPIENT_PRIVATE_KEY for an encrypted structure) and estimatedComplexity. See Analyze Operations.

Analyze request and response

The compact analyze request carries pkcs7Data only; the streaming request's metadata part is an empty JSON object, {}, still required and first. Analysis takes no key and no option, and a member a request does not define is refused 400 invalid-input naming it. detectedFormat.structure is SignedData, EnvelopedData or SignedAndEnvelopedData. limits.maxActors is the actor bound above — the value the conversion enforces — so a client reads it from the analysis instead of hard-coding 16.


POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore

Purpose

Import cryptographic private keys and certificate chains from PKCS#12 (.p12 or .pfx) or JKS (.jks) keystores.

Endpoint Details

POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore
Content-Type: multipart/form-data
Authorization: Bearer {jwt-token}
Scope: SCOPE_migration.importKeystore

The request carries a metadata JSON part, a keystore file part and an optional password part. The parameters below are fields of the metadata part, except password, which is its own part.

Request Parameters

Parameter Type Required Description
kid String Yes Base identifier for imported key(s)
password String No Password protecting keystore file (null if unprotected)
keystore File part Yes The keystore file itself (PKCS#12 or JKS, max 20 MB) — a file part, not a metadata field
validationMode Enum No Certificate validation mode: STRICT, IMPORT_ONLY, SKIP (default: from config)
kidStrategy Enum No Kid generation strategy: AUTO, MANUAL (default: AUTO)
kidMappings Map Conditional Required if kidStrategy=MANUAL. Maps keystore alias to kid
purpose Enum No Keystore-level default key purpose: AUTO, ENCRYPT_DECRYPT, SIGN_VERIFY, DUAL (default: AUTO)
purposeMappings Map No Per-alias purpose override ({alias: AUTO\|ENCRYPT_DECRYPT\|SIGN_VERIFY\|DUAL}); precedence over purpose

KeystoreImportMetadata precedence for an alias's purpose: per-alias purposeMappings → keystore-level purpose → AUTO (derived from the certificate KeyUsage). AUTO that remains ambiguous fails closed with HTTP 422 purpose-required. DUAL projects one private keypair onto two single-purpose kids (<kid>-enc, <kid>-sig).

Request Example

curl -sS -X POST \
  "$ANKA_BASE_URL/api/v3/admin/tenants/$TENANT_ID/keys/import-keystore" \
  -H "Authorization: Bearer $TOKEN" \
  -F 'metadata={"kid":"enterprise-signing-2024","validationMode":"STRICT","kidStrategy":"AUTO","kidMappings":null,"purpose":"AUTO","purposeMappings":{"legacy-rsa":"DUAL"}};type=application/json' \
  -F "[email protected]" \
  -F "password=$KEYSTORE_PASSWORD"

Response - Success (201 Created)

{
  "kid": "enterprise-signing-2024",
  "kty": "RSA",
  "alg": "RSA-4096",
  "publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...",
  "keyOps": ["sign", "verify"],
  "exportable": true,
  "createdAt": "2025-12-19T10:30:00Z",
  "expiresAt": "2026-12-31T23:59:59Z",
  "softLimitExpiration": "2026-11-30T23:59:59Z",
  "usageCount": 0,
  "status": "active",
  "origin": "IMPORTED",
  "restricted": false,
  "hash": "D4E5F6789012345678901234567890ABCDEF0123"
}

Response - Error

400 Bad Request:

{
  "type": "/errors/invalid-input",
  "title": "Invalid Input",
  "status": 400,
  "detail": "Invalid Base64 encoding in keystoreContent"
}

413 Payload Too Large:

{
  "type": "/errors/payload-too-large",
  "title": "Payload Too Large",
  "status": 413,
  "detail": "Keystore file size (25165824 bytes) exceeds the maximum allowed size (20971520 bytes)."
}

422 Unprocessable Entity:

{
  "type": "/errors/certificate-validation",
  "title": "Certificate Validation Failed",
  "status": 422,
  "detail": "Certificate expired on 2023-12-31T23:59:59Z",
  "errors": [
    {
      "type": "EXPIRED",
      "message": "Certificate expired on 2023-12-31T23:59:59Z",
      "severity": "ERROR"
    }
  ]
}

See: Error Reference for what each failure type means and what to change. The step-by-step import procedure is delivered with a licensed deployment.


POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore/analyze (preview)

Purpose

Read-only preview of a keystore import. Parses the uploaded keystore in memory and returns, per alias, the detected algorithm and the KeyUsage-derived purpose (or AMBIGUOUS) without persisting anything — no key is stored and no audit event is written. The console import wizard calls it to render a per-alias purpose preview and prompt only on ambiguous aliases. An ambiguous alias yields detectedPurpose: AMBIGUOUS and HTTP 200 (never 422 — the 422 belongs to the committing import path).

Scope

SCOPE_migration.importKeystore — the same scope as the committing import. Previewing a keystore is not a separate privilege: anything you may import, you may look at first.

Request

multipart/form-data, tenant in the path, with three parts:

Part Required What it is
metadata yes the same KeystoreImportMetadata the committing import takes
keystore yes the binary .p12 / .jks file
password no the keystore password, when the file is protected

Response - Success (200 OK)

{
  "aliases": [
    { "alias": "data-encryption", "kid": "acme-enc", "algorithm": "RSA-2048", "detectedPurpose": "ENCRYPT_DECRYPT", "keyUsage": ["encrypt", "decrypt"], "privateKeyPresent": true },
    { "alias": "legal-signing", "kid": "acme-sig", "algorithm": "RSA-2048", "detectedPurpose": "SIGN_VERIFY", "keyUsage": ["sign", "verify"], "privateKeyPresent": true },
    { "alias": "legacy-rsa", "kid": "acme-legacy", "algorithm": "RSA-2048", "detectedPurpose": "AMBIGUOUS", "keyUsage": [], "privateKeyPresent": true }
  ]
}

The response wraps the per-alias entries in an aliases array. It contains no key material — only alias, kid, algorithm, the KeyUsage-derived detectedPurpose, the resolved keyUsage operations (empty when AMBIGUOUS), and privateKeyPresent.


POST /api/v3/interoperability/verify

Purpose

Verify a detached JWS signature using a caller-supplied public key without storing it.

Endpoint Details

POST /api/v3/interoperability/verify
Content-Type: multipart/form-data
Authorization: Bearer {jwt-token}
Scope: SCOPE_interoperability.verifySignature

Request Parameters (Multipart)

Part 1: metadata (application/json)

Field Type Required Description
kty String Yes Key type (informative)
alg String Yes The public key's algorithm name (RSA-2048, P-256, ML-DSA-87, …); an EC key on another curve, or an ML-DSA/EdDSA key of another parameter set, is refused with 422. Not the JWS alg, which the protected header carries
publicKey String Yes Base64-encoded public key (DER or PEM)
signatureBase64 String Yes Base64-encoded detached JWS object (JSON)

Part 2: file (application/octet-stream)

  • Binary payload that was originally signed

Request Example

curl -X POST /api/v3/interoperability/verify \
  -H "Authorization: Bearer $TOKEN" \
  -F 'metadata={"kty":"ML-DSA","alg":"ML-DSA-87","publicKey":"...","signatureBase64":"..."}' \
  -F '[email protected]'

Accepted alg Values

The JWS was signed by a third party, so its protected alg is honoured (RFC 7515 §4.1.1): it selects the verification scheme, and it must be a registered JWS algorithm that fits the supplied key.

Supplied key Accepted alg
RSA RS256, RS384, RS512 (RSASSA-PKCS1-v1_5) and PS256, PS384, PS512 (RSASSA-PSS, MGF1 over the same hash, salt of the hash length; RFC 7518 §3.3, §3.5)
EC The one ECDSA alg of its curve, with the fixed-length r‖s signature of RFC 7518 §3.4: ES256 (P-256), ES384 (P-384), ES512 (P-521), and ESB256, ESB384, ESB512 on brainpoolP256r1, brainpoolP384r1 and brainpoolP512r1 (RFC 9864)
Ed25519 or Ed448 EdDSA (RFC 8037) or the curve's fully-specified name, Ed25519 or Ed448 (RFC 9864)
ML-DSA and the other post-quantum signing keys The parameter-set name, e.g. ML-DSA-65

Refused with 422 application/problem+json before any verification: none, an absent alg, an HMAC alg such as HS256, a composite alg (a composite signature is not a single-key signature), an alg that does not fit the key (such as ES256 on a P-384 key), and an ECDSA signature that is not its curve's fixed length. The detail names neither the alg nor the key.

Response - Success (200 OK)

{
  "valid": true,
  "signatureResults": []
}

Response - Verification Failed (200 OK)

A signature that does not verify is still HTTP 200, with valid: false.

{
  "valid": false,
  "signatureResults": []
}

See: Utility Operations for detailed documentation


POST /api/v3/interoperability/encrypt

Purpose

Encrypt plaintext using a caller-supplied public key without storing it.

Endpoint Details

POST /api/v3/interoperability/encrypt
Content-Type: multipart/form-data
Authorization: Bearer {jwt-token}
Scope: SCOPE_interoperability.encrypt

Request Parameters (Multipart)

Part 1: metadata (application/json)

Field Type Required Description
kty String Yes Key type (ML-KEM, RSA, etc.)
alg String Yes Key algorithm name (ML-KEM-768, RSA-2048, etc.)
publicKey String Yes Base64-encoded public key (DER or PEM)

Part 2: file (application/octet-stream)

  • Binary plaintext to encrypt

Request Example

curl -X POST /api/v3/interoperability/encrypt \
  -H "Authorization: Bearer $TOKEN" \
  -F 'metadata={"kty":"ML-KEM","alg":"ML-KEM-768","publicKey":"..."}' \
  -F '[email protected]' \
  --output encrypted-bundle.mime

Response - Success (200 OK)

Content-Type: multipart/mixed; boundary=----ankatech-{uuid}

Part 1: Detached JWE header (application/jose+json)
Part 2: Ciphertext (application/octet-stream)

See: Utility Operations for detailed documentation


GET /api/v3/admin/tenants/{tenantId}/keys/{kid}/export

Purpose

Retrieve the public material and operational metadata of a key, for external system integration or to compare a tenant key with a certificate held elsewhere. Export is an Admin API operation.

Endpoint Details

GET /api/v3/admin/tenants/{tenantId}/keys/{kid}/export
Authorization: Bearer {jwt-token}
Scope: SCOPE_admin.keys.read

Path Parameters

Parameter Type Required Description
tenantId UUID Yes The caller's tenant
kid String Yes Key identifier

Request Example

curl -X GET "$ANKA_BASE_URL/api/v3/admin/tenants/$TENANT_ID/keys/prod-signing-key/export" \
  -H "Authorization: Bearer $TOKEN"

Response - Success (200 OK)

Public material only: private key material is never returned, in any key shape. For a simple asymmetric key publicKey is the Base64 SubjectPublicKeyInfo; a symmetric key (kty oct) has no publicKey; a composite key returns its component public keys in components[].

{
  "kid": "prod-signing-key",
  "kty": "ML-DSA",
  "alg": "ML-DSA-87",
  "type": "SIMPLE",
  "publicKey": "MIIHsjALBglghkgBZQMEAxMDggehAE...",
  "keyOps": ["sign", "verify"],
  "exportable": true,
  "origin": "GENERATED",
  "restricted": false,
  "status": "active",
  "createdAt": "2025-01-15T10:20:00Z",
  "expiresAt": "2030-01-15T10:20:00Z",
  "usageCount": 1542
}

Errors

  • 400 invalid-input — the key is non-exportable (exportable false); no key material is returned.
  • 404 not-found — the kid does not exist for the tenant. A kid of another tenant, and a path naming another tenant, answer the same 404.

POST /api/v3/admin/tenants/{tenantId}/keys/import-key

Purpose

Import key from JWK (JSON Web Key) or DER format.

Endpoint Details

POST /api/v3/admin/tenants/{tenantId}/keys/import-key
Content-Type: application/json
Authorization: Bearer {jwt-token}
Scope: SCOPE_migration.importKey

Request Parameters

Parameter Type Required Description
kid String Yes Key identifier
kty String Yes Key type (RSA, EC, ML-KEM, ML-DSA, etc.)
alg String Yes Algorithm (RSA-2048, ML-KEM-768, etc.)
publicKey String Yes Base64-encoded public key
privateKey String Conditional Base64-encoded private key (required for asymmetric)
keyOps Array Yes Allowed operations

Request Example

{
  "kid": "imported-ec-key",
  "kty": "EC",
  "alg": "ECDSA-P256",
  "publicKey": "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE...",
  "privateKey": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEH...",
  "keyOps": ["sign", "verify"]
}

Response - Success (201 Created)

{
  "kid": "imported-ec-key",
  "kty": "EC",
  "alg": "ECDSA-P256",
  "keyOps": ["sign", "verify"],
  "status": "active",
  "createdAt": "2025-12-24T10:00:00Z",
  "expiresAt": "2126-12-24T10:00:00Z"
}

Note: This endpoint is simpler than keystore import—no certificate validation, used for JWK/DER imports.


GET /api/v3/key-management/keys

Purpose

Query and filter keys in the tenant keystore with support for multiple filter criteria.

Endpoint Details

GET /api/v3/key-management/keys
Authorization: Bearer {jwt-token}
Scope: SCOPE_key_management.listKeys

Query Parameters

Parameter Type Values Description
origin String GENERATED, IMPORTED Filter by key origin (how key was created)
restricted Boolean true, false Filter by restriction status (expired certificates)
kty String ML-KEM, HQC, FRODO, CMCE, BIKE, SABER, NTRU, NTRUPRIME, SMAUG-T, ML-DSA, SLH-DSA, FALCON, XMSS, LMS, EC, RSA, oct, OKP, SM2, GOST-EC, COMPOSITE Filter by key type — the same token the response kty field carries. Every hybrid pairing publishes the coarse COMPOSITE; the construction is on the response's compositeMode member, not on kty. Any other value is rejected with 400
status String active, pending_rotation, rotated, expired, revoked, archived, deleted_logical, suspended, pending_destruction Filter by key status — the same token the response status field carries

Request Examples

Get all imported keys:

GET /api/v3/key-management/keys?origin=IMPORTED

Get restricted keys (expired certificates):

GET /api/v3/key-management/keys?origin=IMPORTED&restricted=true

Get valid imported keys:

GET /api/v3/key-management/keys?origin=IMPORTED&restricted=false

Get all RSA keys:

GET /api/v3/key-management/keys?kty=RSA

Get every hybrid (composite) key:

GET /api/v3/key-management/keys?kty=COMPOSITE

A composite mode name is not a key type — ?kty=COMPOSITE_KEM_COMBINE is rejected with 400. The construction is read from each key's compositeMode field in the response.

Combine filters:

GET /api/v3/key-management/keys?origin=IMPORTED&kty=RSA&status=active

Response - Success (200 OK)

[
  {
    "kid": "imported-signing-2024",
    "kty": "RSA",
    "alg": "RSA-3072",
    "keyOps": ["sign", "verify"],
    "status": "active",
    "origin": "IMPORTED",
    "restricted": false,
    "createdAt": "2025-12-19T10:30:00Z",
    "expiresAt": "2026-12-31T23:59:59Z"
  },
  {
    "kid": "recovery-key",
    "kty": "RSA",
    "alg": "RSA-2048",
    "keyOps": ["encrypt", "decrypt"],
    "status": "expired",
    "origin": "IMPORTED",
    "restricted": true,
    "createdAt": "2025-12-15T08:00:00Z",
    "expiresAt": "2023-12-31T23:59:59Z"
  },
  {
    "kid": "hybrid-transport-2026",
    "kty": "COMPOSITE",
    "compositeMode": "COMPOSITE_KEM_COMBINE",
    "alg": "X25519+ML-KEM-768",
    "keyOps": ["encrypt", "decrypt"],
    "status": "active",
    "origin": "GENERATED",
    "restricted": false,
    "createdAt": "2026-01-08T09:15:00Z",
    "expiresAt": "2028-01-08T09:15:00Z"
  }
]

Field Definitions

origin:

  • GENERATED: Key created natively by ANKASecure
  • IMPORTED: Key imported from external keystore (PKCS#12/JKS)

restricted:

  • false: Key can perform all operations defined in keyOps
  • true: Key limited to decrypt and verify only (imported with expired certificate)

Restricted Key Behavior:

When restricted: true, attempts to use the key for operations other than decrypt/verify will fail:

{
  "type": "/errors/operation-not-allowed",
  "status": 400,
  "detail": "Operation 'encrypt' is not allowed for restricted key 'recovery-key'. Restricted keys (imported with expired certificates) are limited to decrypt and verify operations only."
}

Security Scopes Matrix

Scope Definitions

Scope Permission Typical Role Risk Level
SCOPE_migration.importKeystore Import PKCS#12/JKS keystores Migration Admin High (private keys)
SCOPE_interoperability.verifySignature Verify with external keys Application User Low (read-only)
SCOPE_interoperability.encrypt Encrypt with external keys Application User Low (encryption only)
SCOPE_admin.keys.read Export public keys (GET /api/v3/admin/tenants/{tenantId}/keys/{kid}/export) and read key metadata Security Admin Medium (public keys only)
SCOPE_migration.importKey Import a JWK/DER public key (POST /api/v3/admin/tenants/{tenantId}/keys/import-key) Migration Admin Medium (public key material only)
SCOPE_migration.analyzePkcs7 Analyze PKCS#7/CMS structure Application User Low (no key used)
SCOPE_migration.convertPkcs7 Convert PKCS#7/CMS to JOSE. The scope admits the endpoint only: the actor also needs REENCRYPT (decrypting recipient → every recipient) and RESIGN (signer → itself) Capability Grants on the file's keys Migration Admin High (decrypts and signs with tenant private keys)

Scope Assignment Recommendations

Platform Administrator (highest privilege):

  • ✅ All scopes (including importKeystore, importKey)
  • Use for: Initial migration, tenant provisioning

Tenant Administrator:

  • ✅ importKeystore, exportKey, importKey (tenant-scoped)
  • ✅ Migration utility scopes
  • Use for: Tenant-level migrations

Application User:

  • ✅ Utility scopes only (verifySignatureUtilityStream, encryptUtilityStream)
  • ❌ No import or export scopes
  • Use for: Runtime interoperability operations

Size Limits & Constraints

Request Size Limits

Resource Limit Operator-configurable
Keystore file (PKCS#12/JKS) 20 MiB (default), settable from 1 MiB to 100 MiB (over it: 413) ✅ Yes (platform setting)
Generic key import (JWK/DER) 5 MB of decoded key material (default), settable from 1 KiB to 100 MB; the JSON body is admitted up to 4 × ⌈limit ÷ 3⌉ + 65,536 bytes ✅ Yes (platform setting)
Any other JSON request body Admin API 1 MiB, Core API 2 MiB (over it: 413) ❌ No
Streaming payload No API limit N/A
Keystore metadata part 64 KiB (over it: 400) ❌ No
Keystore password part 4 KiB (over it: 400) ❌ No

"No API limit" is the API's own bound; a proxy in front of the deployment may cap uploads; see Behind a buffering proxy.

The key-import bound

The generic-key-import bound is 5,000,000 bytes of decoded key material by default, and the operator can set it from 1,024 to 100,000,000 bytes. A change applies to the next call, without a restart. The same bound applies to the decoded Base64 blob handed to the compact analyze-pkcs7 and convert-pkcs7-to-jose endpoints.

It applies to key material: an oversize key-material import receives 413 payload-too-large.

It also sets the JSON body ceiling of the import and of the two compact PKCS#7 operations: one Base64 encoding of the limit plus a 64 KiB allowance, so a body over it is refused 413 before it is read.

Increasing Keystore Size Limit

The maximum keystore size is a platform setting. The operator raises or lowers it in the Admin Console under Platform Settings → Cryptography & Trust → Migration & Trust, for example to 52428800 for 50 MB. The change applies to the next import, without a restart.

Accepted range: 1048576–104857600 bytes. A value outside it is refused on write.

Certificate Chain Limits

Constraint Limit Reasoning
Chain depth 10 certificates Prevents malicious deep chains
Certificate size 10 KB per certificate Standard X.509 size
Total chain size Included in keystore limit 20 MB default covers ~2,000 certificates

Error Response Reference

HTTP Status Codes

Status Code Meaning Common Causes
200 OK Operation successful (utility endpoints) Verification completed, encryption completed
201 Created Key imported successfully Keystore or JWK import succeeded
400 Bad Request Invalid input Malformed Base64, wrong password, missing required field
401 Unauthorized Authentication failed Missing JWT, expired token, invalid signature
403 Forbidden Insufficient permissions Missing required scope
404 Not Found Key not found GET /keys/{kid} for non-existent key
413 Payload Too Large File size exceeded Keystore over the maximum keystore size (20 MiB by default)
422 Unprocessable Entity Validation failed Certificate expired (STRICT mode), invalid chain, untrusted CA
500 Internal Server Error Server error Unexpected error

Error Response Format (RFC 7807)

All errors follow Problem Details for HTTP APIs standard:

{
  "type": "/errors/{error-category}",
  "title": "Human-Readable Title",
  "status": 400,
  "detail": "Specific error description",
  "instance": "/api/v3/admin/tenants/{tenantId}/keys/import-keystore",
  "errors": [
    {
      "type": "ERROR_CODE",
      "message": "Detailed error message",
      "severity": "ERROR"
    }
  ]
}

Common Error Types

Error Type HTTP Status Solution
INVALID_INPUT 400 Validate request format, check Base64 encoding
PAYLOAD_TOO_LARGE 413 Ask the operator to raise the maximum keystore size, or split the file
CERTIFICATE_VALIDATION 422 Use IMPORT_ONLY mode or fix certificate
purpose-required 422 Declare the alias's purpose (ENCRYPT_DECRYPT/SIGN_VERIFY/DUAL) in purposeMappings, or re-issue the certificate with a restrictive KeyUsage
dual-not-applicable 422 Do not declare DUAL for a public-only entry (no private key); DUAL needs a private keypair
EXPIRED 422 Use IMPORT_ONLY mode for recovery
CHAIN_INVALID 422 Include the intermediate certificates, or ask the operator to add the root CA to the deployment's trusted CA certificates
NO_TRUST_ANCHOR 422 Ask the operator to add the issuing root CA to the deployment's trusted CA certificates, or use a validation mode that does not require a trust anchor (Validation Mode Comparison)
UNAUTHORIZED 401 Check JWT token validity and scopes
FORBIDDEN 403 Add required scope to user/app
NOT_FOUND 404 Verify kid exists in keystore

Configuration Reference

Platform Settings

The operator sets these in the Admin Console under Platform Settings → Cryptography & Trust → Migration & Trust. A change applies to the next import, without a restart.

Setting Default Accepted values
Maximum keystore file size, in bytes 20971520 (20 MB) 1048576–104857600
Certificate validation mode STRICT STRICT, IMPORT_ONLY, SKIP
Allow a request to override the validation mode false true (dev/staging), false (prod)

Trust Anchors and Revocation

Certificate chains are validated against the trusted CA certificates the operator installs on the deployment.

The import does not check certificate revocation status (CRL or OCSP). Check that a certificate is not revoked before you import it.

Fixed Limits

Limit Value Behaviour
Signers and recipients 16, counted together The upper bound on the signers and recipients that one PKCS#7 conversion, or one JOSE token on the crypto plane, may name. Above it the conversion is refused 422 ACTOR_LIMIT_EXCEEDED, analyze reports the same code as an ERROR reason, and a crypto operation is refused 422 JOSE_ACTOR_LIMIT_EXCEEDED. It is not an operator setting.

Validation Mode Comparison

Mode Production Use Expired Certs Invalid Chains Security Level
STRICT ✅ Recommended ❌ Rejected ❌ Rejected Highest
IMPORT_ONLY ⚠️ Recovery only ✅ Allowed (restricted=true, runtime-limited to decrypt/verify) ⚠️ Allowed (restricted=true, runtime-limited to decrypt/verify) Medium
SKIP ❌ Never ✅ Allowed (restricted=false) ✅ Allowed (restricted=false) ⚠️ Lowest (testing only)

In every mode the stored keyOps are derived from the certificate KeyUsage (single-purpose; the precedence is stated with the request parameters above); the restricted=true runtime gate additionally limits expired/IMPORT_ONLY keys to decrypt/verify.


The step-by-step import guide and the troubleshooting decision matrices are delivered with a licensed deployment rather than published here; ask whoever operates your deployment for either.


Version: 3.0.0
Last Updated: 2025-12-24 Status: Production Ready

* restricted content — see higher-tier documentation