Skip to main content

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-key
  • WithEncryptionContext("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"​

  1. Verify the KMS key ARN is correct and the key exists
  2. Check IAM role has kms:Encrypt, kms:Decrypt, and kms:GenerateDataKey permissions
  3. 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"​

  1. Verify you're using the same KMS key for encryption and decryption
  2. Check if encryption context matches
  3. 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​