Skip to content
SEC08

SEC08-BP01: Implement secure key management

One-Click Remediation

Deploy CloudFormation stacks to implement this best practice with a single click.

KMS Key with Rotation
Create a KMS customer-managed key with automatic annual rotation
Launch Stack

Stacks deploy to your AWS account. Review parameters before creating. Standard AWS charges apply.

SEC08-BP01: Implement secure key management

Overview

Secure key management is fundamental to protecting data at rest. Encryption keys must be properly generated, stored, rotated, and managed throughout their lifecycle to ensure the confidentiality and integrity of encrypted data. Poor key management can render even the strongest encryption ineffective.

This best practice focuses on implementing comprehensive key management strategies using AWS Key Management Service (KMS) and other AWS services to ensure encryption keys are securely managed, properly rotated, and appropriately controlled throughout their lifecycle.

Implementation Guidance

1. Use AWS Key Management Service (KMS)

Leverage AWS KMS for centralized key management:

  • Customer Managed Keys: Create and manage your own KMS keys for full control
  • AWS Managed Keys: Use service-specific AWS managed keys for simplified management
  • Key Policies: Implement fine-grained access controls for key usage
  • Cross-Account Access: Enable secure key sharing across AWS accounts

2. Implement Key Rotation

Establish automated key rotation policies:

  • Automatic Rotation: Enable automatic annual rotation for customer managed keys
  • Manual Rotation: Implement manual rotation for specific compliance requirements
  • Rotation Monitoring: Track rotation status and compliance
  • Backward Compatibility: Ensure rotated keys maintain access to existing encrypted data

3. Establish Key Lifecycle Management

Manage keys throughout their entire lifecycle:

  • Key Creation: Secure key generation with proper entropy
  • Key Distribution: Secure key distribution and provisioning
  • Key Usage: Monitor and audit key usage patterns
  • Key Archival: Properly archive keys for compliance and recovery
  • Key Destruction: Securely destroy keys when no longer needed

4. Implement Access Controls

Control who can use and manage encryption keys:

  • Principle of Least Privilege: Grant minimum necessary key permissions
  • Role-Based Access: Use IAM roles for key access management
  • Multi-Factor Authentication: Require MFA for sensitive key operations
  • Cross-Service Access: Control key usage across different AWS services

5. Enable Monitoring and Auditing

Implement comprehensive key usage monitoring:

  • CloudTrail Integration: Log all key management operations
  • CloudWatch Metrics: Monitor key usage patterns and anomalies
  • AWS Config: Track key configuration compliance
  • Alerting: Set up alerts for unauthorized key usage attempts

6. Plan for Disaster Recovery

Ensure key availability for disaster recovery scenarios:

  • Multi-Region Keys: Use multi-region keys for global applications
  • Key Backup: Implement secure key backup strategies
  • Recovery Procedures: Establish key recovery procedures
  • Business Continuity: Ensure key availability doesn’t impact business operations

Implementation Examples

Example 1: Comprehensive KMS Key Management System

View code
# kms_key_manager.py
import boto3
import json
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, asdict
from datetime import datetime, timedelta
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@dataclass
class KeyPolicy:
    key_id: str
    policy_document: Dict[str, Any]
    description: str
    compliance_frameworks: List[str]

@dataclass
class KeyMetadata:
    key_id: str
    key_arn: str
    description: str
    key_usage: str
    key_state: str
    creation_date: datetime
    deletion_date: Optional[datetime]
    rotation_enabled: bool
    multi_region: bool
    tags: Dict[str, str]

class KMSKeyManager:
    """
    Comprehensive AWS KMS key management system
    """
    
    def __init__(self, region: str = 'us-east-1'):
        self.region = region
        self.kms_client = boto3.client('kms', region_name=region)
        self.iam_client = boto3.client('iam', region_name=region)
        self.cloudtrail_client = boto3.client('cloudtrail', region_name=region)
        self.cloudwatch = boto3.client('cloudwatch', region_name=region)
        self.dynamodb = boto3.resource('dynamodb', region_name=region)
        
        # Key management tracking table
        self.key_tracking_table = self.dynamodb.Table('kms-key-management-tracking')
        
        # Standard key policies
        self.standard_policies = self._define_standard_key_policies()
    
    def _define_standard_key_policies(self) -> Dict[str, KeyPolicy]:
        """
        Define standard key policies for different use cases
        """
        account_id = self._get_account_id()
        
        return {
            'application_key': KeyPolicy(
                key_id='',
                policy_document={
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "Enable IAM User Permissions",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:root"},
                            "Action": "kms:*",
                            "Resource": "*"
                        },
                        {
                            "Sid": "Allow use of the key for application",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:role/ApplicationRole"},
                            "Action": [
                                "kms:Encrypt",
                                "kms:Decrypt",
                                "kms:ReEncrypt*",
                                "kms:GenerateDataKey*",
                                "kms:DescribeKey"
                            ],
                            "Resource": "*"
                        },
                        {
                            "Sid": "Allow attachment of persistent resources",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:role/ApplicationRole"},
                            "Action": [
                                "kms:CreateGrant",
                                "kms:ListGrants",
                                "kms:RevokeGrant"
                            ],
                            "Resource": "*",
                            "Condition": {
                                "Bool": {"kms:GrantIsForAWSResource": "true"}
                            }
                        }
                    ]
                },
                description='Standard policy for application encryption keys',
                compliance_frameworks=['SOX', 'PCI_DSS']
            ),
            
            'database_key': KeyPolicy(
                key_id='',
                policy_document={
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "Enable IAM User Permissions",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:root"},
                            "Action": "kms:*",
                            "Resource": "*"
                        },
                        {
                            "Sid": "Allow RDS service to use the key",
                            "Effect": "Allow",
                            "Principal": {"Service": "rds.amazonaws.com"},
                            "Action": [
                                "kms:Encrypt",
                                "kms:Decrypt",
                                "kms:ReEncrypt*",
                                "kms:GenerateDataKey*",
                                "kms:DescribeKey",
                                "kms:CreateGrant"
                            ],
                            "Resource": "*"
                        },
                        {
                            "Sid": "Allow database administrators",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:role/DatabaseAdminRole"},
                            "Action": [
                                "kms:Decrypt",
                                "kms:DescribeKey"
                            ],
                            "Resource": "*",
                            "Condition": {
                                "StringEquals": {
                                    "kms:ViaService": f"rds.{self.region}.amazonaws.com"
                                }
                            }
                        }
                    ]
                },
                description='Policy for database encryption keys',
                compliance_frameworks=['HIPAA', 'GDPR']
            ),
            
            'backup_key': KeyPolicy(
                key_id='',
                policy_document={
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "Enable IAM User Permissions",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:root"},
                            "Action": "kms:*",
                            "Resource": "*"
                        },
                        {
                            "Sid": "Allow AWS Backup service",
                            "Effect": "Allow",
                            "Principal": {"Service": "backup.amazonaws.com"},
                            "Action": [
                                "kms:Encrypt",
                                "kms:Decrypt",
                                "kms:ReEncrypt*",
                                "kms:GenerateDataKey*",
                                "kms:DescribeKey",
                                "kms:CreateGrant"
                            ],
                            "Resource": "*"
                        },
                        {
                            "Sid": "Allow backup administrators",
                            "Effect": "Allow",
                            "Principal": {"AWS": f"arn:aws:iam::{account_id}:role/BackupAdminRole"},
                            "Action": [
                                "kms:Decrypt",
                                "kms:DescribeKey",
                                "kms:ListGrants"
                            ],
                            "Resource": "*"
                        }
                    ]
                },
                description='Policy for backup encryption keys',
                compliance_frameworks=['SOX', 'HIPAA']
            )
        }
    
    def create_customer_managed_key(self, 
                                  key_description: str,
                                  key_usage: str = 'ENCRYPT_DECRYPT',
                                  policy_type: str = 'application_key',
                                  enable_rotation: bool = True,
                                  multi_region: bool = False,
                                  tags: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
        """
        Create a customer managed KMS key with specified configuration
        """
        try:
            # Prepare key creation parameters
            create_params = {
                'Description': key_description,
                'KeyUsage': key_usage,
                'KeySpec': 'SYMMETRIC_DEFAULT',
                'Origin': 'AWS_KMS',
                'MultiRegion': multi_region
            }
            
            # Add tags if provided
            if tags:
                tag_list = [{'TagKey': k, 'TagValue': v} for k, v in tags.items()]
                create_params['Tags'] = tag_list
            
            # Create the key
            response = self.kms_client.create_key(**create_params)
            key_id = response['KeyMetadata']['KeyId']
            key_arn = response['KeyMetadata']['Arn']
            
            # Apply key policy
            if policy_type in self.standard_policies:
                policy = self.standard_policies[policy_type]
                policy.key_id = key_id
                
                self.kms_client.put_key_policy(
                    KeyId=key_id,
                    PolicyName='default',
                    Policy=json.dumps(policy.policy_document)
                )
            
            # Enable key rotation if requested
            if enable_rotation:
                self.kms_client.enable_key_rotation(KeyId=key_id)
            
            # Create key alias
            alias_name = f"alias/{key_description.lower().replace(' ', '-')}"
            try:
                self.kms_client.create_alias(
                    AliasName=alias_name,
                    TargetKeyId=key_id
                )
            except self.kms_client.exceptions.AlreadyExistsException:
                # Alias already exists, update it
                self.kms_client.update_alias(
                    AliasName=alias_name,
                    TargetKeyId=key_id
                )
            
            # Track key creation
            self._track_key_operation(key_id, 'CREATE', {
                'description': key_description,
                'policy_type': policy_type,
                'rotation_enabled': enable_rotation,
                'multi_region': multi_region
            })
            
            logger.info(f"Created KMS key: {key_id} with alias: {alias_name}")
            
            return {
                'status': 'success',
                'key_id': key_id,
                'key_arn': key_arn,
                'alias_name': alias_name,
                'rotation_enabled': enable_rotation,
                'multi_region': multi_region
            }
            
        except Exception as e:
            logger.error(f"Error creating KMS key: {str(e)}")
            return {
                'status': 'error',
                'message': str(e)
            }
    
    def rotate_key(self, key_id: str, force_rotation: bool = False) -> Dict[str, Any]:
        """
        Rotate a customer managed key
        """
        try:
            # Check if key supports rotation
            key_metadata = self.kms_client.describe_key(KeyId=key_id)['KeyMetadata']
            
            if key_metadata['KeyManager'] != 'CUSTOMER':
                return {
                    'status': 'error',
                    'message': 'Only customer managed keys can be manually rotated'
                }
            
            # Check current rotation status
            rotation_status = self.kms_client.get_key_rotation_status(KeyId=key_id)
            
            if not rotation_status['KeyRotationEnabled'] and not force_rotation:
                return {
                    'status': 'error',
                    'message': 'Key rotation is not enabled. Use force_rotation=True to override.'
                }
            
            # Enable rotation if not already enabled
            if not rotation_status['KeyRotationEnabled']:
                self.kms_client.enable_key_rotation(KeyId=key_id)
            
            # For immediate rotation, we need to create a new key version
            # This is done automatically by AWS KMS during the rotation process
            
            # Track rotation operation
            self._track_key_operation(key_id, 'ROTATE', {
                'forced': force_rotation,
                'previous_rotation_enabled': rotation_status['KeyRotationEnabled']
            })
            
            logger.info(f"Key rotation initiated for key: {key_id}")
            
            return {
                'status': 'success',
                'key_id': key_id,
                'rotation_enabled': True,
                'message': 'Key rotation initiated successfully'
            }
            
        except Exception as e:
            logger.error(f"Error rotating key {key_id}: {str(e)}")
            return {
                'status': 'error',
                'key_id': key_id,
                'message': str(e)
            }
    
    def audit_key_usage(self, key_id: str, days: int = 30) -> Dict[str, Any]:
        """
        Audit key usage patterns and compliance
        """
        try:
            # Get key metadata
            key_metadata = self.kms_client.describe_key(KeyId=key_id)['KeyMetadata']
            
            # Get CloudTrail events for key usage
            end_time = datetime.utcnow()
            start_time = end_time - timedelta(days=days)
            
            events = self.cloudtrail_client.lookup_events(
                LookupAttributes=[
                    {
                        'AttributeKey': 'ResourceName',
                        'AttributeValue': key_id
                    }
                ],
                StartTime=start_time,
                EndTime=end_time
            )
            
            # Analyze usage patterns
            usage_analysis = {
                'key_id': key_id,
                'key_arn': key_metadata['Arn'],
                'analysis_period_days': days,
                'total_operations': len(events['Events']),
                'operations_by_type': {},
                'operations_by_user': {},
                'operations_by_service': {},
                'compliance_status': 'compliant',
                'recommendations': []
            }
            
            for event in events['Events']:
                event_name = event['EventName']
                username = event.get('Username', 'Unknown')
                source_ip = event.get('SourceIPAddress', 'Unknown')
                
                # Count operations by type
                usage_analysis['operations_by_type'][event_name] = \
                    usage_analysis['operations_by_type'].get(event_name, 0) + 1
                
                # Count operations by user
                usage_analysis['operations_by_user'][username] = \
                    usage_analysis['operations_by_user'].get(username, 0) + 1
                
                # Identify service usage
                if '.amazonaws.com' in source_ip:
                    service = source_ip.split('.')[0]
                    usage_analysis['operations_by_service'][service] = \
                        usage_analysis['operations_by_service'].get(service, 0) + 1
            
            # Check compliance
            compliance_issues = self._check_key_compliance(key_id, key_metadata, usage_analysis)
            if compliance_issues:
                usage_analysis['compliance_status'] = 'non_compliant'
                usage_analysis['recommendations'].extend(compliance_issues)
            
            return usage_analysis
            
        except Exception as e:
            logger.error(f"Error auditing key usage for {key_id}: {str(e)}")
            return {
                'status': 'error',
                'key_id': key_id,
                'message': str(e)
            }
    
    def _check_key_compliance(self, key_id: str, key_metadata: Dict[str, Any], usage_analysis: Dict[str, Any]) -> List[str]:
        """
        Check key compliance against best practices
        """
        issues = []
        
        # Check if rotation is enabled
        try:
            rotation_status = self.kms_client.get_key_rotation_status(KeyId=key_id)
            if not rotation_status['KeyRotationEnabled'] and key_metadata['KeyManager'] == 'CUSTOMER':
                issues.append('Key rotation is not enabled for customer managed key')
        except:
            pass
        
        # Check key age
        key_age = datetime.utcnow() - key_metadata['CreationDate'].replace(tzinfo=None)
        if key_age.days > 365:
            issues.append(f'Key is {key_age.days} days old - consider rotation')
        
        # Check for excessive permissions
        if usage_analysis['total_operations'] == 0:
            issues.append('Key has not been used in the analysis period - consider deletion')
        
        # Check for unusual access patterns
        decrypt_ops = usage_analysis['operations_by_type'].get('Decrypt', 0)
        encrypt_ops = usage_analysis['operations_by_type'].get('Encrypt', 0)
        
        if decrypt_ops > encrypt_ops * 10:  # More than 10:1 ratio
            issues.append('Unusual access pattern detected - high decrypt to encrypt ratio')
        
        return issues
    
    def _track_key_operation(self, key_id: str, operation: str, metadata: Dict[str, Any]):
        """
        Track key operations in DynamoDB
        """
        try:
            self.key_tracking_table.put_item(
                Item={
                    'key_id': key_id,
                    'timestamp': datetime.utcnow().isoformat(),
                    'operation': operation,
                    'metadata': metadata,
                    'ttl': int((datetime.utcnow() + timedelta(days=365)).timestamp())
                }
            )
        except Exception as e:
            logger.error(f"Error tracking key operation: {str(e)}")
    
    def _get_account_id(self) -> str:
        """Get AWS account ID"""
        return boto3.client('sts').get_caller_identity()['Account']

# Example usage
if __name__ == "__main__":
    # Initialize key manager
    key_manager = KMSKeyManager()
    
    # Create application encryption key
    app_key_result = key_manager.create_customer_managed_key(
        key_description='Application Data Encryption Key',
        policy_type='application_key',
        enable_rotation=True,
        tags={
            'Environment': 'Production',
            'Application': 'WebApp',
            'DataClassification': 'Confidential'
        }
    )
    print(f"Application key creation: {app_key_result}")
    
    # Create database encryption key
    db_key_result = key_manager.create_customer_managed_key(
        key_description='Database Encryption Key',
        policy_type='database_key',
        enable_rotation=True,
        tags={
            'Environment': 'Production',
            'Service': 'RDS',
            'DataClassification': 'Restricted'
        }
    )
    print(f"Database key creation: {db_key_result}")
    
    # Audit key usage
    if app_key_result['status'] == 'success':
        audit_result = key_manager.audit_key_usage(app_key_result['key_id'])
        print(f"Key audit result: {audit_result}")

Example 2: Multi-Region Key Management with Disaster Recovery

View code
# multi_region_key_manager.py
import boto3
import json
from typing import Dict, List, Any
from dataclasses import dataclass
from datetime import datetime
import logging

logger = logging.getLogger(__name__)

@dataclass
class MultiRegionKeyConfig:
    primary_region: str
    replica_regions: List[str]
    key_description: str
    policy_document: Dict[str, Any]
    tags: Dict[str, str]

class MultiRegionKeyManager:
    """
    Manages KMS keys across multiple AWS regions for disaster recovery
    """
    
    def __init__(self, primary_region: str = 'us-east-1'):
        self.primary_region = primary_region
        self.kms_clients = {}
        self.regions = ['us-east-1', 'us-west-2', 'eu-west-1']
        
        # Initialize KMS clients for all regions
        for region in self.regions:
            self.kms_clients[region] = boto3.client('kms', region_name=region)
    
    def create_multi_region_key(self, config: MultiRegionKeyConfig) -> Dict[str, Any]:
        """
        Create a multi-region KMS key with replicas
        """
        try:
            primary_client = self.kms_clients[config.primary_region]
            
            # Create primary multi-region key
            primary_response = primary_client.create_key(
                Description=config.key_description,
                KeyUsage='ENCRYPT_DECRYPT',
                KeySpec='SYMMETRIC_DEFAULT',
                Origin='AWS_KMS',
                MultiRegion=True,
                Tags=[{'TagKey': k, 'TagValue': v} for k, v in config.tags.items()]
            )
            
            primary_key_id = primary_response['KeyMetadata']['KeyId']
            primary_key_arn = primary_response['KeyMetadata']['Arn']
            
            # Apply key policy to primary key
            primary_client.put_key_policy(
                KeyId=primary_key_id,
                PolicyName='default',
                Policy=json.dumps(config.policy_document)
            )
            
            # Enable rotation on primary key
            primary_client.enable_key_rotation(KeyId=primary_key_id)
            
            # Create replica keys in other regions
            replica_keys = {}
            for region in config.replica_regions:
                if region != config.primary_region and region in self.kms_clients:
                    replica_client = self.kms_clients[region]
                    
                    replica_response = replica_client.replicate_key(
                        KeyId=primary_key_id,
                        ReplicaRegion=region,
                        Description=f"{config.key_description} - {region} replica",
                        Tags=[{'TagKey': k, 'TagValue': v} for k, v in config.tags.items()]
                    )
                    
                    replica_key_id = replica_response['ReplicaKeyMetadata']['KeyId']
                    replica_keys[region] = {
                        'key_id': replica_key_id,
                        'key_arn': replica_response['ReplicaKeyMetadata']['Arn']
                    }
                    
                    # Apply same policy to replica
                    replica_client.put_key_policy(
                        KeyId=replica_key_id,
                        PolicyName='default',
                        Policy=json.dumps(config.policy_document)
                    )
            
            result = {
                'status': 'success',
                'primary_key': {
                    'region': config.primary_region,
                    'key_id': primary_key_id,
                    'key_arn': primary_key_arn
                },
                'replica_keys': replica_keys,
                'total_regions': len(replica_keys) + 1
            }
            
            logger.info(f"Created multi-region key with {len(replica_keys)} replicas")
            return result
            
        except Exception as e:
            logger.error(f"Error creating multi-region key: {str(e)}")
            return {
                'status': 'error',
                'message': str(e)
            }
    
    def test_cross_region_encryption(self, primary_key_id: str, test_data: str = "Test encryption data") -> Dict[str, Any]:
        """
        Test encryption/decryption across regions
        """
        results = {
            'test_data': test_data,
            'encryption_tests': {},
            'cross_region_tests': {},
            'overall_status': 'success'
        }
        
        try:
            # Test encryption in each region
            encrypted_data = {}
            
            for region, client in self.kms_clients.items():
                try:
                    # Encrypt data in this region
                    encrypt_response = client.encrypt(
                        KeyId=primary_key_id,
                        Plaintext=test_data.encode()
                    )
                    
                    encrypted_data[region] = encrypt_response['CiphertextBlob']
                    results['encryption_tests'][region] = 'success'
                    
                except Exception as e:
                    results['encryption_tests'][region] = f'failed: {str(e)}'
                    results['overall_status'] = 'partial_failure'
            
            # Test cross-region decryption
            for encrypt_region, ciphertext in encrypted_data.items():
                for decrypt_region, client in self.kms_clients.items():
                    test_key = f"{encrypt_region}_to_{decrypt_region}"
                    
                    try:
                        decrypt_response = client.decrypt(CiphertextBlob=ciphertext)
                        decrypted_text = decrypt_response['Plaintext'].decode()
                        
                        if decrypted_text == test_data:
                            results['cross_region_tests'][test_key] = 'success'
                        else:
                            results['cross_region_tests'][test_key] = 'data_mismatch'
                            results['overall_status'] = 'partial_failure'
                            
                    except Exception as e:
                        results['cross_region_tests'][test_key] = f'failed: {str(e)}'
                        results['overall_status'] = 'partial_failure'
            
            return results
            
        except Exception as e:
            logger.error(f"Error testing cross-region encryption: {str(e)}")
            return {
                'status': 'error',
                'message': str(e)
            }
    
    def monitor_key_health(self, key_id: str) -> Dict[str, Any]:
        """
        Monitor key health across all regions
        """
        health_status = {
            'key_id': key_id,
            'timestamp': datetime.utcnow().isoformat(),
            'region_status': {},
            'overall_health': 'healthy',
            'issues': []
        }
        
        for region, client in self.kms_clients.items():
            try:
                # Check key status
                key_response = client.describe_key(KeyId=key_id)
                key_metadata = key_response['KeyMetadata']
                
                region_health = {
                    'key_state': key_metadata['KeyState'],
                    'enabled': key_metadata['Enabled'],
                    'key_usage': key_metadata['KeyUsage'],
                    'rotation_enabled': False
                }
                
                # Check rotation status
                try:
                    rotation_response = client.get_key_rotation_status(KeyId=key_id)
                    region_health['rotation_enabled'] = rotation_response['KeyRotationEnabled']
                except:
                    pass
                
                # Determine health status
                if key_metadata['KeyState'] != 'Enabled' or not key_metadata['Enabled']:
                    region_health['status'] = 'unhealthy'
                    health_status['overall_health'] = 'degraded'
                    health_status['issues'].append(f"Key disabled or in invalid state in {region}")
                else:
                    region_health['status'] = 'healthy'
                
                health_status['region_status'][region] = region_health
                
            except Exception as e:
                health_status['region_status'][region] = {
                    'status': 'error',
                    'error': str(e)
                }
                health_status['overall_health'] = 'degraded'
                health_status['issues'].append(f"Cannot access key in {region}: {str(e)}")
        
        return health_status

# Example usage
if __name__ == "__main__":
    # Initialize multi-region key manager
    mr_manager = MultiRegionKeyManager()
    
    # Configure multi-region key
    config = MultiRegionKeyConfig(
        primary_region='us-east-1',
        replica_regions=['us-west-2', 'eu-west-1'],
        key_description='Multi-Region Application Key',
        policy_document={
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "Enable IAM User Permissions",
                    "Effect": "Allow",
                    "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
                    "Action": "kms:*",
                    "Resource": "*"
                }
            ]
        },
        tags={
            'Environment': 'Production',
            'Application': 'GlobalApp',
            'DisasterRecovery': 'Enabled'
        }
    )
    
    # Create multi-region key
    result = mr_manager.create_multi_region_key(config)
    print(f"Multi-region key creation: {result}")
    
    # Test cross-region functionality
    if result['status'] == 'success':
        test_result = mr_manager.test_cross_region_encryption(
            result['primary_key']['key_id']
        )
        print(f"Cross-region test: {test_result}")
        
        # Monitor key health
        health_result = mr_manager.monitor_key_health(
            result['primary_key']['key_id']
        )
        print(f"Key health: {health_result}")

Example 3: CloudFormation Template for Secure Key Infrastructure

View code
# secure-key-infrastructure.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Secure KMS key infrastructure with comprehensive management'

Parameters:
  Environment:
    Type: String
    Default: 'prod'
    AllowedValues: ['dev', 'staging', 'prod']
  
  ApplicationName:
    Type: String
    Description: 'Name of the application using these keys'
  
  EnableMultiRegion:
    Type: String
    Default: 'false'
    AllowedValues: ['true', 'false']
    Description: 'Enable multi-region keys'

Conditions:
  IsProduction: !Equals [!Ref Environment, 'prod']
  EnableMR: !Equals [!Ref EnableMultiRegion, 'true']

Resources:
  # Application encryption key
  ApplicationEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: !Sub '${ApplicationName} application encryption key'
      KeyUsage: ENCRYPT_DECRYPT
      KeySpec: SYMMETRIC_DEFAULT
      MultiRegion: !If [EnableMR, true, false]
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'
          - Sid: Allow application role to use key
            Effect: Allow
            Principal:
              AWS: !GetAtt ApplicationRole.Arn
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:DescribeKey'
            Resource: '*'
          - Sid: Allow application role to create grants
            Effect: Allow
            Principal:
              AWS: !GetAtt ApplicationRole.Arn
            Action:
              - 'kms:CreateGrant'
              - 'kms:ListGrants'
              - 'kms:RevokeGrant'
            Resource: '*'
            Condition:
              Bool:
                'kms:GrantIsForAWSResource': 'true'
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName
        - Key: KeyType
          Value: Application
        - Key: RotationEnabled
          Value: 'true'

  ApplicationEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: !Sub 'alias/${ApplicationName}-${Environment}-app-key'
      TargetKeyId: !Ref ApplicationEncryptionKey

  # Database encryption key
  DatabaseEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: !Sub '${ApplicationName} database encryption key'
      KeyUsage: ENCRYPT_DECRYPT
      KeySpec: SYMMETRIC_DEFAULT
      MultiRegion: !If [EnableMR, true, false]
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'
          - Sid: Allow RDS service
            Effect: Allow
            Principal:
              Service: rds.amazonaws.com
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:DescribeKey'
              - 'kms:CreateGrant'
            Resource: '*'
          - Sid: Allow database administrators
            Effect: Allow
            Principal:
              AWS: !GetAtt DatabaseAdminRole.Arn
            Action:
              - 'kms:Decrypt'
              - 'kms:DescribeKey'
            Resource: '*'
            Condition:
              StringEquals:
                'kms:ViaService': !Sub 'rds.${AWS::Region}.amazonaws.com'
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName
        - Key: KeyType
          Value: Database
        - Key: RotationEnabled
          Value: 'true'

  DatabaseEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: !Sub 'alias/${ApplicationName}-${Environment}-db-key'
      TargetKeyId: !Ref DatabaseEncryptionKey

  # Backup encryption key
  BackupEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: !Sub '${ApplicationName} backup encryption key'
      KeyUsage: ENCRYPT_DECRYPT
      KeySpec: SYMMETRIC_DEFAULT
      MultiRegion: !If [EnableMR, true, false]
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'
          - Sid: Allow AWS Backup service
            Effect: Allow
            Principal:
              Service: backup.amazonaws.com
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:DescribeKey'
              - 'kms:CreateGrant'
            Resource: '*'
          - Sid: Allow backup administrators
            Effect: Allow
            Principal:
              AWS: !GetAtt BackupAdminRole.Arn
            Action:
              - 'kms:Decrypt'
              - 'kms:DescribeKey'
              - 'kms:ListGrants'
            Resource: '*'
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName
        - Key: KeyType
          Value: Backup
        - Key: RotationEnabled
          Value: 'true'

  BackupEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: !Sub 'alias/${ApplicationName}-${Environment}-backup-key'
      TargetKeyId: !Ref BackupEncryptionKey

  # IAM Roles
  ApplicationRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ApplicationName}-${Environment}-application-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName

  DatabaseAdminRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ApplicationName}-${Environment}-db-admin-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: sts:AssumeRole
            Condition:
              Bool:
                'aws:MultiFactorAuthPresent': 'true'
      Policies:
        - PolicyName: DatabaseAdminPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'rds:*'
                Resource: '*'
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName

  BackupAdminRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ApplicationName}-${Environment}-backup-admin-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: sts:AssumeRole
            Condition:
              Bool:
                'aws:MultiFactorAuthPresent': 'true'
      Policies:
        - PolicyName: BackupAdminPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'backup:*'
                  - 's3:GetObject'
                  - 's3:ListBucket'
                Resource: '*'
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName

  # CloudWatch Alarms for key monitoring
  KeyUsageAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub '${ApplicationName}-${Environment}-key-usage-alarm'
      AlarmDescription: 'Monitor unusual KMS key usage patterns'
      MetricName: NumberOfRequestsSucceeded
      Namespace: AWS/KMS
      Statistic: Sum
      Period: 300
      EvaluationPeriods: 2
      Threshold: 1000
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: KeyId
          Value: !Ref ApplicationEncryptionKey
      AlarmActions:
        - !Ref SecurityNotificationTopic

  # SNS Topic for security notifications
  SecurityNotificationTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: !Sub '${ApplicationName}-${Environment}-security-notifications'
      KmsMasterKeyId: !Ref ApplicationEncryptionKey
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName

  # Lambda function for key rotation monitoring
  KeyRotationMonitor:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub '${ApplicationName}-${Environment}-key-rotation-monitor'
      Runtime: python3.9
      Handler: index.lambda_handler
      Role: !GetAtt KeyRotationMonitorRole.Arn
      Environment:
        Variables:
          APPLICATION_KEY_ID: !Ref ApplicationEncryptionKey
          DATABASE_KEY_ID: !Ref DatabaseEncryptionKey
          BACKUP_KEY_ID: !Ref BackupEncryptionKey
          SNS_TOPIC_ARN: !Ref SecurityNotificationTopic
      Code:
        ZipFile: |
          import boto3
          import json
          import os
          from datetime import datetime, timedelta
          
          def lambda_handler(event, context):
              kms = boto3.client('kms')
              sns = boto3.client('sns')
              
              key_ids = [
                  os.environ['APPLICATION_KEY_ID'],
                  os.environ['DATABASE_KEY_ID'],
                  os.environ['BACKUP_KEY_ID']
              ]
              
              alerts = []
              
              for key_id in key_ids:
                  try:
                      # Check rotation status
                      rotation_response = kms.get_key_rotation_status(KeyId=key_id)
                      
                      if not rotation_response['KeyRotationEnabled']:
                          alerts.append(f"Key rotation disabled for {key_id}")
                      
                      # Check key metadata
                      key_response = kms.describe_key(KeyId=key_id)
                      key_metadata = key_response['KeyMetadata']
                      
                      # Check key age
                      key_age = datetime.utcnow() - key_metadata['CreationDate'].replace(tzinfo=None)
                      if key_age.days > 365:
                          alerts.append(f"Key {key_id} is {key_age.days} days old")
                      
                  except Exception as e:
                      alerts.append(f"Error checking key {key_id}: {str(e)}")
              
              if alerts:
                  message = "KMS Key Rotation Alerts:\n" + "\n".join(alerts)
                  sns.publish(
                      TopicArn=os.environ['SNS_TOPIC_ARN'],
                      Message=message,
                      Subject="KMS Key Rotation Alert"
                  )
              
              return {
                  'statusCode': 200,
                  'body': json.dumps({
                      'alerts_count': len(alerts),
                      'alerts': alerts
                  })
              }
      Tags:
        - Key: Environment
          Value: !Ref Environment
        - Key: Application
          Value: !Ref ApplicationName

  KeyRotationMonitorRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: KeyMonitoringPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - 'kms:DescribeKey'
                  - 'kms:GetKeyRotationStatus'
                  - 'sns:Publish'
                Resource: '*'

  # EventBridge rule for scheduled key monitoring
  KeyMonitoringSchedule:
    Type: AWS::Events::Rule
    Properties:
      Name: !Sub '${ApplicationName}-${Environment}-key-monitoring'
      ScheduleExpression: 'rate(1 day)'
      State: ENABLED
      Targets:
        - Arn: !GetAtt KeyRotationMonitor.Arn
          Id: KeyRotationMonitorTarget

  KeyMonitoringPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref KeyRotationMonitor
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt KeyMonitoringSchedule.Arn

Outputs:
  ApplicationKeyId:
    Description: 'Application encryption key ID'
    Value: !Ref ApplicationEncryptionKey
    Export:
      Name: !Sub '${AWS::StackName}-ApplicationKeyId'
  
  ApplicationKeyArn:
    Description: 'Application encryption key ARN'
    Value: !GetAtt ApplicationEncryptionKey.Arn
    Export:
      Name: !Sub '${AWS::StackName}-ApplicationKeyArn'
  
  DatabaseKeyId:
    Description: 'Database encryption key ID'
    Value: !Ref DatabaseEncryptionKey
    Export:
      Name: !Sub '${AWS::StackName}-DatabaseKeyId'
  
  BackupKeyId:
    Description: 'Backup encryption key ID'
    Value: !Ref BackupEncryptionKey
    Export:
      Name: !Sub '${AWS::StackName}-BackupKeyId'
  
  ApplicationRoleArn:
    Description: 'Application role ARN'
    Value: !GetAtt ApplicationRole.Arn
    Export:
      Name: !Sub '${AWS::StackName}-ApplicationRoleArn'

Relevant AWS Services

Core Key Management Services

  • AWS Key Management Service (KMS): Centralized key management with hardware security modules
  • AWS CloudHSM: Dedicated hardware security modules for high-security requirements
  • AWS Certificate Manager: SSL/TLS certificate management and automatic renewal
  • AWS Secrets Manager: Secure storage and automatic rotation of secrets

Integration Services

  • AWS IAM: Identity and access management for key permissions
  • AWS CloudTrail: Audit logging for all key management operations
  • AWS Config: Configuration compliance monitoring for key policies
  • Amazon CloudWatch: Monitoring and alerting for key usage patterns

Application Integration

  • Amazon S3: Server-side encryption with KMS keys
  • Amazon RDS: Database encryption with customer managed keys
  • Amazon EBS: Volume encryption with KMS integration
  • AWS Lambda: Serverless functions with environment variable encryption

Benefits of Secure Key Management

Security Benefits

  • Centralized Control: Single point of control for all encryption keys
  • Hardware Protection: Keys protected by FIPS 140-2 Level 2 validated HSMs
  • Access Control: Fine-grained permissions for key usage and management
  • Audit Trail: Complete logging of all key operations

Operational Benefits

  • Automated Rotation: Automatic key rotation without application changes
  • Service Integration: Native integration with AWS services
  • Multi-Region Support: Global key availability for distributed applications
  • Disaster Recovery: Built-in redundancy and backup capabilities

Compliance Benefits

  • Regulatory Compliance: Meet requirements for key management standards
  • Data Sovereignty: Control over key location and access
  • Audit Readiness: Comprehensive audit trails for compliance reporting
  • Policy Enforcement: Automated enforcement of key usage policies
View code