Getting Started with AWS Key Management Service (KMS) — Python Edition
We live in a digital world where protecting sensitive information is more critical than ever. One of the most effective ways to safeguard data is encryption. But encryption is only as strong as your key management. That’s where AWS Key Management Service (KMS) shines.
In this hands-on lab, you’ll create and use a customer-managed KMS key, generate data keys, encrypt/decrypt data in Python, and integrate KMS with DynamoDB. You’ll also see how key policy adds a security layer beyond IAM permissions.
What You’ll Learn
- Create a customer-managed KMS key
- Generate data keys with KMS (Python + boto3)
- Encrypt & decrypt data locally with the plaintext data key
- Integrate KMS with DynamoDB (SSE-KMS)
- Use key policy to control access beyond IAM
Getting Started
AWS KMS is a fully managed service to create and manage cryptographic keys for encrypting data inside and outside AWS. It integrates with services like S3, EBS, RDS, and DynamoDB, helping you meet compliance while keeping data secure.
Lab Setup (Pre-built Resources)
This lab includes a pre-created IAM user that can list DynamoDB tables and read from a specific table Books. We’ll use this user later to show how KMS encryption prevents access until the key policy allows it.
KMS Key Basics
A KMS key (a.k.a. CMK) is the root key used to:
- Generate & encrypt data keys
- Encrypt small payloads (≤ 4 KB)
- Integrate with AWS services’ encryption
Types of keys:
- AWS-managed keys: Created/managed by AWS (limited control)
- Customer-managed keys: Created/managed by you (full control, symmetric or asymmetric)
We’ll use a symmetric customer-managed key.
Create a Customer-Managed Key
AWS Console → KMS → Customer managed keys → Create key
- Key type: Symmetric
- Key usage: Encrypt and decrypt
- Key material origin: KMS (recommended)
- Regionality: Single-region
-
Alias:
myKey - Grant admin and usage permissions to your main user (e.g.,
ed-user) Finish the wizard.
Generate a Data Key (Python)
KMS keys don’t encrypt large blobs directly; instead, they generate data keys. You use the plaintext data key locally for encryption, discard it, and store only the encrypted data key (which you can decrypt later via KMS).
Prereqs
pip install boto3 cryptography
aws configure
Fetch your key ARN
Console → KMS → Customer managed keys → myKey → copy ARN.
Generate the key
import boto3, binascii
kms = boto3.client("kms", region_name="us-east-1")
cmk_arn = "<your-kms-key-arn>"
resp = kms.generate_data_key(KeyId=cmk_arn, KeySpec="AES_256")
plaintext_key = resp["Plaintext"]
encrypted_data_key = resp["CiphertextBlob"]
print("Plaintext Data Key (hex):", binascii.hexlify(plaintext_key).decode())
print("Encrypted Data Key (hex):", binascii.hexlify(encrypted_data_key).decode())
Encrypt Data Using the Data Key (Python)
import base64, binascii
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
plaintext_key_hex = "<your-plaintext-data-key-hex>"
key = binascii.unhexlify(plaintext_key_hex)
data = "Hello, this is my sensitive data.".encode()
padder = padding.PKCS7(128).padder()
padded = padder.update(data) + padder.finalize()
cipher = Cipher(algorithms.AES(key), modes.ECB())
encryptor = cipher.encryptor()
ct = encryptor.update(padded) + encryptor.finalize()
encrypted_b64 = base64.b64encode(ct).decode()
print("Encrypted Data (base64):", encrypted_b64)
Decrypt the Data Key (Python)
import boto3, binascii
kms = boto3.client("kms", region_name="us-east-1")
encrypted_data_key_hex = "<your-encrypted-data-key-hex>"
encrypted_data_key = binascii.unhexlify(encrypted_data_key_hex)
resp = kms.decrypt(CiphertextBlob=encrypted_data_key)
recovered_key = resp["Plaintext"]
print("Recovered Plaintext Key (hex):", binascii.hexlify(recovered_key).decode())
Decrypt the Data (Python)
import base64, binascii
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
plaintext_key_hex = "<your-plaintext-data-key-hex>"
key = binascii.unhexlify(plaintext_key_hex)
encrypted_b64 = "<your-encrypted-data-base64>"
ct = base64.b64decode(encrypted_b64)
cipher = Cipher(algorithms.AES(key), modes.ECB())
decryptor = cipher.decryptor()
padded_plain = decryptor.update(ct) + decryptor.finalize()
unpadder = padding.PKCS7(128).unpadder()
plain = unpadder.update(padded_plain) + unpadder.finalize()
print("Decrypted Data:", plain.decode())
Integrate KMS with DynamoDB (SSE-KMS)
Now we’ll integrate KMS with DynamoDB to encrypt table data at rest.
Create an encrypted table
Console → DynamoDB → Create table
-
Table name:
Books -
Partition key:
Author (String) -
Sort key:
Book_Title (String) -
Table settings: Customize → Encryption at rest: Customer managed key → pick
myKeyCreate and wait until status is Active.
Add an item
Explore table items → Create item
-
Author:Seth Godin -
Book_Title:The Dip
Test Access with a Different IAM User
Even with read permissions, the user can’t read the table yet because the table is encrypted with your customer-managed key, and the key policy doesn’t permit this user to use the key.
Modify the Key Policy to Grant Access
Get the user’s ARN
Console → IAM → Users → IAMLabUser → copy ARN.
Edit the KMS key policy
Console → KMS → Customer managed keys → myKey → Switch to policy view → Edit
Add this statement (replace <IAM_ARN>):
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": { "AWS": "<IAM_ARN>" },
"Action": ["kms:Encrypt", "kms:Decrypt"],
"Resource": "*"
}
Save changes.
Verify as IAM user
Sign back in as IAMLabUser and Scan the Books table.
Now it works — because the user is allowed to use the KMS key.
Wrap-Up
You just built a practical, secure workflow with AWS KMS — end to end and fully in Python:
- Created a customer-managed KMS key
- Generated, stored, and recovered data keys
- Encrypted/decrypted data locally with AES-256
- Secured DynamoDB with SSE-KMS
- Used key policy to gate access beyond IAM



Top comments (0)