教程:在 Azure 存储中使用 Azure 密钥保管库加密和解密 BlobTutorial: Encrypt and decrypt blobs in Azure Storage using Azure Key Vault

简介Introduction

本教程介绍如何结合使用客户端存储加密与 Azure 密钥保管库。This tutorial covers how to make use of client-side storage encryption with Azure Key Vault. 它会逐步演示如何使用这些技术在控制台应用程序中加密和解密 Blob。It walks you through how to encrypt and decrypt a blob in a console application using these technologies.

估计完成时间: 20 分钟Estimated time to complete: 20 minutes

有关 Azure Key Vault 的概述信息,请参阅什么是 Azure Key Vault?For overview information about Azure Key Vault, see What is Azure Key Vault?.

有关 Azure 存储客户端加密的概述信息,请参阅 Azure 存储的客户端加密和 Azure Key VaultFor overview information about client-side encryption for Azure Storage, see Client-Side Encryption and Azure Key Vault for Azure Storage.

先决条件Prerequisites

若要完成本教程,必须具备以下项目:To complete this tutorial, you must have the following:

  • Azure 存储帐户An Azure Storage account
  • Visual Studio 2013 或更高版本Visual Studio 2013 or later
  • Azure PowerShellAzure PowerShell

客户端加密概述Overview of client-side encryption

有关 Azure 存储客户端加密的概述,请参阅 Azure 存储的客户端加密和 Azure Key VaultFor an overview of client-side encryption for Azure Storage, see Client-Side Encryption and Azure Key Vault for Azure Storage

下面是客户端加密的工作原理的简要说明:Here is a brief description of how client side encryption works:

  1. Azure 存储客户端 SDK 生成内容加密密钥 (CEK),这是一次性使用对称密钥。The Azure Storage client SDK generates a content encryption key (CEK), which is a one-time-use symmetric key.
  2. 使用此 CEK 对客户数据进行加密。Customer data is encrypted using this CEK.
  3. 然后,使用密钥加密密钥 (KEK) 对此 CEK 进行包装(加密)。The CEK is then wrapped (encrypted) using the key encryption key (KEK). KEK 由密钥标识符标识,可以是非对称密钥对或对称密钥,还可以在本地托管或存储在 Azure 密钥保管库中。The KEK is identified by a key identifier and can be an asymmetric key pair or a symmetric key and can be managed locally or stored in Azure Key Vault. 存储空间客户端本身永远无法访问 KEK。The Storage client itself never has access to the KEK. 它只能调用密钥保管库提供的密钥包装算法。It just invokes the key wrapping algorithm that is provided by Key Vault. 客户可以根据需要选择使用自定义提供程序进行密钥包装/解包。Customers can choose to use custom providers for key wrapping/unwrapping if they want.
  4. 然后,将已加密的数据上传到 Azure 存储服务。The encrypted data is then uploaded to the Azure Storage service.

设置 Azure 密钥保管库Set up your Azure Key Vault

若要继续本教程,请执行教程什么是 Azure 密钥保管库?中所述的以下步骤:In order to proceed with this tutorial, you need to do the following steps, which are outlined in the tutorial What is Azure Key Vault?:

  • 创建密钥保管库。Create a key vault.
  • 将密钥或密码添加到密钥保管库。Add a key or secret to the key vault.
  • 将应用程序注册到 Azure Active Directory。Register an application with Azure Active Directory.
  • 授权应用程序使用密钥或密码。Authorize the application to use the key or secret.

记下将应用程序注册到 Azure Active Directory 时生成的 ClientID 和 ClientSecret。Make note of the ClientID and ClientSecret that were generated when registering an application with Azure Active Directory.

在密钥保管库中创建这两个密钥。Create both keys in the key vault. 我们在本教程的其余部分假定使用了以下名称:ContosoKeyVault 和 TestRSAKey1。We assume for the rest of the tutorial that you have used the following names: ContosoKeyVault and TestRSAKey1.

使用程序包和 AppSettings 创建控制台应用程序Create a console application with packages and AppSettings

在 Visual Studio 中创建新的控制台应用程序。In Visual Studio, create a new console application.

在 Package Manager Console 中添加必要的 Nuget 包。Add necessary nuget packages in the Package Manager Console.

Install-Package WindowsAzure.Storage
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

Install-Package Microsoft.Azure.KeyVault
Install-Package Microsoft.Azure.KeyVault.Extensions

将 AppSettings 添加到 App.Config。Add AppSettings to the App.Config.

<appSettings>
    <add key="accountName" value="myaccount"/>
    <add key="accountKey" value="theaccountkey"/>
    <add key="clientId" value="theclientid"/>
    <add key="clientSecret" value="theclientsecret"/>
    <add key="container" value="stuff"/>
</appSettings>

添加以下 using 指令并确保将对 System.Configuration 的引用添加到项目中。Add the following using directives and make sure to add a reference to System.Configuration to the project.

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Configuration;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.Azure.KeyVault;
using System.Threading;        
using System.IO;

添加方法以便为控制台应用程序获取令牌Add a method to get a token to your console application

以下方法由密钥保管库类使用,这些类需要进行身份验证才能访问密钥保管库。The following method is used by Key Vault classes that need to authenticate for access to your key vault.

private async static Task<string> GetToken(string authority, string resource, string scope)
{
    var authContext = new AuthenticationContext(authority);
    ClientCredential clientCred = new ClientCredential(
        ConfigurationManager.AppSettings["clientId"],
        ConfigurationManager.AppSettings["clientSecret"]);
    AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);

    if (result == null)
        throw new InvalidOperationException("Failed to obtain the JWT token");

    return result.AccessToken;
}

在程序中访问存储和密钥保管库Access Storage and Key Vault in your program

在 Main 函数中,添加以下代码。In the Main function, add the following code.

// This is standard code to interact with Blob storage.
StorageCredentials creds = new StorageCredentials(
    ConfigurationManager.AppSettings["accountName"],
       ConfigurationManager.AppSettings["accountKey"]);
CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);
CloudBlobClient client = account.CreateCloudBlobClient();
CloudBlobContainer contain = client.GetContainerReference(ConfigurationManager.AppSettings["container"]);
contain.CreateIfNotExists();

// The Resolver object is used to interact with Key Vault for Azure Storage.
// This is where the GetToken method from above is used.
KeyVaultKeyResolver cloudResolver = new KeyVaultKeyResolver(GetToken);

Note

密钥保管库对象模型Key Vault Object Models

务必了解,实际上有两个 Key Vault 对象模型:一个基于 REST API(KeyVault 命名空间),另一个是客户端加密的扩展。It is important to understand that there are actually two Key Vault object models to be aware of: one is based on the REST API (KeyVault namespace) and the other is an extension for client-side encryption.

密钥保管库客户端与 REST API 进行交互,并了解密钥保管库中包含的两种模型的 JSON Web 密钥和密码。The Key Vault Client interacts with the REST API and understands JSON Web Keys and secrets for the two kinds of things that are contained in Key Vault.

密钥保管库扩展似乎是专门为 Azure 存储中的客户端加密所创建的类。The Key Vault Extensions are classes that seem specifically created for client-side encryption in Azure Storage. 根据密钥解析程序的概念,它们包含密钥 (IKey) 和类的接口。They contain an interface for keys (IKey) and classes based on the concept of a Key Resolver. 需要了解两种 IKey 实现:RSAKey 和 SymmetricKey。There are two implementations of IKey that you need to know: RSAKey and SymmetricKey. 现在它们碰巧与 Key Vault 中包含的内容保持一致,但此时它们是独立的类(因此,Key Vault 客户端检索到的密钥与秘密检索未实现 IKey)。Now they happen to coincide with the things that are contained in a Key Vault, but at this point they are independent classes (so the Key and Secret retrieved by the Key Vault Client do not implement IKey).

加密 blob 和上传Encrypt blob and upload

添加以下代码以加密 Blob 并将其上传到 Azure 存储帐户。Add the following code to encrypt a blob and upload it to your Azure storage account. 使用的 ResolveKeyAsync 方法会返回 IKey。The ResolveKeyAsync method that is used returns an IKey.

// Retrieve the key that you created previously.
// The IKey that is returned here is an RsaKey.
// Remember that we used the names contosokeyvault and testrsakey1.
var rsa = cloudResolver.ResolveKeyAsync("https://contosokeyvault.vault.azure.cn/keys/TestRSAKey1", CancellationToken.None).GetAwaiter().GetResult();

// Now you simply use the RSA key to encrypt by setting it in the BlobEncryptionPolicy.
BlobEncryptionPolicy policy = new BlobEncryptionPolicy(rsa, null);
BlobRequestOptions options = new BlobRequestOptions() { EncryptionPolicy = policy };

// Reference a block blob.
CloudBlockBlob blob = contain.GetBlockBlobReference("MyFile.txt");

// Upload using the UploadFromStream method.
using (var stream = System.IO.File.OpenRead(@"C:\data\MyFile.txt"))
    blob.UploadFromStream(stream, stream.Length, null, options, null);

Note

如果查看 BlobEncryptionPolicy 构造函数,会看到它可以接受密钥和/或解析程序。If you look at the BlobEncryptionPolicy constructor, you will see that it can accept a key and/or a resolver. 请注意,现在无法将解析程序用于加密,因为它当前不支持默认密钥。Be aware that right now you cannot use a resolver for encryption because it does not currently support a default key.

解密 Blob 并下载Decrypt blob and download

当使用解析程序类有意义时,实际上就是解密。Decryption is really when using the Resolver classes make sense. 用于加密的密钥的 ID 与其元数据中的 Blob 相关联,因此没有理由检索该密钥,请记住密钥与 Blob 之间的关联关系。The ID of the key used for encryption is associated with the blob in its metadata, so there is no reason for you to retrieve the key and remember the association between key and blob. 只需确保该密钥保留在密钥保管库中。You just have to make sure that the key remains in Key Vault.

RSA 密钥的私钥则保留在密钥保管库中,因此,为了进行解密,来自包含 CEK 的 Blob 元数据的加密密钥会发送到密钥保管库进行解密。The private key of an RSA Key remains in Key Vault, so for decryption to occur, the Encrypted Key from the blob metadata that contains the CEK is sent to Key Vault for decryption.

添加以下代码以解密刚刚上传的 Blob。Add the following to decrypt the blob that you just uploaded.

// In this case, we will not pass a key and only pass the resolver because
// this policy will only be used for downloading / decrypting.
BlobEncryptionPolicy policy = new BlobEncryptionPolicy(null, cloudResolver);
BlobRequestOptions options = new BlobRequestOptions() { EncryptionPolicy = policy };

using (var np = File.Open(@"C:\data\MyFileDecrypted.txt", FileMode.Create))
    blob.DownloadToStream(np, null, options, null);

Note

可以通过几个其他类型的解析程序来简化密钥管理,其中包括:AggregateKeyResolver 和 CachingKeyResolver。There are a couple of other kinds of resolvers to make key management easier, including: AggregateKeyResolver and CachingKeyResolver.

使用密钥保管库密码Use Key Vault secrets

将密码用于客户端加密的方式是通过 SymmetricKey 类,因为密码实际上是一种对称密钥。The way to use a secret with client-side encryption is via the SymmetricKey class because a secret is essentially a symmetric key. 但是,如上所述,密钥保管库中的密码不会完全映射到 SymmetricKey。But, as noted above, a secret in Key Vault does not map exactly to a SymmetricKey. 这里要注意几个问题:There are a few things to understand:

  • SymmetricKey 中的密钥必须是固定长度:128、192、256、384 或 512 位。The key in a SymmetricKey has to be a fixed length: 128, 192, 256, 384, or 512 bits.
  • SymmetricKey 中的密钥应采用 Base64 编码。The key in a SymmetricKey should be Base64 encoded.
  • 用作 SymmetricKey 的密钥保管库密钥需要在密钥保管库中具有“application/octet-stream”内容类型。A Key Vault secret that will be used as a SymmetricKey needs to have a Content Type of "application/octet-stream" in Key Vault.

以下是使用 PowerShell 在密钥保管库中创建可用作 SymmetricKey 的密钥的示例。Here is an example in PowerShell of creating a secret in Key Vault that can be used as a SymmetricKey. 请注意,硬编码值 $key 仅用于演示目的。Please note that the hard coded value, $key, is for demonstration purpose only. 请在自己的代码中生成此密钥。In your own code you'll want to generate this key.

// Here we are making a 128-bit key so we have 16 characters.
//     The characters are in the ASCII range of UTF8 so they are
//    each 1 byte. 16 x 8 = 128.
$key = "qwertyuiopasdfgh"
$b = [System.Text.Encoding]::UTF8.GetBytes($key)
$enc = [System.Convert]::ToBase64String($b)
$secretvalue = ConvertTo-SecureString $enc -AsPlainText -Force

// Substitute the VaultName and Name in this command.
$secret = Set-AzureKeyVaultSecret -VaultName 'ContosoKeyVault' -Name 'TestSecret2' -SecretValue $secretvalue -ContentType "application/octet-stream"

在控制台应用程序中,可以使用与之前相同的调用将此密钥作为 SymmetricKey 进行检索。In your console application, you can use the same call as before to retrieve this secret as a SymmetricKey.

SymmetricKey sec = (SymmetricKey) cloudResolver.ResolveKeyAsync(
    "https://contosokeyvault.vault.azure.cn/secrets/TestSecret2/", 
    CancellationToken.None).GetAwaiter().GetResult();

就这么简单。That's it. 请尽情享受其中的乐趣!Enjoy!

后续步骤Next steps

若要深入了解如何将 Azure 存储与 C# 配合使用,请参阅适用于 .NET 的 Azure 存储客户端库For more information about using Azure Storage with C#, see Azure Storage Client Library for .NET.

有关 Blob REST API 的详细信息,请参阅 Blob 服务 REST APIFor more information about the Blob REST API, see Blob Service REST API.

有关 Azure 存储的最新信息,请转到 Azure 存储团队博客For the latest information on Azure Storage, go to the Azure Storage Team Blog.