Nas Taibi

Building resilient, secure cloud architectures for regulated industries.


Client side encryption for Amazon S3 and Azure Blob Storage

client side encryption for S3 and Azure Blob Storage can give you a very different level of control over who can see your data.

Why bother encrypting on the client at all?

Most cloud storage services already offer encryption at rest as a standard feature, but client side encryption for S3 and Azure Blob Storage can give you a very different level of control over who can see your data. In Amazon S3 you can enable server side encryption with S3 managed keys or AWS KMS keys, and Azure Storage has its own server side encryption for data at rest. These options are deliberately easy to turn on and, for many systems, they give a perfectly reasonable security baseline.

Client side encryption is different. Instead of relying on the storage platform to encrypt your data, you encrypt it before it ever leaves your application. The storage service then just holds ciphertext. That changes who has access to plaintext, how keys are managed, and which services can work directly with your data.

Both AWS and Azure now have mature client side encryption options for object storage. Understanding how they work in practice, and when they are worth the extra effort, is the focus of this article.

What client side encryption actually changes

With server side encryption, the provider encrypts objects as they are written and decrypts them when authorised clients read them. Keys might be managed by the provider or by a key management service such as AWS KMS or Azure Key Vault, but the storage platform always participates in the cryptographic operations.

Client side encryption moves that responsibility into your code. The typical pattern in both S3 and Azure Blob Storage looks like this:

  • The client library generates a one time symmetric data key for a particular object or blob.
  • Data is encrypted locally with that key.
  • The data key is then encrypted with another key, usually held in a key management system.
  • The ciphertext and the encrypted data key are sent to storage together.

This is envelope encryption. The storage service never needs the unencrypted data key, only the encrypted version stored as metadata alongside the blob or object. The result is that even if someone can download your data directly from the console or portal, they still only see ciphertext unless they also have access to the relevant keys.

The trade off is that you must handle key protection, rotation and recovery yourself, or integrate carefully with a key management system. Encryption and decryption also happen on the client machine, so your applications pay the CPU cost.

Client side encryption options for Amazon S3

S3 Encryption Client

The Amazon S3 Encryption Client is a specialised SDK that wraps standard S3 PutObject and GetObject calls. When you use it, objects are encrypted before upload and decrypted after download, so S3 only ever stores and transfers ciphertext.

The client uses envelope encryption. For each object it creates a fresh data key, encrypts the object with that key, then encrypts the data key with a separate wrapping key. Both the encrypted object and the encrypted data key are stored in S3. The object and the data key are protected using AES in Galois/Counter Mode, and the wrapping key can be stored in AWS KMS, in a hardware security module such as AWS CloudHSM, or in another key management system.

Using customer managed AWS KMS keys with the S3 Encryption Client is often referred to as CSE KMS. In that case the client must call KMS to encrypt and decrypt data keys, which introduces extra KMS requests and cost similar to using server side encryption with KMS keys. If you keep wrapping keys outside KMS, you avoid those calls but you take on full responsibility for availability and lifecycle of those keys and lose some integrations with analytics services.

The client is available in several languages, including C++, Go, Java, .NET, PHP and Ruby, and there is documented integration with services such as Amazon Athena, Amazon Redshift and Amazon EMR for specific configurations.

AWS Encryption SDK

The AWS Encryption SDK is a more general toolkit. It applies the same envelope encryption pattern to arbitrary data rather than just S3 objects. You can encrypt strings, byte arrays or streams and then store the resulting ciphertext anywhere, including S3.

As with the S3 Encryption Client, the SDK generates a new data key per message, encrypts the data, and then encrypts the data key with one or more wrapping keys. The encrypted payload contains the ciphertext, the encrypted data keys and metadata describing the algorithms and keys used. By default the SDK uses AES GCM with a 256 bit key.

The SDK has official implementations in Java, Python, C, C sharp and JavaScript, and can use AWS KMS keys as wrapping keys, including multi Region keys. It does not, however, decrypt data produced by other AWS encryption features such as the S3 Encryption Client, because each service has its own ciphertext format.

SSE C with customer supplied keys

Server side encryption with customer provided keys (SSE C) is sometimes discussed alongside client side encryption because you still manage your own keys, but the encryption happens in S3.

In the SSE C model you generate a 256 bit key and send it over TLS with each upload and download request. S3 uses that key to apply AES 256 encryption on the server. After the upload, the key is removed from memory and is not stored by S3. To retrieve the object, you must present the same key again so S3 can decrypt the data before sending it to you.

SSE C lets you keep physical possession of the keys while avoiding client side cryptographic code, but it still relies on S3 to perform encryption and decryption and does not integrate with all AWS analytics services. You also have to manage secure delivery of the key for every request.

Client side encryption for Azure Blob Storage

Algorithms and versions

Azure Blob Storage supports both client side and server side encryption. For most scenarios, Microsoft recommends using the server side features for simplicity, but the client libraries also provide client side encryption for more demanding requirements.

The Blob Storage client libraries use AES for client side encryption and support two versions in the SDK:

  • Version 2 uses AES in Galois/Counter Mode.
  • Version 1 uses AES in Cipher Block Chaining mode.

Version 1 is no longer recommended because of a security issue in the implementation of CBC mode in the client library. Microsoft advises either moving to service side encryption or upgrading applications to client side encryption version 2 and re encrypting existing data.

There is also a variant called version 2.1 in the Java SDK, which allows you to configure the size of encrypted regions for authenticated encryption between 16 bytes and 1 GiB rather than using a fixed size.

How Azure client side encryption works

Azure Blob Storage follows the same envelope encryption approach used by AWS. When you upload data with client side encryption enabled:

  • The client library generates a one time content encryption key (CEK), which is a symmetric key.
  • The data is encrypted locally using the CEK.
  • The CEK is encrypted with a key encryption key (KEK), which may be symmetric or asymmetric and can be stored in Azure Key Vault or managed elsewhere.
  • The encrypted data, the wrapped CEK and related metadata are uploaded to Blob Storage.

The Blob Storage client library does not need direct access to the KEK. Instead, it calls Key Vault or another provider to wrap and unwrap the CEK as needed. During encryption, the library generates a random initialisation vector of 16 bytes and a random CEK of 32 bytes, and stores the wrapped CEK, the IV and other information as blob metadata.

On download, the client reads the encrypted blob and its metadata, asks the key store to unwrap the CEK using the recorded key identifier, and then decrypts the data locally.

All blob types (block, page and append blobs) can be used with this scheme. Client side encryption version 2 encrypts data in 4 MiB authenticated blocks, while version 2.1 allows the region size to be configured within the documented range.

Azure strongly warns that if you overwrite blob metadata and fail to preserve the encryption metadata, the wrapped CEK and IV will be lost and the blob will become unreadable. Certain low level operations that modify encrypted blobs in place can also corrupt them, so whole blob upload and standard download operations are recommended when using client side encryption.

Azure Key Vault, metadata and a practical implementation view

In a typical design, Azure Key Vault holds the KEK, often an RSA key pair used to wrap and unwrap the CEK. The Blob Storage SDK stores encryption details in a metadata property often named something like encryptiondata. This value is a JSON document that contains the wrapped CEK, the identifier of the Key Vault key (including version), the algorithm used and the initialisation vector.

A practical implementation using .NET and the Azure Storage Blobs library shows how this works. The application creates a BlobServiceClient configured with ClientSideEncryptionOptions, providing a key encryption key (for example via a CryptographyClient backed by Key Vault) and a key resolver that can map key identifiers back to keys. Once configured, blobs written and read through this client are automatically encrypted and decrypted by the SDK.

Key rotation is handled by Key Vault key versions. Each blob is encrypted with its own CEK, but the KEK used to wrap those CEKs can have multiple versions. The metadata for each blob includes the full identifier of the KEK version used. When you rotate the KEK, new blobs are encrypted with the new version and old blobs remain decryptable because the SDK can still resolve and use the previous versions recorded in metadata, provided they remain enabled in Key Vault.

One subtle point from real world implementations is that the client usually reads the current Key Vault key version when the BlobServiceClient is created. If you want new blobs to start using a newer key version after rotation, you may need to recreate the client periodically so that it picks up the updated KEK version.

Comparing the AWS and Azure models

Despite the different packaging, the underlying model is very similar between S3 and Azure Blob Storage. Both:

  • Use envelope encryption with a unique symmetric data key (or CEK) per object or blob.
  • Encrypt that data key with a separate wrapping key held in a key management system, such as AWS KMS or Azure Key Vault.
  • Store the wrapped data key and encryption metadata alongside the encrypted data as object or blob metadata.
  • Perform encryption and decryption on the client side so that the storage service only ever holds ciphertext.

AWS separates concerns between a storage focused S3 Encryption Client and a generic AWS Encryption SDK, and also offers SSE C as a key control option while keeping encryption work on the server. Azure folds client side encryption into the Blob Storage client libraries and provides explicit guidance about using version 2 (or 2.1) rather than the older CBC based version.

In both ecosystems, using client side encryption reduces how much other cloud services can do directly with your data. For example, not all analytics services can read data encrypted with SSE C in S3, and Azure cautions against certain blob operations when client side encryption is in use. Service side encryption remains the default recommendation for straightforward scenarios, with client side encryption reserved for stricter security or regulatory requirements.

When client side encryption is worth the complexity

Client side encryption is most defensible when:

  • Regulations or contracts explicitly limit who can access plaintext, including the cloud provider.
  • You operate a multi tenant platform and want a strong technical boundary between tenants even where storage access is shared.
  • You need to keep encrypted copies of data across regions or providers without trusting every storage location equally.

In these cases, the envelope encryption approach in S3 and Azure Blob Storage lets you treat object storage as an untrusted byte store and concentrate trust in your key management system. Wrapping keys in AWS KMS or Azure Key Vault, and relying on per object data keys stored in metadata, gives you a clear story for key rotation and recovery as long as old key versions remain available.

A practical pattern, especially in multi cloud environments, is to standardise on a general purpose client side encryption library for your most sensitive data, such as the AWS Encryption SDK. You encrypt in the application using keys from your chosen key management services, then store the ciphertext in whichever object storage platform is convenient. Storage becomes a commodity service while keys remain centralised.

The real work is not only in wiring up SDKs but in designing the key hierarchy and operations. You need to decide how tenants or environments map to wrapping keys, how often those keys are rotated, what happens to data encrypted with retired key versions, and how you prevent accidental loss of encryption metadata. The Azure warning about not overwriting metadata on encrypted blobs is a good illustration of the sorts of pitfalls that need to be accounted for in operational runbooks.

Summary

AWS S3 and Azure Blob Storage both offer client side encryption built on envelope encryption and integrated with their respective key management services. In AWS you choose between the S3 Encryption Client, the more general AWS Encryption SDK and SSE C with customer supplied keys. In Azure you configure client side encryption in the Blob Storage client libraries, using version 2 or 2.1 and typically integrating with Azure Key Vault for KEK management.

Compared with simply enabling server side encryption, these options demand more deliberate design. They give you tighter control over who can read your data, including cloud operators, at the cost of additional complexity in key management and application architecture. Whether that trade off is worthwhile depends less on the cryptography itself and more on your regulatory environment, your threat model and your tolerance for operational overhead.