Blob 的客户端加密

用于 .NET 的 Azure Blob 存储客户端库支持在上传到 Azure 存储之前加密客户端应用程序中的数据,以及在下载到客户端时解密数据。 此库还支持与 Azure 密钥保管库集成,以便管理存储帐户密钥。

重要

Blob 存储同时支持服务端和客户端加密。 对于大多数场景,Azure 建议使用服务端加密功能,以便于保护数据。 若要详细了解服务端加密,请参阅静态数据的 Azure 存储加密

有关使用客户端的加密和 Azure Key Vault 引导完成加密 Blob 的过程的分步教程,请参阅使用 Azure Key Vault 在 Azure 存储中加密和解密 Blob

关于客户端加密

Azure Blob 存储客户端库使用 AES 来加密用户数据。 客户端库中提供了两个版本的客户端加密:

警告

由于客户端库的 CBC 模式实现中存在安全漏洞,因此不再建议使用版本 1 客户端加密。 有关此安全漏洞的详细信息,请参阅 Azure 存储更新 SDK 中的客户端加密以解决安全漏洞。 如果当前正在使用版本 1,建议更新应用程序以使用版本 2,并迁移数据。 有关进一步指导,请参阅以下部分:缓解应用程序中的安全漏洞

缓解应用程序中的安全漏洞

由于在 Blob 存储客户端库的 CBC 模式实现中发现了安全漏洞,因此 Azure 建议立即执行以下一项或多项操作:

  • 考虑使用服务端加密功能,而不是客户端加密。 有关服务端加密功能的详细信息,请参阅静态数据的 Azure 存储加密

  • 如果需要使用客户端加密,请将应用程序从客户端加密 v1 迁移到客户端加密 v2。

下表总结了在选择将应用程序迁移到客户端加密 v2 时需要执行的步骤:

客户端加密状态 建议的操作
应用程序将客户端加密与仅支持客户端加密 v1 的客户端库版本配合使用。 更新应用程序以使用支持客户端加密 v2 的客户端库版本。 有关受支持版本的列表,请参阅客户端加密的 SDK 支持矩阵了解详细信息...

更新代码以使用客户端加密 v2。 了解详细信息...

下载任何加密数据进行解密,然后使用客户端加密 v2 重新加密。 了解详细信息...
应用程序将客户端加密与支持客户端加密 v2 的客户端库版本配合使用。 更新代码以使用客户端加密 v2。 了解详细信息...

下载任何加密数据进行解密,然后使用客户端加密 v2 重新加密。 了解详细信息...

此外,Azure 建议执行以下步骤来帮助保护数据:

  • 将存储帐户配置为使用专用终结点来保护虚拟网络 (VNet) 和存储帐户之间通过专用链接的所有流量。 有关详细信息,请参阅对 Azure 存储使用专用终结点
  • 仅限制对特定网络的网络访问。

客户端加密的 SDK 支持矩阵

下表显示了哪些版本的适用于 .NET、Java 和 Python 的客户端库支持哪些版本的客户端加密:

.NET Java Python
客户端加密 v2 和 v1 版本 12.13.0 及更高版本 版本 12.18.0 及更高版本 版本 12.13.0 及更高版本
仅客户端加密 v1 版本 12.12.0 及更早版本 版本 12.17.0 及更早版本 版本 12.12.0 及更早版本

如果应用程序将客户端加密与早期版本的 .NET、Java 或 Python 客户端库配合使用,则必须首先将代码升级到支持客户端加密 v2 的版本。 接下来,必须使用客户端加密 v2 解密和重新加密数据。 可以根据需要在迁移代码时并行使用支持客户端加密 v2 的客户端库版本和更早版本的客户端库。 有关代码示例,请参阅示例:使用客户端加密 v2 加密和解密 Blob

客户端加密的工作原理

Azure Blob 存储客户端库使用信封加密来加密和解密客户端上的数据。 信封加密使用一个或多个其他密钥来加密密钥。

Blob 存储客户端库依赖于 Azure Key Vault 来保护用于客户端加密的密钥。 有关 Azure Key Vault 的详细信息,请参阅什么是 Azure Key Vault?

通过信封技术加密和解密

通过信封技术加密的工作方式如下:

  1. Azure 存储客户端库生成内容加密密钥 (CEK),这是一次性使用对称密钥。

  2. 使用此 CEK 对用户数据进行加密。

  3. 然后,使用密钥加密密钥 (KEK) 对此 CEK 进行包装(加密)。 KEK 由密钥标识符标识,可以是非对称密钥对或对称密钥。 可在本地管理 KEK 或将其存储在 Azure Key Vault 中。

    Azure 存储客户端库本身永远无法访问 KEK。 该库调用密钥保管库提供的密钥包装算法。 用户可以根据需要选择使用自定义提供程序进行密钥包装/解包。

  4. 然后,将加密数据上传到 Azure Blob 存储。 将包装的密钥与一些其他加密元数据一起作为元数据存储在 Blob 上。

通过信封技术解密的工作方式如下:

  1. Azure 存储客户端库假定用户在本地或 Azure Key Vault 中管理 KEK。 用户不需要知道用于加密的特定密钥。 但是,可以设置和使用一个密钥解析程序,将不同的密钥标识符解析为密钥。
  2. 客户端库下载存储在 Azure 存储中的已加密数据以及任何加密材料。
  3. 然后使用 KEK 解包(解密)包装的 CEK。 在此过程中,客户端库无权访问 KEK,但仅调用 Azure Key Vault 或其他密钥存储的解包算法。
  4. 客户端库使用 CEK 解密已加密的用户数据。

Blob 上传/下载上的加密/解密

Blob 存储客户端库仅支持在上传时加密整个 Blob。 对于下载,支持完整下载和范围下载。 客户端加密 v2 将数据区块化为 4MB 缓冲的经过身份验证的加密块,这些加密块只能整体转换。 要调整区块大小,请确保使用的是支持客户端加密 v2.1 的最新版本 SDK。 区域长度可从 16 字节到最多 1 GiB 进行配置。

在加密过程中,客户端库将生成 16 字节的随机初始化向量 (IV) 和 32 字节的随机 CEK,并使用此信息对 Blob 数据执行信封加密。 然后,已包装的 CEK 和一些附加加密元数据将与已加密的 Blob 一起存储为 Blob 元数据。

在客户端下载整个 Blob 时,已包装的 CEK 会解包,并与 IV 一起使用,以将解密后的数据返回给客户端。

下载已加密 Blob 中的任意范围需要调整用户提供的范围,以便获取少量可用于成功解密所请求范围的附加数据。

所有 Blob 类型(块 Blob、页 Blob 和追加 Blob)都可以使用此方案进行加密/解密。

警告

如果要针对 Blob 编辑或上传自己的元数据,则必须确保加密元数据已保留。 如果上传新元数据而不保留加密元数据,则已包装的 CEK、IV 和其他元数据将丢失,并且无法检索 Blob 的内容。 调用设置 Blob 元数据操作始终会替换所有 Blob 元数据。

读取或写入到已加密的 Blob 时,请使用完整 Blob 上传命令,如 Put Blob,以及范围或完整 Blob 下载命令,如 Get Blob。 避免使用协议操作(例如放置块放置块列表放置页追加块)写入加密 Blob。 对加密 Blob 调用这些操作可能会损坏该 Blob 并使其不可读。

示例:使用客户端加密 v2 加密和解密 Blob

本部分中的代码示例演示如何使用客户端加密 v2 来加密和解密 Blob。

重要

如果你有以前使用客户端加密 v1 加密的数据,则需要解密该数据,并使用客户端加密 v2 重新加密。 请参阅下面的客户端库指南和示例。

若要从 .NET 代码使用客户端加密,请参阅 Blob 存储客户端库。 请确保使用的是版本 12.13.0 或更高版本。 如果需要从版本 11.x 迁移到版本 12.13.0,请参阅迁移指南

对于客户端加密,Azure Key Vault 集成需要另外两个包:

  • Azure.Core 包提供 IKeyEncryptionKeyIKeyEncryptionKeyResolver 接口。 用于 .NET 的 Blob 存储客户端库已将此程序集定义为一个依赖项。

  • Azure.Security.KeyVault.Keys 包(版本 4.x 及更高版本)提供 Key Vault REST 客户端和与客户端加密一起使用的加密客户端。 如果使用 Azure Key Vault 作为密钥存储,则需要确保在项目中引用此包。

    Azure Key Vault 专为高价值主密钥设计,每个密钥保管库的限制也反映了这种设计。 从 Azure.Security.KeyVault.Keys 版本 4.1.0 开始,IKeyEncryptionKeyResolver 接口不支持密钥缓存。 如果在限制条件下需要缓存,可以使用本示例中演示的方法将缓存层注入到 Azure.Security.KeyVault.Keys.Cryptography.KeyResolver 实例中。

开发人员可以提供密钥、密钥解析程序或同时提供密钥和密钥解析程序。 密钥是使用密钥标识符标识的,提供包装和解包 CEK 的逻辑。 密钥解析程序用于在解密过程中解析密钥。 密钥解析程序定义了解析方法,该方法根据给定的密钥标识符返回密钥。 解析程序使用户能够在多个位置中托管的多个密钥之间进行选择。

在加密时,始终使用该密钥,而没有密钥将导致错误。

在解密时,如果指定了密钥,并且其标识符与所需的密钥标识符匹配,则使用该密钥进行解密。 否则,客户端库会尝试调用解析程序。 如果没有指定的解析程序,则客户端库将引发错误。 如果指定了解析程序,则会调用密钥解析程序来获取密钥。 如果指定了解析程序,但该解析程序不具有密钥标识符的映射,则客户端库将引发错误。

若要使用客户端加密,请创建一个 ClientSideEncryptionOptions 对象并在创建客户端时为其设置 SpecializedBlobClientOptions。 你无法按 API 设置加密选项。 其他所有事项均由客户端库在内部处理。

// Your key and key resolver instances, either through Azure Key Vault SDK or an external implementation.
IKeyEncryptionKey key;
IKeyEncryptionKeyResolver keyResolver;

// Create the encryption options to be used for upload and download.
ClientSideEncryptionOptions encryptionOptions = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V2_0)
{
   KeyEncryptionKey = key,
   KeyResolver = keyResolver,
   // String value that the client library will use when calling IKeyEncryptionKey.WrapKey()
   KeyWrapAlgorithm = "some algorithm name"
};

// Set the encryption options on the client options.
BlobClientOptions options = new SpecializedBlobClientOptions() { ClientSideEncryption = encryptionOptions };

// Create blob client with client-side encryption enabled.
// Client-side encryption options are passed from service clients to container clients, 
// and from container clients to blob clients.
// Attempting to construct a BlockBlobClient, PageBlobClient, or AppendBlobClient from a BlobContainerClient
// with client-side encryption options present will throw, as this functionality is only supported with BlobClient.
BlobClient blob = new BlobServiceClient
(new Uri($"https://{accountName}.blob.core.chinacloudapi.cn"), new DefaultAzureCredential(), options).GetBlobContainerClient("my-container").GetBlobClient("myBlob");

// Upload the encrypted contents to the blob.
blob.Upload(stream);

// Download and decrypt the encrypted contents from the blob.
MemoryStream outputStream = new MemoryStream();
blob.DownloadTo(outputStream);

可以将加密选项应用于接受 BlobClientOptions 对象的 BlobServiceClient、BlobContainerClient 或 BlobClient 构造函数。

如果代码中已存在 BlobClient 对象,但没有客户端加密选项,则可以使用一个扩展方法通过给定的 ClientSideEncryptionOptions 创建该对象的副本。 此扩展方法避免了从头开始构造新的 BlobClient 对象的开销。

using Azure.Storage.Blobs.Specialized;

// An existing BlobClient instance and encryption options.
BlobClient plaintextBlob;
ClientSideEncryptionOptions encryptionOptions;

// Get a copy of the blob that uses client-side encryption.
BlobClient clientSideEncryptionBlob = plaintextBlob.WithClientSideEncryptionOptions(encryptionOptions);

将代码更新为使用客户端加密 v2 后,请确保解密并重新加密任何现有加密数据,如使用客户端加密 v2 重新加密以前加密的数据中所述。

使用客户端加密 v2 重新加密以前加密的数据

以前使用客户端加密 v1 加密的任何数据都必须解密,然后使用客户端加密 v2 重新加密,以缓解安全漏洞。 解密需要下载数据,重新加密需要将数据重新加载到 Blob 存储。

有关演示如何将数据从客户端加密 v1 迁移到 v2 的示例项目,以及如何使用 .NET 中的客户端加密 v2 加密数据,请参阅加密迁移示例项目

客户端加密和性能

请记住,加密存储数据会导致额外的性能开销。 在应用程序中使用客户端加密时,客户端库必须安全生成 CEK 和 IV、加密内容本身、与所选密钥存储通信以进行密钥封装,以及设置其他元数据的格式并上传它们。 此开销因所加密的数据量而有所变化。 我们建议客户在开发过程中始终测试其应用程序的性能。

后续步骤