Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions all-examples/net/migration/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Makefile for S3 Encryption Client .NET migration Example

# Default target
.PHONY: all install clean run help

# Default arguments for running the example
# Override these when calling make run
BUCKET_NAME ?= testwiththisbucket
KMS_KEY_ID ?= arn:aws:kms:us-west-2:992382771485:key/ad1d7ff3-79f3-40f4-b31d-7be4d9c8b3ca

all: install

# Install dependencies using .NET modules
install:
@echo "[NET migration] Installing .NET dependencies..."
dotnet restore
@echo "[NET migration] Dependencies installed successfully!"

# Clean .NET artifacts
clean:
@echo "[NET migration] Cleaning .NET artifacts..."
dotnet clean
@echo "[NET migration] Clean completed!"

# Run the example with default arguments
run: install
@echo "[NET migration] Running S3 Encryption Client .NET Migration example..."
@export MigrationExample_S3_BUCKET=$(BUCKET_NAME) && export MigrationExample_KMS_KEY_ARN=$(KMS_KEY_ID) && dotnet test

# Run with custom arguments
# Usage: make run-custom BUCKET_NAME=my-bucket KMS_KEY_ID=my-kms-key
run-custom: install
@export MigrationExample_S3_BUCKET=$(BUCKET_NAME) && export MigrationExample_KMS_KEY_ARN=$(KMS_KEY_ID) && dotnet test

# Show help
help:
@echo "S3 Encryption Client .NET migration Example Makefile"
@echo ""
@echo "Available targets:"
@echo " install - Install .NET dependencies using .NET modules"
@echo " run - Install dependencies and run the example with default parameters"
@echo " run-custom - Install dependencies and run with custom parameters"
@echo " clean - Remove .NET artifacts"
@echo " help - Show this help message"
@echo ""
@echo ""
@echo "Prerequisites:"
@echo " - Supported .NET framework installed on the system. See https://www.nuget.org/packages/Amazon.Extensions.S3.Encryption for supported one."
@echo " - AWS credentials configured (AWS CLI, environment variables, or IAM role)"
@echo " - Valid S3 bucket and KMS key with appropriate permissions"
70 changes: 70 additions & 0 deletions all-examples/net/migration/MigrationStep0.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Amazon.Extensions.S3.Encryption;
using Amazon.Extensions.S3.Encryption.Primitives;
using Amazon.S3.Model;

namespace migration
{
// Migration Step 0: This example demonstrates use of the S3 Encryption Client for .NET v2
// and is the starting state for migrating your data to the v4 client.
//
// This example's purpose is to model behavior of an existing v2 client.
// Subsequent migration steps will demonstrate code changes needed to use the v4 client.
//
// This example configures a v2 client to:
// - Write objects using non-key committing encryption algorithms
// - Read objects encrypted with either key committing algorithms or with non-key committing algorithms
//
// In this configuration, the client can read objects encrypted
// with non-key committing algorithms (written by this v2 client or an in-progress v4 migration),
// as well as objects encrypted by a migrated v4 client
// that is configured to write objects encrypted with key committing algorithms.
// You should ensure you are using the latest version of the v2 client
// that can read objects encrypted with key committing algorithms before proceeding with migration.
public static class MigrationStep0
{
public static async Task MigrationExampleStep0(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut)
{
Console.WriteLine("\n[Migration Step 0] Starting Step 0 Migration step. Inputs received: \n" +
"kms key ID: " + kmsKeyId + "\n" +
"bucket: " + bucket + "\n" +
"put object key: " + putObjectKey + "\n" +
"get object key: " + getObjectKey + "\n" +
"content to put: " + contentToPut + "\n"
);

var encryptionContext = new Dictionary<string, string>();
var encryptionMaterial = new EncryptionMaterialsV2(kmsKeyId, KmsType.KmsContext, encryptionContext);
#pragma warning disable 0618
var configuration = new AmazonS3CryptoConfigurationV2(SecurityProfile.V2);
#pragma warning enable 0618
var encryptionClient = new AmazonS3EncryptionClientV2(configuration, encryptionMaterial);

await encryptionClient.PutObjectAsync(
new PutObjectRequest
{
BucketName = bucket,
Key = putObjectKey,
ContentBody = contentToPut
}
);

var response = await encryptionClient.GetObjectAsync(
new GetObjectRequest
{
BucketName = bucket,
Key = getObjectKey
}
);

using var reader = new StreamReader(response.ResponseStream);
var returnedContent = await reader.ReadToEndAsync();

if (returnedContent != contentToPut)
{
throw new Exception("Content received from getObject call does not match content put to S3.");
}

Console.WriteLine($"[Migration Step 0] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!");
}
}
}
51 changes: 51 additions & 0 deletions all-examples/net/migration/MigrationStep0Test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Xunit;

namespace migration
{
public class MigrationStep0Test
{
[Trait("Category", "Test-Migration-Step0")]
[Fact]
public async Task TestMigrationStep0()
{
const string content = "test-content";
var kmsKeyId = TestUtils.TEST_KMS_KEY_ID;
var bucket = TestUtils.TEST_S3_BUCKET;
var baseKey = $"migration-test-{Guid.NewGuid()}";
var objectKeys = new[] { $"{baseKey}-0", $"{baseKey}-1", $"{baseKey}-2", $"{baseKey}-3" };

// In cross-step compatibility tests, sometimes only the get operation is needed, so we provide a
// dummy key to skip the put operation while maintaining the example's structure.
// Examples are intentionally kept simple and unmodified for customer readability.
var dummyObjectKey = $"{baseKey}-dummy";

// Successfully round trip step 0
await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, objectKeys[0], objectKeys[0], content);

// Given: Step 1 round trip has succeeded with put/get object key = 1
await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, objectKeys[1], objectKeys[1], content);

// When: Execute Step 0 with getObjectKey=1, Then: Success (can read objects from Step 1)
await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, dummyObjectKey, objectKeys[1], content);

// Given: Step 2 round trip has succeeded with put/get object key = 2
await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, objectKeys[2], objectKeys[2], content);

// When: Execute Step 0 with getObjectKey=2, Then: Success (can read commited objects)
await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, dummyObjectKey, objectKeys[2], content);

// Given: Step 3 round trip has succeeded with put/get object key = 3
await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, objectKeys[3], objectKeys[3], content);

// When: Execute Step 0 with getObjectKey=3, Then: Success (can read commited objects)
await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, dummyObjectKey, objectKeys[3], content);

// Cleanup
foreach (var key in objectKeys)
{
await TestUtils.CleanupObject(bucket, key);
}
await TestUtils.CleanupObject(bucket, dummyObjectKey);
}
}
}
71 changes: 71 additions & 0 deletions all-examples/net/migration/MigrationStep1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Amazon.Extensions.S3.Encryption;
using Amazon.Extensions.S3.Encryption.Primitives;
using Amazon.S3.Model;

namespace migration
{
// Migration Step 1: This example demonstrates how to start using the S3 Encryption Client v4.
//
// This example's purpose is to demonstrate the code changes to
// migrate from the v3 client to the v4 client while maintaining identical behavior.
//
// When starting from a v3 client modeled in "Migration Step 0",
// "Migration Step 1" should result in no behavioral changes to your application.
//
// In this example we configure a v4 client to:
// - Write objects encrypted with non-key committing algorithms
// - Read objects encrypted either with or without key committing algorithms
//
// In this configuration, the client will continue to read objects encrypted
// with non-key committing algorithms (written by a v2 client or this migration-in-progress v4 client),
// as well as objects encrypted by a migrated v4 client
// that is configured to write objects encrypted with key committing algorithms.
//
// This configuration results in identical behavior to the S3 Encryption Client v2 client
// configured to use the default FORBID_ENCRYPT_ALLOW_DECRYPT commitment policy.
public static class MigrationStep1
{
public static async Task MigrationExampleStep1(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut)
{
Console.WriteLine("\n[Migration Step 1] Starting Step 1 Migration step. Inputs received: \n" +
"kms key ID: " + kmsKeyId + "\n" +
"bucket: " + bucket + "\n" +
"put object key: " + putObjectKey + "\n" +
"get object key: " + getObjectKey + "\n" +
"content to put: " + contentToPut + "\n"
);

var encryptionContext = new Dictionary<string, string>();
var encryptionMaterial = new EncryptionMaterialsV4(kmsKeyId, KmsType.KmsContext, encryptionContext);
var configuration = new AmazonS3CryptoConfigurationV4(SecurityProfile.V4, CommitmentPolicy.ForbidEncryptAllowDecrypt, ContentEncryptionAlgorithm.AesGcm);
var encryptionClient = new AmazonS3EncryptionClientV4(configuration, encryptionMaterial);

await encryptionClient.PutObjectAsync(
new PutObjectRequest
{
BucketName = bucket,
Key = putObjectKey,
ContentBody = contentToPut
}
);

var response = await encryptionClient.GetObjectAsync(
new GetObjectRequest
{
BucketName = bucket,
Key = getObjectKey
}
);

using var reader = new StreamReader(response.ResponseStream);
var returnedContent = await reader.ReadToEndAsync();

if (returnedContent != contentToPut)
{
throw new Exception("Content received from getObject call does not match content put to S3.");
}

Console.WriteLine($"[Migration Step 1] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!");
}
}
}
51 changes: 51 additions & 0 deletions all-examples/net/migration/MigrationStep1Test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Xunit;

namespace migration
{
public class MigrationStep1Test
{
[Trait("Category", "Test-Migration-Step1")]
[Fact]
public async Task TestMigrationStep1()
{
const string content = "test-content";
var kmsKeyId = TestUtils.TEST_KMS_KEY_ID;
var bucket = TestUtils.TEST_S3_BUCKET;
var baseKey = $"migration-test-{Guid.NewGuid()}";
var objectKeys = new[] { $"{baseKey}-0", $"{baseKey}-1", $"{baseKey}-2", $"{baseKey}-3" };

// In cross-step compatibility tests, sometimes only the get operation is needed, so we provide a
// dummy key to skip the put operation while maintaining the example's structure.
// Examples are intentionally kept simple and unmodified for customer readability.
var dummyObjectKey = $"{baseKey}-dummy";

// Successfully round trip step 1
await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, objectKeys[1], objectKeys[1], content);

// Given: Step 0 round trip has succeeded with put/get object key = 1
await MigrationStep0.MigrationExampleStep0(kmsKeyId, bucket, objectKeys[0], objectKeys[0], content);

// When: Execute Step 1 with getObjectKey=0, Then: Success (can read from Step 0)
await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, dummyObjectKey, objectKeys[0], content);

// Given: Step 2 round trip has succeeded with put/get object key = 2
await MigrationStep2.MigrationExampleStep2(kmsKeyId, bucket, objectKeys[2], objectKeys[2], content);

// When: Execute Step 1 with getObjectKey=2, Then: Success (can read from Step 2)
await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, dummyObjectKey, objectKeys[2], content);

// Given: Step 3 round trip has succeeded with put/get object key = 3
await MigrationStep3.MigrationExampleStep3(kmsKeyId, bucket, objectKeys[3], objectKeys[3], content);

// When: Execute Step 1 with getObjectKey=3, Then: Success (can read from Step 3)
await MigrationStep1.MigrationExampleStep1(kmsKeyId, bucket, dummyObjectKey, objectKeys[3], content);

// Cleanup
foreach (var key in objectKeys)
{
await TestUtils.CleanupObject(bucket, key);
}
await TestUtils.CleanupObject(bucket, dummyObjectKey);
}
}
}
78 changes: 78 additions & 0 deletions all-examples/net/migration/MigrationStep2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using Amazon.Extensions.S3.Encryption;
using Amazon.Extensions.S3.Encryption.Primitives;
using Amazon.S3.Model;

namespace migration
{
// Migration Step 2: This example demonstrates how to update your v4 client configuration
// to start writing objects encrypted with key committing algorithms.
//
// This example's purpose is to demonstrate the commitment policy code changes required to
// start writing objects encrypted with key committing algorithms
// and document the behavioral changes that will result from this change.
//
// When starting from a v4 client modeled in "Migration Step 1",
// "Migration Step 2" WILL result in behavioral changes to your application.
// The client will start writing objects encrypted with key committing algorithms.
//
// IMPORTANT: You MUST have updated your readers to be able to read objects encrypted with key committing algorithms
// before deploying the changes in this step.
// This means deploying the changes from either "Migration Step 0" (if readers are v2 clients)
// or "Migration Step 1" (if readers are v4 clients) to all of your readers
// before deploying the changes from to "Migration Step 2".
//
// Once you deploy this change to your writers, your readers will start seeing
// some objects encrypted with non-key committing algorithms,
// and some objects encrypted with key committing algorithms.
// Because the changes would have already been deployed to all our readers from earlier migration steps,
// we can be sure that our entire system is ready to read both types of objects.
// After deploying these changes but before proceeding to "Migration Step 3",
// you MUST take extra steps to ensure that your system is no longer reading
// objects encrypted with non-key committing algorithms
// (such as re-encrypting any existing objects using key committing algorithms).
public static class MigrationStep2
{
public static async Task MigrationExampleStep2(string kmsKeyId, string bucket, string putObjectKey, string getObjectKey, string contentToPut)
{
Console.WriteLine("\n[Migration Step 2] Starting Step 2 Migration step. Inputs received: \n" +
"kms key ID: " + kmsKeyId + "\n" +
"bucket: " + bucket + "\n" +
"put object key: " + putObjectKey + "\n" +
"get object key: " + getObjectKey + "\n" +
"content to put: " + contentToPut + "\n"
);

var encryptionContext = new Dictionary<string, string>();
var encryptionMaterial = new EncryptionMaterialsV4(kmsKeyId, KmsType.KmsContext, encryptionContext);
var configuration = new AmazonS3CryptoConfigurationV4(SecurityProfile.V4, CommitmentPolicy.RequireEncryptAllowDecrypt, ContentEncryptionAlgorithm.AesGcmWithCommitment);
var encryptionClient = new AmazonS3EncryptionClientV4(configuration, encryptionMaterial);

await encryptionClient.PutObjectAsync(
new PutObjectRequest
{
BucketName = bucket,
Key = putObjectKey,
ContentBody = contentToPut
}
);

var response = await encryptionClient.GetObjectAsync(
new GetObjectRequest
{
BucketName = bucket,
Key = getObjectKey
}
);

using var reader = new StreamReader(response.ResponseStream);
var returnedContent = await reader.ReadToEndAsync();

if (returnedContent != contentToPut)
{
throw new Exception("Content received from getObject call does not match content put to S3.");
}

Console.WriteLine($"[Migration Step 2] success: encryption with {putObjectKey} and decryption with {getObjectKey} completed successfully!");
}
}
}
Loading
Loading