Skip to main content

Key Management

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=false
2

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-encryption
4

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.json

Key Lifecycle Management​

Enabling and Disabling Keys​

OperationCommandImpact
Disable Keyaws kms disable-key --key-id $KEY_IDAll crypto operations fail until re-enabled
Enable Keyaws kms enable-key --key-id $KEY_IDRestores 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_ID

Scheduling 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_ID

External 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
ProsCons
No changes needed in AWS KMSOld data may not decrypt without version retention
Applications continue without modificationRequires 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 table

Key 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_ID

Testing 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.json

Troubleshooting 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

Next Steps​