Skip to main content
KMS holds encryption keys that belong to your account. You create a key, and from then on the platform will encrypt, decrypt, sign and verify with it — but never hand it to you. There is no endpoint that exports the key itself. The service is regionalkms.sa-saopaulo-1.basaltic.sh. A key exists in one region and can only be used from there, so a ciphertext produced in one region cannot be opened in another.

Create a key

Specs, usages, and the one choice you cannot change afterwards.

Envelope encryption

What a data key is, and why you should almost never call encrypt directly.

Sign and verify

Algorithms per spec, and why you must not pre-hash.

Disable and delete

The recovery window, what cancelling gives you back, and what it does not.

Creating a key

The call is synchronous. The response carries the key already in enabled, ready for crypto operations — there is nothing to poll. name is unique per account and lands in the CRN, so it has to be URL-safe: ^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$.

Specs and usages

A key is pinned to one usage at creation: either encrypt_decrypt or sign_verify. The spec decides which usages are even possible. Omit key_usage for aes-256 or ecdsa-p256 and the only usage the spec supports is applied. Omit it for an RSA spec and the request is rejected — RSA can do both, so the service will not guess.
An RSA key pinned to encrypt_decrypt refuses sign and verify, and the other way round, even though the algorithm could do either. The refusal is 400 KMS_INVALID_KEY_USAGE. Pick the usage deliberately: neither the spec nor the usage can be changed later, and PATCH /v1/keys/{key_id} only edits name, description and tags. To change either one, create a new key.

Envelope encryption

POST /v1/keys/{key_id}/encrypt sends your plaintext to KMS and gets ciphertext back. That is fine for something small and rare — a config value, an API token. It is the wrong shape for anything else, because every byte crosses the network twice and every operation costs a round trip. The alternative is a data key: KMS mints a fresh random key, hands you two copies of it, and never stores it.
You encrypt your data locally with plaintext, then throw plaintext away and store ciphertext beside the data it protects. To read the data back, send ciphertext to POST /v1/keys/{key_id}/decrypt and you have the data key again. Your bulk data never leaves your process, one KMS call covers a whole batch, and the KMS key stays a key-encrypting key — the only thing it ever wraps is other keys.
Never persist the plaintext data key. Storing it next to ciphertext defeats the entire arrangement: anyone who reaches your storage then has both the lock and the key, and revoking the KMS key no longer protects anything.
number_of_bytes accepts 16, 32 or 64 and nothing else — 16 for AES-128, 32 for AES-256 (the default), 64 for HMAC-SHA512. Any other value fails.

When direct encrypt runs out

An RSA key cannot encrypt more than a few hundred bytes. RSA-OAEP can only carry a message smaller than the modulus: with SHA-256 that is k - 2·32 - 2 bytes, so 190 bytes for rsa-2048 and 446 bytes for rsa-4096 (RFC 8017 §7.1.1). There is no chunking behind the API. Past that size the operation fails and a data key is the only route.
A symmetric key has no comparable algorithmic ceiling, but the request body still has to fit in one HTTP call and you still pay a round trip per operation. Treat direct encrypt as a convenience for small, infrequent values, and reach for a data key for everything else.

Encryption context

aad is optional additional authenticated data. It is bound into the AES-GCM tag, so a ciphertext will only open if the same context is presented again — useful for pinning a blob to the thing it belongs to, so a stolen ciphertext cannot be replayed against a different record.
The context must match at decrypt exactly, including its absence. Supplying aad to decrypt a ciphertext that was sealed without one is refused rather than ignored — a context that is only sometimes checked is not a check. Encrypting without aad and decrypting with it fails with 400 INVALID_INPUT.
Only a symmetric key can bind a context. Sending aad to encrypt under an RSA key is refused with 400 KMS_INVALID_KEY_SPEC — RSA-OAEP has nowhere to carry one, so accepting it would drop the binding while you went on treating it as an integrity check. The refusal happens at the seal, where you can still pick a different key.

Signing

Pass the raw message, not a digest. The service hashes it with SHA-256 server-side, so a pre-hashed input gets hashed twice and produces a signature nothing will verify.
signing_algorithm is optional; omitted, the default for the spec is used, and the algorithm actually applied comes back in the response. An algorithm that does not match the spec is rejected with 400 KMS_UNSUPPORTED_SIGNING_ALGORITHM rather than being attempted. Signatures come back in the standard encodings, so nothing downstream needs special handling: RSA-PSS uses saltLen = hashLen = 32, and ECDSA returns the ASN.1 DER (r, s) sequence of ANSI X9.62. POST /v1/keys/{key_id}/verify answers {"signature_valid": false} for a signature that simply does not match. That is a 200, not an error — an error means the request or the backend was wrong, not that the signature was.
The API does not expose the public half of an asymmetric key, so verification goes through verify rather than offline against a published key. Plan on a call per check — and note that verify is a separate IAM action, so a party that should only check signatures never needs kms:Sign.

Turning a key off

Disable

POST /v1/keys/{key_id}/disable refuses every crypto operation with 409 KMS_KEY_DISABLED while leaving the material intact. This is the reversible move: stop a suspected-compromised key now, keep the ability to read historical ciphertext after re-enabling.

Schedule deletion

POST /v1/keys/{key_id}/schedule-deletion starts a countdown. pending_window_in_days is 7 to 30, defaulting to 7. The key refuses crypto operations for the whole window, then the material and the record are destroyed.
Deletion destroys the key material. Every ciphertext ever produced under the key — including every data key you wrapped with it — becomes permanently unreadable. The window exists because that is not undoable afterwards, so use it: schedule the deletion, watch for what breaks, and only let it elapse when nothing does.

Cancelling

POST /v1/keys/{key_id}/cancel-deletion exits the window at any point before deletion_scheduled_at.
A cancelled key comes back disabled, not enabled. Nothing starts working again until you explicitly call enable. The window was entered because someone wanted the key gone; recovering it should not silently restore traffic to it.
Two consequences of how quota is accounted are worth knowing before you rely on cancelling:
  • Scheduling a deletion releases the key’s quota immediately, so you can create a replacement inside the same limit without waiting out the window.
  • Cancelling therefore has to take that quota back, and fails with 403 QUOTA_EXCEEDED if your account is now at its limit. If you created a replacement key, free a slot before you cancel.
A key in pending_deletion no longer reserves its name, so a new key can be created with the same one right away. Because a KMS CRN is built from the name (crn:kms:<region>:<account>:key/<name>), the old key and the new key then share a CRN, and an IAM policy naming it matches both. Give the replacement a different name if that distinction matters to your policies.

Controlling who may use a key

Every operation checks a distinct IAM action, and every operation on a specific key is authorized against that key’s CRN with the key’s tags available as condition context. Only the two collection-level operations are not.
kms:Decrypt and kms:GenerateDataKey are separate actions from kms:GetKey precisely so the operations that return usable key material can be granted narrowly. The same pattern appears on certificates, where certificate:GetCertificateMaterial is split out from reading a certificate. A service that only needs to seal data should get kms:Encrypt and kms:GenerateDataKey and nothing else — it can then write, but never read.
A write path that can seal but not open:
Because tags on the key are available as condition context, a fleet can be fenced by label rather than by name:
kms:ListKeys is authorized against the collection, not against individual keys, so restricting it by CRN or tag has no effect. Scope the operations that use a key; listing tells a caller a key exists and nothing more.
See writing policies for the full document format and every condition operator.

Keys other services can use

Some services will encrypt their data under a key of yours instead of a platform-managed one, which puts the kill switch in your hands: disable the key and that service stops being able to read what it stored.

Secrets

Bind a secret to one of your keys at creation and every version is sealed under it.

Telemetry

A log group or trace setting takes a kms_key_crn; each ingest batch gets its own data key wrapped under yours.
Not everything encrypted at rest uses a key of yours. A certificate’s private key, for example, is encrypted under a platform-managed regional key you do not control. Where a service supports your own key, it exposes a field for it — if there is no such field, there is no such binding.

Errors

Next

Secrets

Versioned application secrets, and how to put one behind your own key.

Writing policies

Scoping kms:Decrypt to the keys and tags it should reach.

API reference

Every KMS operation, with request and response schemas.

Authentication

Signing requests to a regional endpoint.