Skip to content

Commit

Permalink
chore(.NET): update hierarchy examples
Browse files Browse the repository at this point in the history
  • Loading branch information
josecorella committed Oct 17, 2023
1 parent 6aab3bf commit f270718
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 120 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using Amazon.DynamoDBv2;
using Amazon.KeyManagementService;
using AWS.Cryptography.EncryptionSDK;
Expand Down Expand Up @@ -34,9 +37,13 @@
/// or you can implement an interface that selects
/// the Branch Key based on the Encryption Context.
///
/// This example demonstrates configuring a Hierarchical Keyring
/// with a Branch Key ID Supplier to encrypt and decrypt data for
/// two separate tenants.
/// This example demonstrates how to:
/// - Create a key store
/// - Create a branch key
/// - Version a branch key
/// - Configure a Hierarchical Keyring
/// with a static branch key configuration to ensure we are restricting
/// access to a single tenant.
///
/// This example requires access to the DDB Table where you are storing the Branch Keys.
/// This table must be configured with the following
Expand All @@ -51,158 +58,121 @@
/// </summary>
public class AwsKmsHierarchicalKeyring
{
// THESE ARE PUBLIC RESOURCES DO NOT USE IN A PRODUCTION ENVIRONMENT
private static string branchKeyIdA = "43574aa0-de30-424e-bad4-0b06f6e89478";
private static string branchKeyIdB = "a2f4be37-15ec-489a-bcb5-dcce1f6bfe84";
private static void Run(MemoryStream plaintext)
private void Run(MemoryStream plaintext)
{
var kmsConfig = new KMSConfiguration { KmsKeyArn = ExampleUtils.ExampleUtils.GetBranchKeyArn() };
// Create an AWS KMS Configuration to use with your KeyStore.
// The KMS Configuration MUST have the right access to the resources in the KeyStore.
var keystoreConfig = new KeyStoreConfig
// 1. Configure your KeyStore resource.
// `ddbTableName` is the name you want for the DDB table that
// will back your keystore.
// `kmsKeyArn` is the KMS Key that will protect your branch keys and beacon keys
// when they are stored in your DDB table.
var keyStoreConfig = new KeyStoreConfig
{
// Client MUST have permissions to decrypt kmsConfig.KmsKeyArn
KmsClient = new AmazonKeyManagementServiceClient(),
KmsConfiguration = kmsConfig,
KmsConfiguration = new KMSConfiguration{ KmsKeyArn = ExampleUtils.ExampleUtils.GetBranchKeyArn() },
DdbTableName = ExampleUtils.ExampleUtils.GetKeyStoreName(),
DdbClient = new AmazonDynamoDBClient(),
LogicalKeyStoreName = ExampleUtils.ExampleUtils.GetLogicalKeyStoreName()
};
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
var keystore = new KeyStore(keystoreConfig);
var keyStore = new KeyStore(keyStoreConfig);

// 2. Create the DynamoDb table that will store the branch keys and beacon keys.
// This checks if the correct table already exists at `ddbTableName`
// by using the DescribeTable API. If no table exists,
// it will create one. If a table exists, it will verify
// the table's configuration and will error if the configuration is incorrect.
// It may take a couple minutes for the table to become ACTIVE,
// at which point it is ready to store branch and beacon keys.

// keyStore.CreateKeyStore(new CreateKeyStoreInput());

// We have already created a Table with the specified configuration.
// For testing purposes we will not create a table when we run this example.

// Create a branch key supplier that maps the branch key id to a more readable format
var branchKeySupplier = new ExampleBranchKeySupplier(branchKeyIdA, branchKeyIdB);
// 3. Create a new branch key and beacon key in our KeyStore.
// Both the branch key and the beacon key will share an Id.
// This creation is eventually consistent. See the CreateBranchKey
// Example for how to populate this table.

// Create an AWS Hierarchical Keyring with the branch key id supplier
// var branchKeyId = CreateBranchKey.createBranchKey();

// For testing purposes we will not create a table when we run this example.
// We will use an existing branch key created using the above code to run this
// example.

// THIS IS A PUBLIC RESOURCE DO NOT USE IN A PRODUCTION ENVIRONMENT
var branchKeyId = "43574aa0-de30-424e-bad4-0b06f6e89478";

// 4. Create the Hierarchical Keyring, using the Branch Key ID Supplier above.
// With this configuration, the AWS SDK Client ultimately configured will be capable
// of encrypting or decrypting items for either tenant (assuming correct KMS access).
// If you want to restrict the client to only encrypt or decrypt for a single tenant,
// configure this Hierarchical Keyring using `BranchKeyId = tenant1BranchKeyId` instead
// of `BranchKeyIdSupplier = branchKeySupplier`.
var createKeyringInput = new CreateAwsKmsHierarchicalKeyringInput
{
KeyStore = keystore,
// This branchKeyId you have configured your keyring with MUST be decrypted by the
// KMS config in the keystore and therefore MUST have the right permissions.
BranchKeyIdSupplier = branchKeySupplier,
KeyStore = keyStore,
BranchKeyId = branchKeyId,
// The value provided to `EntryCapacity` dictates how many branch keys will be held locally
Cache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} },
// This dictates how often we call back to KMS to authorize use of the branch keys
TtlSeconds = 600
};
var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
var keyring = materialProviders.CreateAwsKmsHierarchicalKeyring(createKeyringInput);

// The Branch Key Id supplier uses the encryption context to determine which branch key id
// will be used to encrypt data.
var encryptionContextA = new Dictionary<string, string>()

// 5. Create an encryption context
// Most encrypted data should have an associated encryption context
// to protect integrity. This sample uses placeholder values.
// For more information see:
// blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
var encryptionContext = new Dictionary<string, string>()
{
// We will encrypt with TenantKeyA
{"tenant", "TenantA"},
{"encryption", "context"},
{"is not", "secret"},
{"but adds", "useful metadata"},
{"that can help you", "be confident that"},
{"the data you are handling", "is what you think it is"}
};
var encryptionContextB = new Dictionary<string, string>()
{
// We will encrypt with TenantKeyB
{"tenant", "TenantB"},
{"encryption", "context"},
{"is not", "secret"},
{"but adds", "useful metadata"},
{"that can help you", "be confident that"},
{"the data you are handling", "is what you think it is"}
};

var encryptionSDK = new ESDK(new AwsEncryptionSdkConfig());
var encryptionSdk = new ESDK(new AwsEncryptionSdkConfig());

var encryptInputA = new EncryptInput
var encryptInput = new EncryptInput
{
Plaintext = plaintext,
Keyring = keyring,
// We will encrypt with TenantKeyA
EncryptionContext = encryptionContextA
};

var encryptInputB = new EncryptInput{
Plaintext = plaintext,
Keyring = keyring,
// We will encrypt with TenantKeyB
EncryptionContext = encryptionContextB
EncryptionContext = encryptionContext
};

var encryptOutputA = encryptionSDK.Encrypt(encryptInputA);
var encryptOutputB = encryptionSDK.Encrypt(encryptInputB);
// 6. Encrypt the Data
var encryptOutput = encryptionSdk.Encrypt(encryptInput);

// To attest that TenantKeyB cannot decrypt a message written by TenantKeyA
// let's construct more restrictive hierarchical keyrings.

var createHierarchicalKeyringA = new CreateAwsKmsHierarchicalKeyringInput()
{
KeyStore = keystore,
BranchKeyId = branchKeyIdA,
Cache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} },
TtlSeconds = 600
};
var hierarchicalKeyringA = materialProviders.CreateAwsKmsHierarchicalKeyring(createHierarchicalKeyringA);
// Demonstrate that the ciphertext and plaintext are different.
Assert.NotEqual(encryptOutput.Ciphertext.ToArray(), plaintext.ToArray());

var createHierarchicalKeyringB = new CreateAwsKmsHierarchicalKeyringInput()
var decryptInput = new DecryptInput
{
KeyStore = keystore,
BranchKeyId = branchKeyIdB,
Cache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} },
TtlSeconds = 600
Ciphertext = encryptOutput.Ciphertext,
EncryptionContext = encryptionContext,
Keyring = keyring
};
var hierarchicalKeyringB = materialProviders.CreateAwsKmsHierarchicalKeyring(createHierarchicalKeyringB);

var decryptFailed = false;
try
{
// Try to use keyring for Tenant B to decrypt a message encrypted with Tenant A's key
// Expected to fail.
var decryptInput = new DecryptInput
{
Ciphertext = encryptOutputA.Ciphertext,
Keyring = hierarchicalKeyringB,
};
encryptionSDK.Decrypt(decryptInput);
}
catch (AwsCryptographicMaterialProvidersException)
{
decryptFailed = true;
}
Assert.True(decryptFailed);
// 7. Decrypt the Data
var decryptOutput = encryptionSdk.Decrypt(decryptInput);

decryptFailed = false;
try
{
// Try to use keyring for Tenant A to decrypt a message encrypted with Tenant B's key
// Expected to fail.
var decryptInput = new DecryptInput
{
Ciphertext = encryptOutputB.Ciphertext,
Keyring = hierarchicalKeyringA,
};
encryptionSDK.Decrypt(decryptInput);
}
catch (AwsCryptographicMaterialProvidersException)
{
decryptFailed = true;
}
Assert.True(decryptFailed);

// Decrypt your encrypted data using the same keyring you used on encrypt.
encryptionSDK.Decrypt(new DecryptInput {
Ciphertext = encryptOutputA.Ciphertext,
Keyring = keyring }
);
encryptionSDK.Decrypt(new DecryptInput {
Ciphertext = encryptOutputB.Ciphertext,
Keyring = keyring }
);

// Demonstrate that the decrypted ciphertext and plaintext are the same
Assert.Equal(decryptOutput.Plaintext.ToArray(), plaintext.ToArray());

// 8. Version the Branch Key in our KeyStore.
// Only the branch key will be rotated.
// This rotation is eventually consistent. See the VersionBranchKey
// Example for how to version a branch key.

// For testing purposes we will not version this key when we run this example.
// VersionBranchKey.versionBranchKey(branchKeyId);
}

// We test examples to ensure they remain up-to-date.
[Fact]
public void TestAwsKmsHierarchicalKeyringExample()

public void TestAwsKmsHierarchicalKeyring()
{
Run(ExampleUtils.ExampleUtils.GetPlaintextStream());
}
Expand Down
Loading

0 comments on commit f270718

Please sign in to comment.