

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# AWS KMS 分层钥匙圈
<a name="use-hierarchical-keyring"></a>


****  

|  | 
| --- |
| 我们的客户端加密库已重命名为 AWS 数据库加密 SDK。本开发人员指南仍提供有关 [DynamoDB 加密客户端](legacy-dynamodb-encryption-client.md)的信息。 | 

**注意**  
自 2023 年 7 月 24 日起，不支持在开发者预览期间创建的分支密钥。创建新的分支密钥以继续使用您在开发者预览版期间创建的密钥库。

使用 AWS KMS 分层密钥环，您可以在对称加密 KMS 密钥下保护您的加密材料，而无需在 AWS KMS 每次加密或解密记录时都调用。对于需要最大限度地减少调用的应用程序以及可以在不违反其安全要求的情况下重复使用某些加密材料的应用程序来说，这是一个不错的选择。 AWS KMS

分层密钥环是一种加密材料缓存解决方案，它使用 AWS KMS 保存在Amazon DynamoDB表中的受保护*分支密钥*，然后在本地缓存用于加密和解密操作的分支密钥材料，从而减少 AWS KMS 调用次数。DynamoDB 表用作管理和保护分支密钥的密钥存储。其存储活动分支密钥和分支密钥的所有先前版本。*活动*分支密钥为最新分支密钥版本。分层密钥环对每个加密请求使用唯一的数据加密密钥，并使用从活动分支密钥派生的唯一包装密钥对每个数据加密密钥进行加密。分层密钥环依赖在活动分支密钥及其派生包装密钥之间建立的层次结构。

分层密钥环通常使用各分支密钥版本满足多个请求。但是您可以控制活动分支密钥的重复使用程度，并确定活动分支密钥的轮换频率。在您[轮换](rotate-branch-key.md)之前，分支密钥的活动版本会一直处于活动状态。活动分支密钥的先前版本不会用于执行加密操作，但仍可查询并用于解密操作。

当您实例化分层密钥环时，分层密钥环会创建本地缓存。您可以指定[缓存限制](#cache-limit)，该限制定义了分支密钥材料在过期并从缓存中移出之前存储在本地缓存中的最长时间。首次在操作中指定 a `branch-key-id` 时，分层密钥环会 AWS KMS 调用解密分支密钥并组装分支密钥材料。然后，分支密钥材料存储在本地缓存中，并重复用于所有指定 `branch-key-id` 的加密和解密操作，直至缓存限制到期。将分支密钥材料存储在本地缓存中可以减少 AWS KMS 调用。例如，假设缓存限制为 15 分钟。如果您在该缓存限制内执行 10,000 次加密操作，则[传统 AWS KMS 密钥环](use-kms-keyring.md)需要进行 10,000 次 AWS KMS 调用才能满足 10,000 次加密操作。如果您有一个处于活动状态`branch-key-id`，则分层密钥环只需要进行一次 AWS KMS 调用即可满足 10,000 个加密操作。

本地缓存将加密材料与解密材料分开。加密材料由活动分支密钥组合而成，并在缓存限制到期之前重复用于所有加密操作。解密材料是根据在加密字段的元数据中标识的分支密钥 ID 和版本汇编而成的，在缓存限制到期之前，它们可以重复用于与分支密钥 ID 和版本相关的所有解密操作。本地缓存可以一次存储同一个分支密钥的多个版本。将本地缓存配置为使用时[branch key ID supplier](#branch-key-id-supplier)，它还可以同时存储来自多个活动分支密钥的分支密钥材料。

**注意**  
 AWS 数据库加密 SDK 中所有提及*分层密钥环*的内容均指 AWS KMS 分层密钥环。

**Topics**
+ [工作原理](#how-hierarchical-keyring-works)
+ [先决条件](#hierarchical-keyring-prereqs)
+ [所需的权限](#hierarchical-keyring-permissions)
+ [选择缓存](#hierarchical-keyring-caches)
+ [创建分层密钥环](#initialize-hierarchical-keyring)
+ [使用分层密钥环进行可搜索加密](#searchable-encryption-hierarchical-keyrings)

## 工作原理
<a name="how-hierarchical-keyring-works"></a>

以下演练描述了分层密钥环如何汇编加密和解密材料，以及密钥环对加密和解密操作的不同调用。有关包装密钥派生和明文数据密钥加密过程的技术详细信息，请参阅 [AWS KMS 分层密钥环技术详细信息](reference.md#hierarchical-keyring-details)。

**加密并签名**  
以下演练描述了分层密钥环如何汇编加密材料并派生出唯一的包装密钥。

1. 加密方法要求分层密钥环提供加密材料。密钥环生成纯文本数据密钥，然后检查本地缓存中是否有有效的分支密钥材料来生成包装密钥。如果存在有效的分支密钥材料，则密钥环将进入**步骤 4**。

1. 如果没有有效的分支密钥材料，则分层密钥环会在密钥库中查询活动分支密钥。

   1. 密钥库调用 AWS KMS 解密活动分支密钥并返回纯文本活动分支密钥。标识活动分支密钥的数据会进行序列化，以便在解密调用 AWS KMS时提供额外验证数据。

   1. 密钥库返回纯文本分支密钥和标识该密钥的数据，例如分支密钥版本。

1. 分层密钥环汇编分支密钥材料（明文分支密钥和分支密钥版本），并将其副本存储在本地缓存中。

1. 分层密钥环从明文分支密钥和一个 16 字节的随机加密盐中派生出唯一的包装密钥。其使用派生包装密钥加密明文数据密钥的副本。

此加密方法使用加密材料对记录进行加密和签名。有关如何在 AWS 数据库加密 SDK 中对记录进行加密和签名的更多信息，请参阅[加密和签名](how-it-works.md#encrypt-and-sign)。

**解密并验证**  
以下演练描述了分层密钥环如何汇编解密材料并解密加密数据密钥。

1. 该解密方法标识来自加密记录的材料描述字段中的加密数据密钥，并将其传递给分层密钥环。

1. 分层密钥环反序列化标识加密数据密钥的数据，包括分支密钥版本、16 字节的加密盐以及其他描述数据密钥加密方式的信息。

   有关更多信息，请参阅 [AWS KMS 分层钥匙圈技术细节](reference.md#hierarchical-keyring-details)。

1. 分层密钥环会检查本地缓存中是否存在与**步骤 2** 标识的分支密钥版本相匹配的有效分支密钥材料。如果存在有效分支密钥材料，则密钥环将进入**步骤 6**。

1. 如果没有有效的分支密钥材料，则分层密钥环会在密钥库中查询与**步骤 2** 中确定的分支密钥版本相匹配的分支密钥。

   1. 密钥库调用 AWS KMS 解密分支密钥并返回纯文本活动分支密钥。标识活动分支密钥的数据会进行序列化，以便在解密调用 AWS KMS时提供额外验证数据。

   1. 密钥库返回纯文本分支密钥和标识该密钥的数据，例如分支密钥版本。

1. 分层密钥环汇编分支密钥材料（明文分支密钥和分支密钥版本），并将其副本存储在本地缓存中。

1. 分层密钥环使用汇编的分支密钥材料和**步骤 2** 标识的 16 字节加密盐重现加密数据密钥的唯一包装密钥。

1. 分层密钥环使用重现的包装密钥解密数据密钥并返回明文数据密钥。

该解密方法使用解密材料和明文数据密钥解密和验证记录。有关如何在 AWS 数据库加密 SDK 中解密和验证记录的更多信息，请参阅[解密](how-it-works.md#decrypt-and-verify)和验证。

## 先决条件
<a name="hierarchical-keyring-prereqs"></a>

在创建和使用分层密钥环之前，请确保满足以下先决条件。
+ 您或您的密钥库管理员已[创建密钥库](create-keystore.md)并[创建了至少一个有效的分支密钥](create-branch-keys.md)。
+ 您已经[配置了密钥存储操作](keystore-actions.md#config-keystore-actions)。
**注意**  
如何配置密钥存储操作决定了您可以执行的操作以及分层密钥环可以使用哪些 KMS 密钥。有关更多信息，请参阅[密钥存储操作](keystore-actions.md)。
+ 您拥有访问和使用密钥库和分支密钥所需的 AWS KMS 权限。有关更多信息，请参阅 [所需的权限](#hierarchical-keyring-permissions)。
+ 您已经查看了支持的缓存类型并配置了最适合您需求的缓存类型。有关更多信息，请参阅 [选择缓存](#hierarchical-keyring-caches)。

## 所需的权限
<a name="hierarchical-keyring-permissions"></a>

 AWS 数据库加密 SDK 不需要 AWS 账户 ，也不依赖于任何一个 AWS 服务。但是，要使用分层密钥环，您需要对 AWS 账户 密钥库中的对称加密 AWS KMS key具有以下最低权限。
+ [要使用分层密钥环加密和解密数据，你需要 kms: Decrypt。](https://docs.aws.amazon.com/kms/latest/APIReference/API_Decrypt.html)
+ 要[创建](create-branch-keys.md)和[轮换](rotate-branch-key.md)分支密钥，你需要 k [ms: GenerateDataKeyWithoutPlaintext](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKeyWithoutPlaintext.html) 和 [kms: ReEncrypt](https://docs.aws.amazon.com/kms/latest/APIReference/API_ReEncrypt.html)。

有关控制对分支密钥和密钥库的访问权限的更多信息，请参阅[实施最低权限](keystore-least-privilege.md)。

## 选择缓存
<a name="hierarchical-keyring-caches"></a>

分层密钥环 AWS KMS 通过在本地缓存加密和解密操作中使用的分支密钥材料来减少调用的次数。在[创建分层密钥环](#initialize-hierarchical-keyring)之前，您需要决定要使用的缓存类型。您可以使用默认缓存或自定义缓存以最适合您的需求。

分层密钥环支持以下缓存类型：
+ [默认缓存](#cache-default)
+ [MultiThreaded 缓存](#cache-multithreaded)
+ [StormTracking 缓存](#cache-stormtracking)
+ [共享缓存](#cache-shared)

### 默认缓存
<a name="cache-default"></a>

对于大多数用户而言，默认缓存可满足其线程要求。默认缓存用于支持超多线程环境。当分支密钥材料条目过期时，默认缓存会 AWS KMS 提前 10 秒通知一个线程分支密钥材料条目将过期，从而防止多个线程调用。这样可以确保只有一个线程向发送刷新缓存的请求。 AWS KMS 

Default 和 StormTracking 缓存支持相同的线程模型，但您只需要指定入口容量即可使用 Default 缓存。要进行更精细的缓存自定义，请使用。[StormTracking 缓存](#cache-stormtracking)

除非要自定义可以存储在本地缓存中的分支密钥材料条目的数量，否则在创建分层密钥环时无需指定缓存类型。如果未指定缓存类型，则分层密钥环使用默认缓存类型并将条目容量设置为 1000。

要自定义默认缓存，请指定以下值：
+ **条目容量**：限制可以存储在本地缓存中的分支密钥材料条目的数量。

------
#### [ Java ]

```
.cache(CacheType.builder()
        .Default(DefaultCache.builder()
        .entryCapacity(100)
        .build())
```

------
#### [ C\$1 / .NET ]

```
CacheType defaultCache = new CacheType
{
    Default = new DefaultCache{EntryCapacity = 100}
};
```

------
#### [ Rust ]

```
let cache: CacheType = CacheType::Default(
    DefaultCache::builder()
        .entry_capacity(100)
        .build()?,
);
```

------

### MultiThreaded 缓存
<a name="cache-multithreaded"></a>

 MultiThreaded 缓存可在多线程环境中安全使用，但它不提供任何可最大限度减少 AWS KMS 或 Amazon DynamoDB 调用的功能。因此，当分支密钥材料条目到期时，所有线程均将同时收到通知。这可能会导致多次 AWS KMS 调用刷新缓存。

要使用 MultiThreaded 缓存，请指定以下值：
+ **条目容量**：限制可以存储在本地缓存中的分支密钥材料条目的数量。
+ **条目修剪尾部大小**：定义在达到条目容量时要修剪的条目数量。

------
#### [ Java ]

```
.cache(CacheType.builder()
        .MultiThreaded(MultiThreadedCache.builder()
        .entryCapacity(100)
        .entryPruningTailSize(1)                                        
        .build())
```

------
#### [ C\$1 / .NET ]

```
CacheType multithreadedCache = new CacheType
{
    MultiThreaded = new MultiThreadedCache
    {
        EntryCapacity = 100,
        EntryPruningTailSize = 1
    }
};
```

------
#### [ Rust ]

```
CacheType::MultiThreaded(
            MultiThreadedCache::builder()
                    .entry_capacity(100)
                    .entry_pruning_tail_size(1)
                    .build()?)
```

------

### StormTracking 缓存
<a name="cache-stormtracking"></a>

 StormTracking 缓存旨在支持大量多线程环境。当分支密钥材料条目过期时， StormTracking 缓存会提前通知一个线程分支密钥材料条目即将过期，从而防止多个线程调用 AWS KMS 。这样可以确保只有一个线程向发送刷新缓存的请求。 AWS KMS 



要使用 StormTracking 缓存，请指定以下值：
+ **条目容量**：限制可以存储在本地缓存中的分支密钥材料条目的数量。

  默认值：1000 个条目
+ **条目修剪尾部大小**：定义一次要修剪的分支密钥材料条目的数量。

  默认值：1 个条目
+ **宽限期**：定义在到期前尝试刷新分支密钥材料的秒数。

  默认值：10 秒
+ **宽限间隔**：定义两次尝试刷新分支密钥材料间隔的秒数。

  默认值：1 秒
+ **扇出**：定义可以同时尝试刷新分支密钥材料的次数。

  默认值：20 次尝试
+ **传输中生存时间（TTL）**：定义在分支密钥材料刷新尝试超时之前的秒数。每当缓存为响应 `GetCacheEntry` 而返回 `NoSuchEntry` 时，分支密钥均视为*传输中*，直至相同密钥与 `PutCache` 条目一起写入。

  默认值：10 秒
+ **睡眠**：定义超过 `fanOut` 时线程应睡眠的秒数。

  默认值：20 毫秒

------
#### [ Java ]

```
.cache(CacheType.builder()
        .StormTracking(StormTrackingCache.builder()
        .entryCapacity(100)
        .entryPruningTailSize(1)
        .gracePeriod(10)
        .graceInterval(1)
        .fanOut(20) 
        .inFlightTTL(10)
        .sleepMilli(20)                                        
        .build())
```

------
#### [ C\$1 / .NET ]

```
CacheType stormTrackingCache = new CacheType
{
    StormTracking = new StormTrackingCache
    {
        EntryCapacity = 100,
        EntryPruningTailSize = 1,
        FanOut = 20,
        GraceInterval = 1,
        GracePeriod = 10,
        InFlightTTL = 10,
        SleepMilli = 20
    }
};
```

------
#### [ Rust ]

```
CacheType::StormTracking(
                StormTrackingCache::builder()
                    .entry_capacity(100)
                    .entry_pruning_tail_size(1)
                    .grace_period(10)
                    .grace_interval(1)
                    .fan_out(20)
                    .in_flight_ttl(10)
                    .sleep_milli(20)
                    .build()?)
```

------

### 共享缓存
<a name="cache-shared"></a>

默认情况下，每次实例化密钥环时，分层密钥环都会创建一个新的本地缓存。但是，共享缓存允许您在多个分层密钥环之间共享缓存，从而有助于节省内存。共享缓存不是为您实例化的每个分层密钥环创建新的加密材料缓存，而是在内存中只存储一个缓存，供所有引用它的分层密钥环使用。共享缓存可避免在密钥环之间重复加密材料，从而帮助优化内存使用率。相反，分层密钥环可以访问相同的底层缓存，从而减少总体内存占用。

创建共享缓存时，仍需要定义缓存类型。您可以指定[默认缓存](#cache-default)[MultiThreaded 缓存](#cache-multithreaded)、或[StormTracking 缓存](#cache-stormtracking)作为缓存类型，也可以替换任何兼容的自定义缓存。



**分区**  
多个分层密钥环可以使用单个共享缓存。使用共享缓存创建分层密钥环时，可以定义可选的**分区 ID**。分区 ID 可区分哪个分层密钥环正在写入缓存。如果两个分层密钥环引用相同的分区 ID 和分支密钥 ID[logical key store name](create-keystore.md#logical-key-store-name)，则两个密钥环将在缓存中共享相同的缓存条目。如果您创建了两个具有相同共享缓存但分区不同的分层密钥环 IDs，则每个密钥环只能访问共享缓存中自己指定的分区中的缓存条目。分区充当共享缓存中的逻辑分区，允许每个分层密钥环在自己的指定分区上独立运行，而不会干扰存储在另一个分区中的数据。

如果您打算重复使用或共享分区中的缓存条目，则必须定义自己的分区 ID。当您将分区 ID 传递给分层密钥环时，密钥环可以重复使用共享缓存中已存在的缓存条目，而不必再次检索和重新授权分支密钥材料。如果您未指定分区 ID，则每次实例化分层密钥环时，都会自动为密钥环分配一个唯一的分区 ID。

以下过程演示如何创建[默认缓存类型的共享缓存](#cache-default)并将其传递给分层密钥环。

1. 使用[材料提供者库 `CryptographicMaterialsCache`](https://github.com/aws/aws-cryptographic-material-providers-library) (MPL) 创建 (CMC)。

------
#### [ Java ]

   ```
   // Instantiate the MPL
   final MaterialProviders matProv =
       MaterialProviders.builder()
           .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
           .build();
   
   // Create a CacheType object for the Default cache
   final CacheType cache =
       CacheType.builder() 
           .Default(DefaultCache.builder().entryCapacity(100).build())
           .build();
   
   // Create a CMC using the default cache
   final CreateCryptographicMaterialsCacheInput cryptographicMaterialsCacheInput =
       CreateCryptographicMaterialsCacheInput.builder()
           .cache(cache)
           .build();
   
   final ICryptographicMaterialsCache sharedCryptographicMaterialsCache =
       matProv.CreateCryptographicMaterialsCache(cryptographicMaterialsCacheInput);
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Instantiate the MPL
   var materialProviders = new MaterialProviders(new MaterialProvidersConfig());
    
   // Create a CacheType object for the Default cache
   var cache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} };
    
   // Create a CMC using the default cache
   var cryptographicMaterialsCacheInput = new CreateCryptographicMaterialsCacheInput {Cache = cache};
    
   var sharedCryptographicMaterialsCache = materialProviders.CreateCryptographicMaterialsCache(cryptographicMaterialsCacheInput);
   ```

------
#### [ Rust ]

   ```
   // Instantiate the MPL
   let mpl_config = MaterialProvidersConfig::builder().build()?;
   let mpl = mpl_client::Client::from_conf(mpl_config)?;
   
   // Create a CacheType object for the default cache
   let cache: CacheType = CacheType::Default(
       DefaultCache::builder()
           .entry_capacity(100)
           .build()?,
   );
   
   // Create a CMC using the default cache
   let shared_cryptographic_materials_cache: CryptographicMaterialsCacheRef = mpl.
       create_cryptographic_materials_cache()
       .cache(cache)
       .send()
       .await?;
   ```

------

1. 为共享缓存创建`CacheType`对象。

   将`sharedCryptographicMaterialsCache`您在**步骤 1** 中创建的传递给新`CacheType`对象。

------
#### [ Java ]

   ```
   // Create a CacheType object for the sharedCryptographicMaterialsCache
   final CacheType sharedCache =
       CacheType.builder()
           .Shared(sharedCryptographicMaterialsCache)
           .build();
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Create a CacheType object for the sharedCryptographicMaterialsCache
   var sharedCache = new CacheType { Shared = sharedCryptographicMaterialsCache };
   ```

------
#### [ Rust ]

   ```
   // Create a CacheType object for the shared_cryptographic_materials_cache
   let shared_cache: CacheType = CacheType::Shared(shared_cryptographic_materials_cache);
   ```

------

1. 将**步骤 2** 中的`sharedCache`对象传递到分层密钥环。

   使用共享缓存创建分层密钥环时，可以选择定义一个`partitionID`以在多个分层密钥环之间共享缓存条目。如果您未指定分区 ID，则分层密钥环会自动为密钥环分配一个唯一的分区 ID。
**注意**  
如果您创建两个或更多引用相同分区 ID 和分支密钥 ID 的密钥环，则您的分层密钥环将在共享缓存中共享相同的缓存条目。[logical key store name](create-keystore.md#logical-key-store-name)如果您不希望多个密钥环共享相同的缓存条目，则必须为每个分层密钥环使用唯一的分区 ID。

   以下示例创建了一个分层密钥环[branch key ID supplier](#branch-key-id-supplier)，其[缓存限制为](#cache-limit) 600 秒。有关以下分层密钥环配置中定义的值的更多信息，请参阅[创建分层密钥环](#initialize-hierarchical-keyring)。

------
#### [ Java ]

   ```
   // Create the Hierarchical keyring
   final CreateAwsKmsHierarchicalKeyringInput keyringInput =
       CreateAwsKmsHierarchicalKeyringInput.builder()
           .keyStore(keystore)
           .branchKeyIdSupplier(branchKeyIdSupplier)
           .ttlSeconds(600)
           .cache(sharedCache)
           .partitionID(partitionID)
           .build();        
   final IKeyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Create the Hierarchical keyring        
   var createKeyringInput = new CreateAwsKmsHierarchicalKeyringInput
   {
      KeyStore = keystore,
      BranchKeyIdSupplier = branchKeyIdSupplier,
      Cache = sharedCache,
      TtlSeconds = 600,
      PartitionId = partitionID
   };
   var keyring = materialProviders.CreateAwsKmsHierarchicalKeyring(createKeyringInput);
   ```

------
#### [ Rust ]

   ```
   // Create the Hierarchical keyring
   let keyring1 = mpl
       .create_aws_kms_hierarchical_keyring()
       .key_store(key_store1)
       .branch_key_id(branch_key_id.clone())
       // CryptographicMaterialsCacheRef is an Rc (Reference Counted), so if you clone it to
       // pass it to different Hierarchical Keyrings, it will still point to the same
       // underlying cache, and increment the reference count accordingly.
       .cache(shared_cache.clone())
       .ttl_seconds(600)
       .partition_id(partition_id.clone())
       .send()
       .await?;
   ```

------

## 创建分层密钥环
<a name="initialize-hierarchical-keyring"></a>

要创建分层密钥环，必须提供以下值：
+ **密钥库名称**

  您或您的密钥库管理员创建的用作密钥存储的 DynamoDB 表的名称。
+ 

  **缓存限制生存时间（TTL）**

  本地缓存中的分支密钥材料条目在过期之前可使用的时长（以秒为单位）。缓存限制 TTL 决定了客户端调 AWS KMS 用授权使用分支密钥的频率。该值必须大于零。缓存限制 TTL 到期后，该条目将永远不会被提供，并将从本地缓存中逐出。
+ **分支密钥标识符**

  您可以静态配置用于标识密钥库中单个活动分支密钥的，也可以提供分支密钥 ID 供应商。`branch-key-id`

  

  *分支密钥 ID 提供*者使用存储在加密上下文中的字段来确定解密记录需要哪个分支密钥。默认情况下，加密上下文中仅包含分区和排序密钥。但是，您可以使用加密[操作在`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`加密](concepts.md#crypt-actions)上下文中包含其他字段。

  对于每个租户都有自己的分支密钥的多租户数据库，我们强烈建议使用分支密钥 ID 供应商。您可以使用分支密钥 ID 供应商为分支密钥创建友好名称， IDs 以便轻松识别特定租户的正确分支密钥 ID。例如，易记名称使您可以将分支密钥引用为 `tenant1` 而非 `b3f61619-4d35-48ad-a275-050f87e15122`。

  对于解密操作，您可以静态配置单个分层密钥环以限制对单个租户进行解密，也可以使用分支密钥 ID 提供程序确定哪个租户负责解密记录。
+ **（可选）缓存**

  如果要自定义缓存类型或可存储在本地缓存中分支密钥材料条目的数量，请在初始化密钥环时指定缓存类型和条目容量。

  分层密钥环支持以下缓存类型：默认、 MultiThreaded StormTracking、和共享。有关演示如何定义每种缓存类型的更多信息和示例，请参阅[选择缓存](#hierarchical-keyring-caches)。

  如果未指定缓存，则分层密钥环会自动使用默认缓存类型并将条目容量设置为 1000。
+ **（可选）分区 ID**

  如果指定[共享缓存](#cache-shared)，则可以选择定义分区 ID。分区 ID 可区分哪个分层密钥环正在写入缓存。如果您打算重复使用或共享分区中的缓存条目，则必须定义自己的分区 ID。您可以为分区 ID 指定任何字符串。如果您未指定分区 ID，则会在创建密钥环时自动为密钥环分配一个唯一的分区 ID。

  有关更多信息，请参阅 [Partitions](#shared-cache-partitions)。
**注意**  
如果您创建两个或更多引用相同分区 ID 和分支密钥 ID 的密钥环，则您的分层密钥环将在共享缓存中共享相同的缓存条目。[logical key store name](create-keystore.md#logical-key-store-name)如果您不希望多个密钥环共享相同的缓存条目，则必须为每个分层密钥环使用唯一的分区 ID。
+ **（可选）授权令牌列表**

  如果您通过[授权](https://docs.aws.amazon.com/kms/latest/developerguide/grants.html)控制对分层密钥环中 KMS 密钥的访问权限，则必须在初始化密钥环时提供所有必要的授权令牌。

### 使用静态分支密钥 ID 创建分层密钥环
<a name="static-branch-key-id-config"></a>

以下示例演示如何创建具有静态分支密钥 ID、缓存限制 TTL 为 600 秒的分层密钥环。[默认缓存](#cache-default)

------
#### [ Java ]

```
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder()
        .keyStore(branchKeyStoreName)
        .branchKeyId(branch-key-id)
        .ttlSeconds(600)
        .build();
final Keyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

------
#### [ C\$1 / .NET ]

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var keyringInput = new CreateAwsKmsHierarchicalKeyringInput
{
   KeyStore = keystore,
   BranchKeyIdSupplier = branchKeyIdSupplier,
   TtlSeconds = 600
};
var hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

------
#### [ Rust ]

```
let mpl_config = MaterialProvidersConfig::builder().build()?;
let mpl = mpl_client::Client::from_conf(mpl_config)?;

let hierarchical_keyring = mpl
    .create_aws_kms_hierarchical_keyring()
    .branch_key_id(branch_key_id)
    .key_store(branch_key_store_name)
    .ttl_seconds(600)
    .send()
    .await?;
```

------

### 使用分支密钥 ID 供应商创建分层密钥环
<a name="branch-key-id-supplier-config"></a>

以下过程演示如何使用分支密钥 ID 提供者创建分层密钥环。

1. 创建分支密钥 ID 供应商

   以下示例为**步骤 1** 中创建的两个分支密钥创建友好名称，并使用适用于 DynamoDB 客户端的 AWS 数据库加密 SDK 调`CreateDynamoDbEncryptionBranchKeyIdSupplier`用创建分支密钥 ID 提供者。

------
#### [ Java ]

   ```
   // Create friendly names for each branch-key-id 
   class ExampleBranchKeyIdSupplier implements IDynamoDbKeyBranchKeyIdSupplier {
       private static String branchKeyIdForTenant1;
       private static String branchKeyIdForTenant2;
   
       public ExampleBranchKeyIdSupplier(String tenant1Id, String tenant2Id) {
           this.branchKeyIdForTenant1 = tenant1Id;
           this.branchKeyIdForTenant2 = tenant2Id;
       }
   // Create the branch key ID supplier    
   final DynamoDbEncryption ddbEnc = DynamoDbEncryption.builder()
           .DynamoDbEncryptionConfig(DynamoDbEncryptionConfig.builder().build())
           .build();
   final BranchKeyIdSupplier branchKeyIdSupplier = ddbEnc.CreateDynamoDbEncryptionBranchKeyIdSupplier(
       CreateDynamoDbEncryptionBranchKeyIdSupplierInput.builder()
               .ddbKeyBranchKeyIdSupplier(new ExampleBranchKeyIdSupplier(branch-key-ID-tenant1, branch-key-ID-tenant2))
               .build()).branchKeyIdSupplier();
   ```

------
#### [ C\$1 / .NET ]

   ```
   // Create friendly names for each branch-key-id
    class ExampleBranchKeyIdSupplier : DynamoDbKeyBranchKeyIdSupplierBase {
       private String _branchKeyIdForTenant1;
       private String _branchKeyIdForTenant2;
   
       public ExampleBranchKeyIdSupplier(String tenant1Id, String tenant2Id) {
           this._branchKeyIdForTenant1 = tenant1Id;
           this._branchKeyIdForTenant2 = tenant2Id;
       }    
   // Create the branch key ID supplier
   var ddbEnc = new DynamoDbEncryption(new DynamoDbEncryptionConfig());
   var branchKeyIdSupplier = ddbEnc.CreateDynamoDbEncryptionBranchKeyIdSupplier(
       new CreateDynamoDbEncryptionBranchKeyIdSupplierInput
       {
           DdbKeyBranchKeyIdSupplier = new ExampleBranchKeyIdSupplier(branch-key-ID-tenant1, branch-key-ID-tenant2)
       }).BranchKeyIdSupplier;
   ```

------
#### [ Rust ]

   ```
   // Create friendly names for each branch_key_id
   pub struct ExampleBranchKeyIdSupplier {
       branch_key_id_for_tenant1: String,
       branch_key_id_for_tenant2: String,
   }
   
   impl ExampleBranchKeyIdSupplier {
       pub fn new(tenant1_id: &str, tenant2_id: &str) -> Self {
           Self {
               branch_key_id_for_tenant1: tenant1_id.to_string(),
               branch_key_id_for_tenant2: tenant2_id.to_string(),
           }
       }
   }
   
   // Create the branch key ID supplier                                        
   let dbesdk_config = DynamoDbEncryptionConfig::builder().build()?;
   let dbesdk = dbesdk_client::Client::from_conf(dbesdk_config)?;
   let supplier = ExampleBranchKeyIdSupplier::new(tenant1_branch_key_id, tenant2_branch_key_id);
   
   let branch_key_id_supplier = dbesdk
       .create_dynamo_db_encryption_branch_key_id_supplier()
       .ddb_key_branch_key_id_supplier(supplier)
       .send()
       .await?
       .branch_key_id_supplier
       .unwrap();
   ```

------

1. 创建分层密钥环

   以下示例使用**步骤 1** 中创建的分支密钥 ID 供应商初始化分层密钥环，缓存限制 TLL 为 600 秒，最大缓存大小为 1000。

------
#### [ Java ]

   ```
   final MaterialProviders matProv = MaterialProviders.builder()
           .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
           .build();
   final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder()
           .keyStore(keystore)
           .branchKeyIdSupplier(branchKeyIdSupplier)
           .ttlSeconds(600)
           .cache(CacheType.builder() //OPTIONAL
                   .Default(DefaultCache.builder()
                   .entryCapacity(100)
                   .build())
           .build())
           .build();
   final Keyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
   ```

------
#### [ C\$1 / .NET ]

   ```
   var matProv = new MaterialProviders(new MaterialProvidersConfig());
   var keyringInput = new CreateAwsKmsHierarchicalKeyringInput
   {
      KeyStore = keystore,
      BranchKeyIdSupplier = branchKeyIdSupplier,
      TtlSeconds = 600, 
      Cache = new CacheType
      {
           Default = new DefaultCache { EntryCapacity = 100 }
      }
   };
   var hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
   ```

------
#### [ Rust ]

   ```
   let mpl_config = MaterialProvidersConfig::builder().build()?;
   let mpl = mpl_client::Client::from_conf(mpl_config)?;
   
   let hierarchical_keyring = mpl
       .create_aws_kms_hierarchical_keyring()
       .branch_key_id_supplier(branch_key_id_supplier)
       .key_store(key_store)
       .ttl_seconds(600)
       .send()
       .await?;
   ```

------

## 使用分层密钥环进行可搜索加密
<a name="searchable-encryption-hierarchical-keyrings"></a>

[可搜索加密](searchable-encryption.md)使您无需解密整个数据库即可搜索加密的记录。该操作通过使用[信标](beacons.md)对加密字段的明文值进行索引来实现。要实现可搜索加密，必须使用分层密钥环。

密钥存储 `CreateKey` 操作将产生分支密钥和*信标密钥*。分支密钥用于记录加密和解密操作。信标密钥用于生成信标。

分支密钥和信标密钥受您在创建密钥存储服务时指定的相同 AWS KMS key 密钥和信标密钥的保护。在`CreateKey`操作调 AWS KMS 用生成分支密钥后，它使用以下请求第二次调用 k [ms: GenerateDataKeyWithoutPlaintext](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKeyWithoutPlaintext.html) 以生成信标密钥。

```
{
   "EncryptionContext": { 
      "branch-key-id" : "branch-key-id",
      "type" : type,
      "create-time" : "timestamp",
      "tablename" : "the logical table name for your key store",
      "kms-arn" : the KMS key ARN,
      "hierarchy-version" : 1
   },
   "KeyId": "the KMS key ARN",
   "NumberOfBytes": "32"
}
```

生成两个密钥后，该`CreateKey`操作会调用 [ddb: TransactWriteItems](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html) 来编写两个新项目，它们将在分支密钥存储中保留分支密钥和信标密钥。

[配置标准信标](configure-beacons.md#config-standard-beacons)时， AWS 数据库加密 SDK 会在密钥库中查询信标密钥。然后，它使用基于 HMAC 的 extract-and-expand密钥派生函数 (H [KDF](https://en.wikipedia.org/wiki/HKDF)) 将信标密钥与[标准信标的名称相结合，为给定信标](beacons.md#standard-beacon-overview)创建 HMAC 密钥。

与分支密钥不同，每个密钥库`branch-key-id`中只有一个信标密钥版本。信标密钥永远不会轮换。

### 定义您的信标密钥源
<a name="beacon-key-source"></a>

为标准信标和复合信标定义[信标版本](using-beacons.md#beacon-version)时，必须识别信标密钥并为信标密钥材料定义缓存限制生存时间（TTL）。信标密钥材料与分支密钥分开存储在单独的本地缓存中。以下片段演示了如何为单租户数据库定义 `keySource`。通过与之关联的 `branch-key-id` 来识别您的信标密钥。

------
#### [ Java ]

```
keySource(BeaconKeySource.builder()
        .single(SingleKeyStore.builder()
                .keyId(branch-key-id)
                .cacheTTL(6000)
                .build())
        .build())
```

------
#### [ C\$1 / .NET ]

```
KeySource = new BeaconKeySource
{
    Single = new SingleKeyStore
    {
       KeyId = branch-key-id,
       CacheTTL = 6000
    }
}
```

------
#### [ Rust ]

```
 .key_source(BeaconKeySource::Single(
    SingleKeyStore::builder()
        // `keyId` references a beacon key.
        // For every branch key we create in the keystore,
        // we also create a beacon key.
        // This beacon key is not the same as the branch key,
        // but is created with the same ID as the branch key.
        .key_id(branch_key_id)
        .cache_ttl(6000)
        .build()?,
))
```

------

**在多租户数据库中定义信标源**  
如果您有多租户数据库，则在配置 `keySource` 时必须指定以下值。  
+ 

  **keyFieldName**

  定义存储与信标密钥关联的 `branch-key-id` 的字段名称，该信标密钥用于为给定租户生成信标。`keyFieldName` 可以是任何字符串，但它对于数据库中所有其他字段必须是唯一的。当您将新记录写入数据库时，标识用于为该记录生成任何信标的信标密钥的 `branch-key-id` 将存储在此字段中。您必须在信标查询中包含此字段，并确定重新计算信标所需的相应信标密钥材料。有关更多信息，请参阅 [查询多租户数据库中的信标](searchable-encryption-multitenant.md#query-multitenant-beacons)。
+ **cacheTTL**

  本地信标缓存中的信标密钥材料条目在过期之前可使用的时长（以秒为单位）。该值必须大于零。当缓存限制 TTL 到期时，该条目将从本地缓存中移出。
+ **（可选）缓存**

  如果要自定义缓存类型或可存储在本地缓存中分支密钥材料条目的数量，请在初始化密钥环时指定缓存类型和条目容量。

  分层密钥环支持以下缓存类型：默认、 MultiThreaded StormTracking、和共享。有关演示如何定义每种缓存类型的更多信息和示例，请参阅[选择缓存](#hierarchical-keyring-caches)。

  如果未指定缓存，则分层密钥环会自动使用默认缓存类型并将条目容量设置为 1000。
以下示例创建了一个分层密钥环，其分支密钥 ID 提供者缓存限制 TLL 为 600 秒，条目容量为 1000。  

```
final MaterialProviders matProv = MaterialProviders.builder()
        .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())
        .build();
final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder()
        .keyStore(branchKeyStoreName)
        .branchKeyIdSupplier(branchKeyIdSupplier)
        .ttlSeconds(600)
        .cache(CacheType.builder() //OPTIONAL
                .Default(DefaultCache.builder()
                        .entryCapacity(1000)
                        .build())
                .build());
final IKeyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

```
var matProv = new MaterialProviders(new MaterialProvidersConfig());
var keyringInput = new CreateAwsKmsHierarchicalKeyringInput
{
   KeyStore = keystore,
   BranchKeyIdSupplier = branchKeyIdSupplier,
   TtlSeconds = 600, 
   Cache = new CacheType
   {
        Default = new DefaultCache { EntryCapacity = 1000 }
   }
};
var hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
```

```
let provider_config = MaterialProvidersConfig::builder().build()?;
    let mat_prov = client::Client::from_conf(provider_config)?;
    let kms_keyring = mat_prov
        .create_aws_kms_hierarchical_keyring()
        .branch_key_id(branch_key_id)
        .key_store(key_store)
        .ttl_seconds(600)
        .send()
        .await?;
```