AWS Developer Blog

Client Side Data Encryption with AWS SDK for .NET and Amazon S3

by Steve Roberts | on | in .NET | Permalink | Comments |  Share

What is client-side encryption, and when would you want to use it?

Version 2 of AWS SDK for .NET provides an easy-to-use Amazon S3 encryption client that allows you to secure your sensitive data before you send it to Amazon S3. Using the AmazonS3EncryptionClient class, the SDK automatically encrypts data on the client when uploading to Amazon S3, and automatically decrypts it when data is retrieved.

EncryptionMaterials encryptionMaterials = new EncryptionMaterials(RSA.Create());
AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(encryptionMaterials);
PutObjectResponse putObjectResponse = client.PutObject(putObjectRequest);
GetObjectResponse getObjectResponse = client.GetObject(getObjectRequest);

The entire process of encryption and decryption is called "envelope encryption". AmazonS3EncryptionClient generates a one-time-use AES 256-bit symmetric key (the envelope symmetric key) to encrypt your data, then that key is encrypted by a master encryption key you supply and stored alongside your data in Amazon S3. When accessing your data with the Amazon S3 encryption client, the encrypted symmetric key is retrieved and decrypted with a master encryption key you supply, and then the data is decrypted. Your master encryption key can be a symmetric or asymmetric key.

You can also store your data in Amazon S3 with server-side encryption, but using client-side encryption has some added benefits. First, with server-side encryption, your data is encrypted and decrypted after reaching S3, whereas client-side encryption is performed locally and your data never leaves the execution environment unencrypted.

Another benefit is that client-side encryption allows you to use your own master encryption keys. This ensures that no one can decrypt your data without having access to your master encryption keys.

Encryption metadata storage location

You have the choice to store the encrypted envelope symmetric key either in object metadata or in an instruction file. The instruction file is stored at the same location as that of the object. The following code snippet shows how you can set the storage location.

AmazonS3CryptoConfiguration config = new AmazonS3CryptoConfiguration()
{
    StorageMode = CryptoStorageMode.InstructionFile
};
AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(config, encryptionMaterials);

How simple is it to use the AmazonS3EncryptionClient?

The AmazonS3EncryptionClient class implements the same interface as the standard AmazonS3Client, which means it is easy to switch to the AmazonS3EncryptionClient class. In fact, your application code will not be aware of the encryption and decryption happening automatically in the client. All you have to do is create an EncryptionMaterials object that holds an instance of either an asymmetric algorithm (preferably RSA) or a symmetric algorithm. You then simply pass the EncryptionMaterials object to the constructor of AmazonS3EncryptionClient.

The following example shows how you can use AmazonS3EncryptionClient.

EncryptionMaterials encryptionMaterials = new EncryptionMaterials(RSA.Create());

AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(encryptionMaterials);

string bucketName = "YourBucketName";
string keyName = "YourKeyName";
client.PutBucket(new PutBucketRequest { BucketName = bucketName });
PutObjectRequest putObjectRequest = new PutObjectRequest
{
    BucketName = bucketName,
    Key = keyName,
    ContentBody = "Secret Message"
};
client.PutObject(putObjectRequest);
GetObjectRequest getObjectRequest = new GetObjectRequest
{
    BucketName = bucketName,
    Key = keyName
};
GetObjectResponse getObjectResponse = client.GetObject(getObjectRequest);
using (Stream decryptedStream = getObjectResponse.ResponseStream)
{
    using (StreamReader reader = new StreamReader(decryptedStream))
    {
        string decryptedContent = reader.ReadToEnd();
        Console.WriteLine("Decrypted data: {0}", decryptedContent);
    }
}

The AWS SDK for .NET supports client-side encryption for MultiPartUpload and TransferUtility as well, but since we use Cipher Block Chaining mode, TransferUtility uploads the parts sequentially rather than in parallel. Note that this means encrypted multi-part uploads cannot take advantage of multi-threading.

What happens if your master encryption keys are lost?

If your master encryption keys are lost, you will not be able to decrypt your data. Your master encryption keys are never sent to AWS; hence, it is important that you safely store them (e.g., as a file or using a separate key management system) and load them when needed for uploading or downloading objects.

The following example shows how you can use a master encryption key with an asymmetric algorithm.

Create an instance of an RSA algorithm and save the private key in a file.

RSA rsaAlgorithm = RSA.Create();
string privateKey = rsaAlgorithm.ToXmlString(true);
string filePath = @"c:tempPrivateKey.txt";
File.WriteAllText(filePath, privateKey);
EncryptionMaterials materials = new EncryptionMaterials(rsaAlgorithm);
AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(materials);
// Perform your operations, such as PutObject, GetObject, etc.

Create an instance of an RSA algorithm and load it with the saved private key.

string filePath = @"c:tempPrivateKey.txt";
string privateKey = File.ReadAllText(filePath);
RSA rsaAlgorithm = RSA.Create();
rsaAlgorithm.FromXmlString(privateKey);
EncryptionMaterials materials = new EncryptionMaterials(rsaAlgorithm);
AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(materials);
// Perform your operations, such as PutObject, GetObject, etc.

The following example shows how you can use a master encryption key with a symmetric algorithm.

Create an instance of an AES algorithm and save the symmetric key in a file.

Aes aesAlgorithm = Aes.Create();
File.WriteAllBytes(@"c:tempSymmetricKey.txt", aesAlgorithm.Key);
EncryptionMaterials materials = new EncryptionMaterials(aesAlgorithm);
AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(materials);
//Perform your operations, such as PutObject, GetObject, etc.

Create an instance of an AES algorithm and load it with the saved SymmetricKey key.

Aes aesAlgorithm = Aes.Create();
aesAlgorithm.Key = File.ReadAllBytes(@"c:tempSymmetricKey.txt");
EncryptionMaterials materials = new EncryptionMaterials(aesAlgorithm);
AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(materials);
//Perform your operations, such as PutObject, GetObject, etc.

The AmazonS3EncryptionClient class in the AWS SDK for .NET is fully compatible with the AmazonS3EncryptionClient class in the AWS SDK for Java and AWS SDK for Ruby. All you have to do is, using one SDK, store your private encryption keys in a commonly-accessible location (for example, a .pem file) and then load them in the second SDK.