Skip to main content

Data Encryption and Key Management

In this lesson, we'll explore how to protect your data at rest and in transit using AWS encryption services. You'll learn to implement encryption across various AWS services and manage cryptographic keys securely.

By the end of this lesson, you'll be able to:

  • Understand encryption concepts and AWS Key Management Service (KMS)
  • Implement encryption for S3, EBS, and RDS
  • Manage encryption keys and understand key policies
  • Use envelope encryption for large datasets

Understanding AWS Encryption Services

AWS provides multiple layers of encryption to protect your data:

  • AWS Key Management Service (KMS): Managed service for creating and controlling encryption keys
  • CloudHSM: Dedicated hardware security modules for regulatory compliance
  • Server-side encryption: Automatic encryption by AWS services
  • Client-side encryption: Encryption before sending data to AWS

AWS Key Management Service (KMS)

KMS is the cornerstone of AWS encryption. It provides secure key storage and cryptographic operations.

Creating a Customer Master Key (CMK)

create_kms_key.py
import boto3

def create_kms_key(description):
kms_client = boto3.client('kms')

response = kms_client.create_key(
Description=description,
KeyUsage='ENCRYPT_DECRYPT',
Origin='AWS_KMS'
)

print(f"Created KMS Key: {response['KeyMetadata']['KeyId']}")
return response['KeyMetadata']['KeyId']

# Create a KMS key for application encryption
key_id = create_kms_key("Application Database Encryption Key")
tip

Always use descriptive key names and enable key rotation for production workloads. AWS automatically rotates KMS keys every year when rotation is enabled.

Encrypting and Decrypting Data

encrypt_decrypt.py
import boto3
import base64

def encrypt_data(plaintext, key_id):
kms_client = boto3.client('kms')

response = kms_client.encrypt(
KeyId=key_id,
Plaintext=plaintext.encode('utf-8')
)

return base64.b64encode(response['CiphertextBlob']).decode('utf-8')

def decrypt_data(ciphertext_blob):
kms_client = boto3.client('kms')

response = kms_client.decrypt(
CiphertextBlob=base64.b64decode(ciphertext_blob)
)

return response['Plaintext'].decode('utf-8')

# Example usage
secret_data = "Sensitive application data"
encrypted = encrypt_data(secret_data, key_id)
print(f"Encrypted: {encrypted}")

decrypted = decrypt_data(encrypted)
print(f"Decrypted: {decrypted}")

Implementing Encryption Across AWS Services

S3 Bucket Encryption

s3_encryption.py
import boto3

def create_encrypted_bucket(bucket_name, kms_key_id):
s3_client = boto3.client('s3')

# Create bucket with default encryption
s3_client.create_bucket(Bucket=bucket_name)

s3_client.put_bucket_encryption(
Bucket=bucket_name,
ServerSideEncryptionConfiguration={
'Rules': [
{
'ApplyServerSideEncryptionByDefault': {
'SSEAlgorithm': 'aws:kms',
'KMSMasterKeyID': kms_key_id
}
}
]
}
)

print(f"Created encrypted bucket: {bucket_name}")

# Create an encrypted S3 bucket
create_encrypted_bucket("my-encrypted-data-bucket", key_id)

EBS Volume Encryption

ebs-encrypted.yaml
Resources:
EncryptedEC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0c02fb55956c7d316
InstanceType: t3.micro
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
Encrypted: true
KmsKeyId: alias/aws/ebs
VolumeSize: 8
VolumeType: gp3

RDS Database Encryption

create_encrypted_rds.sql
-- RDS automatically encrypts the database when encryption is enabled
-- This is configured during instance creation

-- Example using AWS CLI:
aws rds create-db-instance \
--db-instance-identifier my-encrypted-db \
--db-instance-class db.t3.micro \
--engine mysql \
--master-username admin \
--master-user-password password123 \
--allocated-storage 20 \
--storage-encrypted \
--kms-key-id alias/aws/rds

Key Policies and Access Control

KMS keys have resource-based policies that control who can use them.

key-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:root"},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow access for Key Administrators",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::123456789012:user/Alice"},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
}
]
}

Envelope Encryption

For large datasets, use envelope encryption where a data key is encrypted by KMS.

envelope_encryption.py
import boto3
from cryptography.fernet import Fernet
import base64

def encrypt_large_data(plaintext, kms_key_id):
kms_client = boto3.client('kms')

# Generate a data key
response = kms_client.generate_data_key(
KeyId=kms_key_id,
KeySpec='AES_256'
)

# Encrypt the data with the data key
fernet = Fernet(base64.b64encode(response['Plaintext']))
encrypted_data = fernet.encrypt(plaintext.encode('utf-8'))

# Return encrypted data and encrypted data key
return {
'encrypted_data': base64.b64encode(encrypted_data).decode('utf-8'),
'encrypted_data_key': base64.b64encode(response['CiphertextBlob']).decode('utf-8')
}

def decrypt_large_data(encrypted_data, encrypted_data_key):
kms_client = boto3.client('kms')

# Decrypt the data key
response = kms_client.decrypt(
CiphertextBlob=base64.b64decode(encrypted_data_key)
)

# Decrypt the data with the decrypted data key
fernet = Fernet(base64.b64encode(response['Plaintext']))
decrypted_data = fernet.decrypt(base64.b64decode(encrypted_data))

return decrypted_data.decode('utf-8')

# Example usage
large_data = "This is a large piece of data that needs efficient encryption..."
encrypted_result = encrypt_large_data(large_data, key_id)
decrypted_result = decrypt_large_data(
encrypted_result['encrypted_data'],
encrypted_result['encrypted_data_key']
)
print(f"Decrypted: {decrypted_result}")
warning

Never store plaintext data keys with your encrypted data. Always encrypt data keys with KMS and store them separately from your encrypted data.

Common Pitfalls

  • Forgetting key policies: KMS keys deny all access by default. Always configure proper key policies
  • Mixing key types: AWS-managed keys vs. customer-managed keys have different capabilities and costs
  • Ignoring key rotation: Manual key rotation requires re-encrypting all data, while automatic rotation doesn't
  • Cost underestimation: KMS API calls incur charges; envelope encryption reduces costs for large datasets
  • Regional limitations: KMS keys are region-specific; plan multi-region deployments accordingly

Summary

Data encryption in AWS is multi-layered and service-specific. KMS provides centralized key management, while individual AWS services offer built-in encryption capabilities. Remember to use envelope encryption for large datasets, configure proper key policies, and understand the cost implications of your encryption strategy.

Quiz

AWS KMS & Encryption Fundamentals

What is the primary advantage of using envelope encryption with KMS?

Question 1/5