KMS supports two families of keys, and picking the wrong one for the job usually shows up as a confusing ValidationException rather than a slow realization. This page lays out the decision plainly: symmetric for encrypting your own data, asymmetric for signing or for encryption that must happen outside AWS.
# Symmetric: the default for encrypting your own application data.kms.create_key(KeySpec="SYMMETRIC_DEFAULT", KeyUsage="ENCRYPT_DECRYPT")# Asymmetric, signing: verify signatures outside AWS with a public key.kms.create_key(KeySpec="RSA_2048", KeyUsage="SIGN_VERIFY")# Asymmetric, encryption: let external parties encrypt with a public key.kms.create_key(KeySpec="RSA_2048", KeyUsage="ENCRYPT_DECRYPT")
When to reach for this:
Deciding a key type before your first CreateKey call.
A vendor or partner needs to encrypt data for you without an AWS account.
You need to sign documents or tokens that a third party verifies independently.
Migrating from a self-managed asymmetric keypair into KMS custody.
# --- Python (boto3) ---import boto3kms = boto3.client("kms", region_name="us-east-1")# Symmetric key for encrypting your own data.sym_key = kms.create_key(KeySpec="SYMMETRIC_DEFAULT", KeyUsage="ENCRYPT_DECRYPT")sym_id = sym_key["KeyMetadata"]["KeyId"]# Asymmetric key for signing, verified by anyone with the public key.sign_key = kms.create_key(KeySpec="ECC_NIST_P256", KeyUsage="SIGN_VERIFY")sign_id = sign_key["KeyMetadata"]["KeyId"]# Export the public half - safe to hand out, unlike a symmetric key.pub = kms.get_public_key(KeyId=sign_id)print(len(pub["PublicKey"]), "bytes of DER-encoded public key")
// --- TypeScript (AWS SDK v3) ---import { KMSClient, CreateKeyCommand, GetPublicKeyCommand } from "@aws-sdk/client-kms";const kms = new KMSClient({ region: "us-east-1" });// Symmetric key for encrypting your own data.const symKey = await kms.send(new CreateKeyCommand({ KeySpec: "SYMMETRIC_DEFAULT", KeyUsage: "ENCRYPT_DECRYPT",}));const symId = symKey.KeyMetadata?.KeyId;// Asymmetric key for signing, verified by anyone with the public key.const signKey = await kms.send(new CreateKeyCommand({ KeySpec: "ECC_NIST_P256", KeyUsage: "SIGN_VERIFY",}));const signId = signKey.KeyMetadata?.KeyId;// Export the public half - safe to hand out, unlike a symmetric key.const pub = await kms.send(new GetPublicKeyCommand({ KeyId: signId }));console.log(pub.PublicKey?.length, "bytes of DER-encoded public key");
What this demonstrates:
KeySpec and KeyUsage are set together at CreateKey time and never change afterward.
GetPublicKey only works on asymmetric keys - there is no equivalent for symmetric keys because there is no public half.
The private half of an asymmetric key stays inside KMS exactly like a symmetric key's material - only the public key ever leaves.
SYMMETRIC_DEFAULT is AES-256-GCM, and it is the right choice for the overwhelming majority of use cases: encrypting database columns, files via envelope encryption, secrets, and backups. Both Encrypt/Decrypt and GenerateDataKey require the same principal to hold key permissions, since there is no way to encrypt with one half and decrypt with another - it is one key, one operation set.
Asymmetric ENCRYPT_DECRYPT keys (RSA specs like RSA_2048, RSA_3072, RSA_4096) split the operation: anyone holding the exported public key can encrypt, using an algorithm like RSAES_OAEP_SHA_256, but only KMS - holding the private key - can decrypt. This matters when an external party (a partner system, a client-side app, a vendor with no AWS credentials) needs to send you encrypted data without ever touching your AWS account.
# A partner encrypts locally with your exported public key (outside KMS, outside boto3).# You decrypt inside KMS, since only KMS holds the private key.kms.decrypt( KeyId="alias/partner-inbound-key", CiphertextBlob=received_ciphertext, EncryptionAlgorithm="RSAES_OAEP_SHA_256",)
SIGN_VERIFY keys (RSA or ECC specs like ECC_NIST_P256) let KMS sign a message digest with the private key via Sign, producing a signature that anyone can verify with the exported public key - either through KMS's Verify operation or entirely offline with a standard crypto library. This is the pattern for code signing, JWT signing, or any scenario where the verifier should not need AWS credentials or network access to KMS at all.
sig = kms.sign( KeyId="alias/doc-signing-key", Message=b"contract-hash-bytes", MessageType="DIGEST", SigningAlgorithm="ECDSA_SHA_256",)["Signature"]
Trying to change KeyUsage after creation - it is immutable. Fix: create a new key with the right KeyUsage and migrate; there is no update path.
Calling GetPublicKey on a symmetric key - fails, since symmetric keys have no public half. Fix: only call it on SIGN_VERIFY or asymmetric ENCRYPT_DECRYPT keys.
Assuming asymmetric decryption is as cheap as symmetric - RSA operations inside KMS are noticeably more expensive in latency than symmetric AES-GCM. Fix: default to symmetric unless the outside-KMS-encryption or signing requirement is real.
Mismatched SigningAlgorithm or EncryptionAlgorithm - each KeySpec supports only specific algorithms. Fix: check DescribeKey's SigningAlgorithms/EncryptionAlgorithms list before calling Sign or Encrypt.
Forgetting MessageType on Sign - passing a raw message when KMS expects a pre-hashed DIGEST (or vice versa) produces a signature that fails verification. Fix: match MessageType to what you actually pass.
What is the default key type if I don't specify one?
CreateKey requires you to choose KeySpec explicitly in current SDK versions, but SYMMETRIC_DEFAULT is the recommended and most common choice for general application data.
Can I change a key from symmetric to asymmetric later?
No. KeySpec and KeyUsage are fixed for the life of the key. You must create a new key and migrate.
Why would I ever need an asymmetric key instead of symmetric?
When encryption or signature verification must happen outside AWS - by a partner, a client app, or offline - using a public key you export. Symmetric keys never expose usable material outside KMS.
Is asymmetric encryption slower than symmetric?
Yes, meaningfully. RSA operations inside KMS take longer than AES-GCM symmetric operations, so use asymmetric only when the outside-KMS requirement is real.
What does GetPublicKey return?
The DER-encoded public key material for an asymmetric CMK, safe to distribute to anyone who needs to encrypt for you or verify your signatures.
Which SigningAlgorithm should I pick?
It depends on the KeySpec. Check DescribeKey's SigningAlgorithms field for the exact list a given key supports, and match MessageType (RAW or DIGEST) to what you pass into Sign.
Can a symmetric key sign messages?
No - Sign/Verify require KeyUsage: SIGN_VERIFY, which only asymmetric keys support. Use an HMAC key if you need message authentication without full asymmetric signing.