KMS Setup
Configure AWS Key Management Service (KMS) for field-level encryption with FluentDynamoDB.
Installation
dotnet add package Oproto.FluentDynamoDb.Encryption.Kms
Creating a KMS Key
Create a symmetric encryption key in AWS KMS:
aws kms create-key \
--description "FluentDynamoDB field encryption key" \
--key-usage ENCRYPT_DECRYPT \
--key-spec SYMMETRIC_DEFAULT
Note the KeyId or Arn from the response — you'll use this when configuring the encryptor.
Enable Key Rotation
Enable automatic key rotation for security best practices:
aws kms enable-key-rotation --key-id <your-key-id>
Basic Configuration
Single Key Setup
using Oproto.FluentDynamoDb;
using Oproto.FluentDynamoDb.Encryption.Kms;
// Create key resolver with a single default key
var keyResolver = new DefaultKmsKeyResolver("arn:aws:kms:us-east-1:123456789012:key/my-key-id");
// Create the field encryptor
var encryptor = new AwsEncryptionSdkFieldEncryptor(keyResolver);
// Configure options
var options = new FluentDynamoDbOptions()
.WithEncryption(encryptor);
var table = new CustomersTable(client, "customers", options);
With Caching Enabled
var encryptorOptions = new AwsEncryptionSdkOptions
{
EnableCaching = true
};
var encryptor = new AwsEncryptionSdkFieldEncryptor(keyResolver, encryptorOptions);
var options = new FluentDynamoDbOptions()
.WithEncryption(encryptor);
Multi-Tenant Key Configuration
For multi-tenant applications, use per-tenant KMS keys for isolation:
var keyResolver = new DefaultKmsKeyResolver(
defaultKeyId: "arn:aws:kms:us-east-1:123456789012:key/default-key",
contextKeyMap: new Dictionary<string, string>
{
["tenant-a"] = "arn:aws:kms:us-east-1:123456789012:key/tenant-a-key",
["tenant-b"] = "arn:aws:kms:us-east-1:123456789012:key/tenant-b-key",
["tenant-c"] = "arn:aws:kms:us-east-1:123456789012:key/tenant-c-key"
});
var encryptor = new AwsEncryptionSdkFieldEncryptor(keyResolver);
Key resolution:
WithEncryptionContext("tenant-a")→ uses tenant-a-keyWithEncryptionContext("unknown")→ uses default-key- No context provided → uses default-key
Custom Key Resolver
For dynamic key resolution (database, external service, etc.):
public class DatabaseKmsKeyResolver : IKmsKeyResolver
{
private readonly IKeyRepository _keyRepo;
private readonly string _defaultKey;
public DatabaseKmsKeyResolver(IKeyRepository keyRepo, string defaultKey)
{
_keyRepo = keyRepo;
_defaultKey = defaultKey;
}
public string ResolveKeyId(string? contextId)
{
if (contextId == null)
return _defaultKey;
var keyArn = _keyRepo.GetKmsKeyForTenant(contextId);
return keyArn ?? _defaultKey;
}
}
IAM Permissions
Your application needs the following KMS permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/my-key-id"
}
]
}
With Encryption Context Conditions
For additional security, restrict access based on encryption context:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/my-key-id",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:app": "my-app"
}
}
}
]
}
AwsEncryptionSdkOptions Reference
public class AwsEncryptionSdkOptions
{
/// <summary>
/// Enable keyring caching (default: true).
/// Caches keyring objects to reduce creation overhead.
/// </summary>
public bool EnableCaching { get; set; } = true;
/// <summary>
/// Algorithm suite (default: AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384).
/// Uses key commitment by default for security.
/// </summary>
public string Algorithm { get; set; } =
"AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384";
}
Troubleshooting
"KMS key not found" or "Access denied"
- Verify the KMS key ARN is correct and the key exists
- Check IAM role has
kms:Encrypt,kms:Decrypt, andkms:GenerateDataKeypermissions - Verify encryption context matches key policy conditions
# Test KMS access
aws kms encrypt \
--key-id <your-key-id> \
--plaintext "test" \
--encryption-context field=test,entity=User
"Failed to decrypt: Invalid ciphertext"
- Verify you're using the same KMS key for encryption and decryption
- Check if encryption context matches
- Verify data hasn't been corrupted in DynamoDB
Diagnostic Warning FDDB4001
If you use [Encrypted] without the encryption package installed:
Warning FDDB4001: Property 'SensitiveData' has [Encrypted] attribute but
Oproto.FluentDynamoDb.Encryption.Kms package is not referenced.
Solution: Install the package with dotnet add package Oproto.FluentDynamoDb.Encryption.Kms.
See Also
- Encryption Overview — When and why to use encryption
- Field-Level Encryption — Marking fields for encryption
- Envelope Encryption — How the encryption pattern works
- Scoped Security — STS-based multi-tenant patterns