Import Legacy Keys to AnkaSecure
Two operations bring existing key material into a tenant, and they are not interchangeable:
| Operation | What it accepts | What it produces |
|---|---|---|
POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore | a PKCS#12 or JKS keystore file | one or more keys, plus their certificates |
POST /api/v3/admin/tenants/{tenantId}/keys/import-key | a single public key, as JSON | one key |
Both live on the Admin API, because importing key material is a control-plane act. Both are tenant-scoped: the tenant is in the path, and the token must be authorised for it.
Importing a keystore
import-keystore takes multipart/form-data with three parts:
| Part | Required | What it is |
|---|---|---|
metadata | yes | JSON: how to name and validate the imported keys (fields below) |
keystore | yes | the binary .p12 / .jks file |
password | no | the keystore password, when the file is protected |
The metadata part is validated, and kid is required. A keystore holds aliases, not key identifiers, so you supply the base identifier the imported keys hang off:
| Field | Required | Values |
|---|---|---|
kid | yes | the base key identifier; bounded in length and character set |
kidStrategy | no | AUTO (default) derives each alias's kid from kid; MANUAL requires kidMappings |
kidMappings | under MANUAL | {alias: kid} — an explicit kid per alias |
validationMode | no | STRICT, IMPORT_ONLY, SKIP — how far certificate chains are validated |
purpose | no | AUTO, ENCRYPT_DECRYPT, SIGN_VERIFY, DUAL — the default for every alias |
purposeMappings | no | {alias: purpose} — overrides purpose for named aliases |
curl -sS -X POST \
"$ANKA_BASE_URL/api/v3/admin/tenants/$TENANT_ID/keys/import-keystore" \
-H "Authorization: Bearer $TOKEN" \
-F 'metadata={"kid":"migration-2024-prod-key","kidStrategy":"AUTO","validationMode":"STRICT"};type=application/json' \
-F "[email protected]" \
-F "password=$KEYSTORE_PASSWORD"
The response reports what happened to every alias in the file, in four groups:
keys— imported successfullycertificates— certificate material that accompanied themskipped— aliases deliberately not importedfailures— aliases that could not be imported, each with a reason
Read skipped and failures before treating an import as complete. A keystore with ten aliases of which three failed still returns 200; the per-alias outcome is in the body, not in the status code.
Documented failure responses: 400, 401, 403, 422, 502. A 422 means the file was readable but its contents were not admissible — a wrong password produces 400, not 422.
Importing a single public key
import-key takes application/json and imports a public key only. There is no operation that imports a bare private key: private material arrives inside a keystore or not at all.
Required fields are kid, kty and alg:
curl -sS -X POST \
"$ANKA_BASE_URL/api/v3/admin/tenants/$TENANT_ID/keys/import-key" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kid": "partner-verification-2026",
"kty": "RSA",
"alg": "RSA-3072",
"type": "SIMPLE",
"publicKey": "<base64url DER SubjectPublicKeyInfo>",
"keyOps": ["verify"]
}'
Fields worth knowing:
ktyis a coarse key type —RSA,EC,oct,ML-KEM,ML-DSA,SLH-DSA,COMPOSITE. It is not where the construction lives.algcarries the specific algorithm, hyphenated:RSA-3072,ML-KEM-768,ML-DSA-65. Underscored forms are not accepted.typeisSIMPLEorCOMPOSITE. A composite import additionally suppliescomponents,kdfandverificationPolicy(ALL,ANY,CLASSICAL_REQUIRED,PQC_REQUIRED).keyOpsconstrains what the key may be used for.- Usage bounds —
expiresAt,maxUsageLimit,softUsageLimit,softLimitExpiration— may be set at import time. On the two usage fields0andnullboth mean UNLIMITED, and an omittedmaxUsageLimitis stored as0: an imported key has no operation cap unless one is asked for. A positivesoftUsageLimitrequires a finitemaxUsageLimit— a soft threshold on an unlimited key is rejected.
Documented failure responses: 400, 401, 403, 409, 502. A 409 is narrower than "the kid is taken": it means a key with that kid exists with mismatched public material. Re-importing the same public key under the same kid is not a conflict.
Before you import: look first
import-keystore/analyze reads a keystore and reports its contents without importing anything. On an unfamiliar file — an old backup, something handed over by a departing vendor — analyse before you import. See Analyze Operations.
After importing
The imported keys appear in the ordinary key listing:
From that point they are ordinary keys: usable by kid from the data plane, and subject to the same policy as any other key in the tenant.
Next
- Analyze Operations — inspecting a keystore before importing it
- Convert Operations — moving data onto post-quantum keys