Key Management
Key Management
Create, manage, and rotate KMS keys in your AWS external key store
Applies to:
AWS KMSExternal Key StoreKey Lifecycle
Overview
Managing keys in an external key store differs from standard KMS keys because each KMS key is associated with an external key in the vault backing your XKS endpoint (HashiCorp Vault, AWS KMS, or DuoKey MPC). You must coordinate operations between AWS KMS and that vault.
Prerequisites
- External key store connected in AWS KMS
- External key created in your key manager first
- Proper IAM and key manager permissions configured
Creating KMS Keys
1
Create External Key in Key Manager
Create a 256-bit AES key in your external key manager.
Create Vault Transit KeyBASH
# Create a new encryption key in Vault Transit engine
vault write transit/keys/my-external-key type=aes256-gcm96
# Verify the key was created
vault read transit/keys/my-external-key
# Configure the key
vault write transit/keys/my-external-key/config \
exportable=false \
allow_plaintext_backup=false2
Create KMS Key in AWS
Create KMS KeyBASH
# Set variables
KEYSTORE_ID="cks-1234567890abcdef0"
EXTERNAL_KEY_ID="my-external-key"
# Create KMS key
aws kms create-key \
--custom-key-store-id $KEYSTORE_ID \
--xks-key-id $EXTERNAL_KEY_ID \
--description "Production encryption key for S3 buckets" \
--tags TagKey=Environment,TagValue=Production \
TagKey=Application,TagValue=DataStorage \
--region us-east-1
# Capture the key ID
KEY_ID=$(aws kms create-key \
--custom-key-store-id $KEYSTORE_ID \
--xks-key-id $EXTERNAL_KEY_ID \
--query 'KeyMetadata.KeyId' \
--output text)
echo "Created KMS Key: $KEY_ID"3
Create Alias
Create Key AliasBASH
# Create alias
aws kms create-alias \
--alias-name alias/production-s3-encryption \
--target-key-id $KEY_ID
# Verify alias
aws kms describe-key --key-id alias/production-s3-encryption4
Set Key Policy
Configure Key PolicyBASH
# Create key policy JSON
cat > key-policy.json <<EOF
{
"Version": "2012-10-17",
"Id": "xks-key-policy",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow S3 to use the key",
"Effect": "Allow",
"Principal": {
"Service": "s3.amazonaws.com"
},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
}
]
}
EOF
# Apply key policy
aws kms put-key-policy \
--key-id $KEY_ID \
--policy-name default \
--policy file://key-policy.jsonKey Lifecycle Management
Enabling and Disabling Keys
| Operation | Command | Impact |
|---|---|---|
| Disable Key | aws kms disable-key --key-id $KEY_ID | All crypto operations fail until re-enabled |
| Enable Key | aws kms enable-key --key-id $KEY_ID | Restores key to operational state |
Enable/Disable KeysBASH
# Disable the key
aws kms disable-key --key-id $KEY_ID
# Verify key state
aws kms describe-key --key-id $KEY_ID \
--query 'KeyMetadata.KeyState' \
--output text
# Output: Disabled
# Enable the key
aws kms enable-key --key-id $KEY_IDScheduling Key Deletion
Warning
Once deleted, all data encrypted with this key becomes unrecoverable!
Schedule Key DeletionBASH
# Schedule key deletion (7-30 days waiting period)
aws kms schedule-key-deletion \
--key-id $KEY_ID \
--pending-window-in-days 30
# Verify deletion is scheduled
aws kms describe-key --key-id $KEY_ID \
--query 'KeyMetadata.{State:KeyState,DeletionDate:DeletionDate}' \
--output json
# Cancel deletion if needed
aws kms cancel-key-deletion --key-id $KEY_IDExternal Key Rotation
Important
Unlike standard KMS keys, external key stores don't support automatic key rotation. You must manually rotate keys in your external key manager.
Rotate the key material in your external key manager while keeping the same key ID:
In-Place RotationBASH
# HashiCorp Vault - rotate key material
vault write -f transit/keys/my-external-key/rotate
# Verify new version
vault read transit/keys/my-external-key| Pros | Cons |
|---|---|
| No changes needed in AWS KMS | Old data may not decrypt without version retention |
| Applications continue without modification | Requires key manager version support |
| Maintains same KMS key ID and alias |
Key Discovery and Inventory
List and Describe KeysBASH
# Get key details
aws kms describe-key --key-id $KEY_ID \
--query 'KeyMetadata.{
KeyId:KeyId,
XksKeyId:XksKeyId,
KeyState:KeyState,
CreationDate:CreationDate,
CustomKeyStoreId:CustomKeyStoreId,
Origin:Origin
}' \
--output json
# List all aliases for a key
aws kms list-aliases \
--key-id $KEY_ID \
--query 'Aliases[*].AliasName' \
--output text
# Get key usage metrics
aws cloudwatch get-metric-statistics \
--namespace AWS/KMS \
--metric-name NumberOfCalls \
--dimensions Name=KeyId,Value=$KEY_ID \
--start-time $(date -u -d '7 days ago' '+%Y-%m-%dT%H:%M:%S') \
--end-time $(date -u '+%Y-%m-%dT%H:%M:%S') \
--period 3600 \
--statistics Sum \
--output tableKey Grants
Manage Key GrantsBASH
# Create grant for specific operations
GRANT_TOKEN=$(aws kms create-grant \
--key-id $KEY_ID \
--grantee-principal arn:aws:iam::123456789012:role/LambdaExecutionRole \
--operations Encrypt Decrypt GenerateDataKey \
--query 'GrantToken' \
--output text)
# Create grant with constraints
aws kms create-grant \
--key-id $KEY_ID \
--grantee-principal arn:aws:iam::123456789012:role/DataProcessingRole \
--operations Decrypt \
--constraints EncryptionContextSubset={Department=Finance} \
--name "Finance-Data-Processing-Grant"
# List grants
aws kms list-grants --key-id $KEY_ID \
--query 'Grants[*].{
GrantId:GrantId,
GranteePrincipal:GranteePrincipal,
Operations:Operations,
Name:Name
}' \
--output table
# Revoke grant
aws kms revoke-grant \
--key-id $KEY_ID \
--grant-id $GRANT_IDTesting Key Operations
Test Encryption/DecryptionBASH
# Create test file
echo "Sensitive data for testing" > test-plaintext.txt
# Encrypt data
aws kms encrypt \
--key-id $KEY_ID \
--plaintext fileb://test-plaintext.txt \
--output text \
--query CiphertextBlob | base64 -d > test-encrypted.bin
echo "Encryption successful"
# Decrypt the data
aws kms decrypt \
--ciphertext-blob fileb://test-encrypted.bin \
--output text \
--query Plaintext | base64 -d > test-decrypted.txt
# Verify decrypted content matches original
diff test-plaintext.txt test-decrypted.txt && echo "Decryption successful"
# Generate data key
aws kms generate-data-key \
--key-id $KEY_ID \
--key-spec AES_256 \
--query '{Plaintext:Plaintext,CiphertextBlob:CiphertextBlob}' \
--output json > data-key.jsonTroubleshooting Key Operations
Best Practices
Key Management Best Practices
Use Descriptive NamesMeaningful descriptions and tags for keys
Least PrivilegeGrant minimum necessary permissions
Regular AuditsAudit key usage and permissions regularly
Document Key PurposeMaintain documentation of what each key protects
Monitor Key UsageSet up CloudWatch alarms for unusual activity
Separate EnvironmentsUse different keys for dev, staging, and production
Tagging Strategy
Apply Consistent TagsBASH
aws kms tag-resource \
--key-id $KEY_ID \
--tags \
TagKey=Environment,TagValue=Production \
TagKey=Application,TagValue=CustomerData \
TagKey=Owner,TagValue=SecurityTeam \
TagKey=CostCenter,TagValue=IT-Security \
TagKey=Compliance,TagValue=PCI-DSS \
TagKey=DataClassification,TagValue=Confidential \
TagKey=BackupRequired,TagValue=Yes \
TagKey=RotationSchedule,TagValue=Quarterly