KMS Basics
8 examples to get you started with AWS KMS via the SDK - 5 basic and 3 intermediate.
Search across all documentation pages
8 examples to get you started with AWS KMS via the SDK - 5 basic and 3 intermediate.
pip install boto3 (1.43.x, Python 3.10+) or Node.js npm install @aws-sdk/client-kms (AWS SDK v3, Node 18+).~/.aws/credentials, or an attached role).kms:CreateKey, kms:Encrypt, kms:Decrypt, and kms:CreateAlias permissions, or an admin who can grant them.Start with the default key type - it covers almost all encrypt/decrypt use cases.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
key = kms.create_key(
Description="app-config-key",
KeySpec="SYMMETRIC_DEFAULT",
KeyUsage="ENCRYPT_DECRYPT",
)
key_id = key["KeyMetadata"]["KeyId"]
print(key_id)// --- TypeScript (AWS SDK v3) ---
import { KMSClient, CreateKeyCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
const key = await kms.send(new CreateKeyCommand({
Description: "app-config-key",
KeySpec: "SYMMETRIC_DEFAULT",
KeyUsage: "ENCRYPT_DECRYPT",
}));
const keyId = key.KeyMetadata?.KeyId;
console.log(keyId);SYMMETRIC_DEFAULT is AES-256-GCM, the right choice unless you specifically need asymmetric signing or encryption.KeyUsage: ENCRYPT_DECRYPT is the default and cannot be changed after creation.KeyId is a UUID; you will almost always want an alias on top of it (example 2).Related: KMS's Envelope-Encryption Model - what this key actually does under the hood.
Aliases decouple your code from raw key IDs.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
kms.create_alias(AliasName="alias/app-config-key", TargetKeyId="1234abcd-12ab-34cd-56ef-1234567890ab")// --- TypeScript (AWS SDK v3) ---
import { KMSClient, CreateAliasCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
await kms.send(new CreateAliasCommand({
AliasName: "alias/app-config-key",
TargetKeyId: "1234abcd-12ab-34cd-56ef-1234567890ab",
}));alias/ and are unique per account per region.alias/app-config-key anywhere a KeyId parameter is accepted - Encrypt, Decrypt, GenerateDataKey, all of it.UpdateAlias) lets you change which physical key your code uses without a deploy.For values under the size limit, skip the data-key dance entirely.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
resp = kms.encrypt(KeyId="alias/app-config-key", Plaintext=b"db-password-123")
ciphertext = resp["CiphertextBlob"]// --- TypeScript (AWS SDK v3) ---
import { KMSClient, EncryptCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
const resp = await kms.send(new EncryptCommand({
KeyId: "alias/app-config-key",
Plaintext: new TextEncoder().encode("db-password-123"),
}));
const ciphertext = resp.CiphertextBlob;Plaintext must be bytes, not a string - encode it first in both SDKs.Encrypt is capped near 4KB for symmetric keys; use GenerateDataKey for anything larger.CiphertextBlob is opaque binary - store it as-is (e.g., base64 in a text column).Decrypt only needs the blob - not the key ID.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
resp = kms.decrypt(CiphertextBlob=ciphertext)
plaintext = resp["Plaintext"].decode("utf-8")
print(plaintext)// --- TypeScript (AWS SDK v3) ---
import { KMSClient, DecryptCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
const resp = await kms.send(new DecryptCommand({ CiphertextBlob: ciphertext }));
const plaintext = new TextDecoder().decode(resp.Plaintext);
console.log(plaintext);Decrypt resolves that automatically for single-region keys.KeyId to Decrypt is optional but recommended as a defense-in-depth check that the ciphertext came from the key you expect.AccessDeniedException or InvalidCiphertextException, not a silent wrong answer.Read key state before you build automation around it.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
desc = kms.describe_key(KeyId="alias/app-config-key")["KeyMetadata"]
print(desc["KeyState"], desc["KeySpec"], desc["KeyUsage"])// --- TypeScript (AWS SDK v3) ---
import { KMSClient, DescribeKeyCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
const desc = (await kms.send(new DescribeKeyCommand({ KeyId: "alias/app-config-key" }))).KeyMetadata;
console.log(desc?.KeyState, desc?.KeySpec, desc?.KeyUsage);KeyState is typically Enabled; it can also be Disabled, PendingDeletion, or PendingImport.PendingDeletion still exists but rejects Encrypt/Decrypt - check this before an outage surprises you.DescribeKey needs only kms:DescribeKey and works with either a key ID or an alias.Symmetric CMKs can rotate their backing key material once a year with no code changes.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
kms.enable_key_rotation(KeyId="alias/app-config-key")
status = kms.get_key_rotation_status(KeyId="alias/app-config-key")
print(status["KeyRotationEnabled"])// --- TypeScript (AWS SDK v3) ---
import { KMSClient, EnableKeyRotationCommand, GetKeyRotationStatusCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
await kms.send(new EnableKeyRotationCommand({ KeyId: "alias/app-config-key" }));
const status = await kms.send(new GetKeyRotationStatusCommand({ KeyId: "alias/app-config-key" }));
console.log(status.KeyRotationEnabled);Origin: AWS_KMS.KeyId never change across a rotation - callers notice nothing.Related: Automatic Key Rotation & Multi-Region Keys - rotation schedules in depth.
Grants let a specific principal use the key for specific operations, revocable independently.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
grant = kms.create_grant(
KeyId="alias/app-config-key",
GranteePrincipal="arn:aws:iam::111122223333:role/report-worker-role",
Operations=["Decrypt"],
)
print(grant["GrantId"])// --- TypeScript (AWS SDK v3) ---
import { KMSClient, CreateGrantCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
const grant = await kms.send(new CreateGrantCommand({
KeyId: "alias/app-config-key",
GranteePrincipal: "arn:aws:iam::111122223333:role/report-worker-role",
Operations: ["Decrypt"],
}));
console.log(grant.GrantId);Operations is an explicit allow-list - grant only what the principal needs, such as ["Decrypt"].RevokeGrant using the returned GrantId when access is no longer needed.Related: Grants & Key Policies for Delegated Access - grants versus key policies in depth.
Bind a ciphertext to metadata that must match again on decrypt.
# --- Python (boto3) ---
import boto3
kms = boto3.client("kms", region_name="us-east-1")
ctx = {"tenant": "acme-corp"}
resp = kms.encrypt(
KeyId="alias/app-config-key",
Plaintext=b"tenant-secret",
EncryptionContext=ctx,
)
# Decrypt must pass the identical context or the call fails.
kms.decrypt(CiphertextBlob=resp["CiphertextBlob"], EncryptionContext=ctx)// --- TypeScript (AWS SDK v3) ---
import { KMSClient, EncryptCommand, DecryptCommand } from "@aws-sdk/client-kms";
const kms = new KMSClient({ region: "us-east-1" });
const ctx = { tenant: "acme-corp" };
const resp = await kms.send(new EncryptCommand({
KeyId: "alias/app-config-key",
Plaintext: new TextEncoder().encode("tenant-secret"),
EncryptionContext: ctx,
}));
// Decrypt must pass the identical context or the call fails.
await kms.send(new DecryptCommand({ CiphertextBlob: resp.CiphertextBlob, EncryptionContext: ctx }));InvalidCiphertextException.Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 25, 2026