

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

# 可搜索的加密
<a name="searchable-encryption"></a>


****  

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

可搜索加密使您无需解密整个数据库即可搜索加密的记录。该操作使用*信标*完成，信标在写入字段的明文值和实际存储在数据库中的加密值之间创建映射。 AWS 数据库加密 SDK 将信标存储在添加到记录中的新字段中。根据您使用的信标类型，您可以对加密的数据执行完全匹配搜索或更为自定义的复杂查询。

**注意**  
 AWS 数据库加密 SDK 中的可搜索加密不同于学术研究中定义的可搜索对称加密，例如可[搜索](https://dl.acm.org/doi/10.1145/1180405.1180417)的对称加密。



信标是一种截断的 HMAC 散列消息认证码（HMAC）标签，用于在字段的明文值和加密值之间创建映射。当您向配置为可搜索加密的加密字段写入新值时， AWS 数据库加密 SDK 会根据纯文本值计算 HMAC。此 HMAC 输出与该字段的明文值进行一对一（1:1）匹配。HMAC 输出会被截断，以便多个不同的明文值映射到同一个被截断的 HMAC 标签中。这些误报限制了未经授权的用户识别有关明文值的区别信息的能力。当您查询信标时， AWS 数据库加密 SDK 会自动筛选掉这些误报，并返回查询的明文结果。

为每个信标生成的平均误报数通过截断后剩余的信标长度决定。如需帮助确定适合您的实现的信标长度，请参阅[确定信标长度](choosing-beacon-length.md)。

**注意**  
可搜索加密旨在在未填充的新数据库中实现。在现有数据库中配置的任何信标都只能映射上传到数据库的新记录，信标无法映射现有的数据。

**主题**
+ [信标是否适合我的数据集？](#are-beacons-right-for-me)
+ [可搜索的加密场景](#beacon-overview-example)

## 信标是否适合我的数据集？
<a name="are-beacons-right-for-me"></a>

使用信标对加密的数据执行查询会降低与客户端加密数据库相关的性能成本。当您使用信标时，查询的效率和泄露的有关数据分布的信息量之间存在内在权衡。信标不会更改字段的加密状态。使用 AWS 数据库加密 SDK 对字段进行加密和签名时，该字段的纯文本值永远不会暴露给数据库。数据库将存储字段的随机加密值。

信标与计算信标所依据的加密字段一同存储。这意味着，即使未经授权的用户无法查看加密字段的明文值，他们也可以对信标进行统计分析，以了解有关数据集分布的更多信息，并在极端情况下识别信标映射到的明文值。配置信标的方式可以缓解这些风险。特别是，[选择适当的信标长度](choosing-beacon-length.md)可以帮助您保护数据集的机密性。

**安全与性能**
+ 信标长度越短，越能保证安全性。
+ 信标长度越长，越能保证性能。

可搜索的加密可能无法为所有数据集同时提供所需的性能和安全级别。配置任何信标之前，请查看您的威胁模型、安全要求和性能需求。

确定可搜索加密是否适合您的数据集时，请考虑以下数据集唯一性要求。

**分布**  
信标所保证的安全程度取决于数据集的分布。将加密字段配置为可搜索加密时， AWS 数据库加密 SDK 会根据写入该字段的纯文本值计算 HMAC。为给定字段计算的所有信标均使用相同的密钥计算，但多租户数据库除外，它们对每个租户使用不同的密钥。这意味着，如果多次向该字段写入相同的明文值，则会为该明文值的每个实例创建相同的 HMAC 标签。  
您应避免使用包含非常常见值的字段构造信标。例如，考虑一个存储伊利诺伊州每位居民地址的数据库。如果您从加密的 `City` 字段构建信标，则由于居住在芝加哥的伊利诺伊州人口比例很大，因此按“芝加哥”计算的信标将被过度代表。即使未经授权的用户只能读取加密的值和信标值，如果信标保留了此分布，它们也可能能够识别哪些记录包含芝加哥居民的数据。为了最大限度地减少所泄露的有关您的分布的区分信息量，您必须充分截断信标。隐藏这种不均匀分布所需的信标长度会带来巨大的性能成本，可能无法满足您的应用程序需求。  
您必须仔细分析数据集的分布，以确定需要将信标截断多少。截断后剩余的信标长度与可以识别的关于分布的统计信息量直接相关。您可能需要选择较短的信标长度，以充分地最大限度减少泄露的有关数据集的区分信息量。  
在极端情况下，您无法为分布不均匀的数据集计算信标长度，以有效地平衡性能和安全性。例如，您不应使用存储罕见疾病医学检查结果的字段构造信标。由于 `NEGATIVE` 结果预计在数据集中会更加普遍，因此可以很容易地通过 `POSITIVE` 结果的罕见程度来识别结果。当字段只包含两个可能的值时，隐藏分布非常困难。如果您使用的信标长度足够短，可以隐藏分布，则所有明文值都会映射到相同的 HMAC 标签。如果您使用更长的信标长度，则可以很明显看到哪些信标会映射到明文 `POSITIVE` 值。

**相关性**  
强烈建议您避免使用具有相关值的字段构造不同的信标。使用相关字段构造的信标需要更短的信标长度，以充分地最大限度减少向未经授权的用户泄露的有关每个数据集分布的信息量。您必须仔细分析数据集，包括它的熵和相关值的联合分布，以确定需要将信标截断多少。如果产生的信标长度不能满足您的性能需求，则信标可能不适合您的数据集。  
例如，您不应使用 `City` 和 `ZIPCode` 字段构造两个单独的信标，因为邮政编码可能只与一个城市相关联。通常，信标生产生误报会限制未经授权的用户识别有关您的数据集的区分信息的能力。但是 `City` 和 `ZIPCode` 字段之间的关联意味着未经授权的用户可以轻松识别哪些结果是误报，并区分不同的邮政编码。  
您还应避免使用包含相同明文值的字段构造信标。例如，您不应使用 `mobilePhone` 和 `preferredPhone` 字段构造信标，因为它们可能具有相同的值。如果您从两个字段构造不同的信标，则 AWS 数据库加密 SDK 会使用不同的密钥为每个字段创建信标。这会为相同的明文值生成两个不同的 HMAC 标签。这两个不同的信标不太可能产生相同的误报，且未经授权的用户可能能够区分不同的电话号码。

即使您的数据集包含相关字段或具有不均匀的分布，您也可以使用较短的信标长度来构造能够保护数据集机密性的信标。但是，信标长度并不能保证数据集中的每个唯一值都会产生大量误报，从而有效地最大限度减少泄露的有关数据集的区分信息量。信标长度仅能估计产生的误报平均数。数据集分布越不均匀，信标长度在确定产生的误报平均数量方面的有效性就越低。

请仔细考虑您构造信标所依据的字段的分布，并考虑需要将信标长度截断多少才能满足您的安全要求。本章节中的以下主题假设您的信标分布均匀，并且不包含相关数据。

## 可搜索的加密场景
<a name="beacon-overview-example"></a>

下面的示例演示了一种简单的可搜索加密解决方案。在应用中，本示例中使用的示例字段可能不符合信标的分布和关联唯一性建议。在阅读本章节中的可搜索加密概念时，您可以将此示例作为参考。

以一个名为 `Employees` 的跟踪公司员工数据的数据库为例。*数据库中的每条记录都包含名为 *employeeID *LastName**、*FirstName*、和 “地址” 的字段。*`Employees` 数据库中的每个字段都由主键 `EmployeeID` 标识。

以下是数据库中的明文记录示例。

```
{
    "EmployeeID": 101,
    "LastName": "Jones",
    "FirstName": "Mary",
    "Address": {
                "Street": "123 Main",
                "City": "Anytown",
                "State": "OH",
                "ZIPCode": 12345
    }
}
```

如果您在[加密操作](concepts.md#crypt-actions)中将 `LastName` 和 `FirstName` 字段标记为 `ENCRYPT_AND_SIGN`，则这些字段中的值在上传到数据库之前会在本地进行加密。上传的已加密数据是完全随机的，数据库无法将这些数据识别为受保护。它只检测典型的数据条目。这意味着，实际存储在数据库中的记录可能如下所示。

```
{
    "PersonID": 101,
    "LastName": "1d76e94a2063578637d51371b363c9682bad926cbd",
    "FirstName": "21d6d54b0aaabc411e9f9b34b6d53aa4ef3b0a35",
    "Address": {
                "Street": "123 Main",
                "City": "Anytown",
                "State": "OH",
                "ZIPCode": 12345
    }
}
```

如果您需要在数据库中查询`LastName`字段中的精确匹配项，请[配置一个名为的标准信标](configure-beacons.md#config-standard-beacons)，*LastName*以将写入该`LastName`字段的纯文本值映射到存储在数据库中的加密值。

该信标根据字段 HMACs 中的纯文本值进行`LastName`计算。每个 HMAC 输出都被截断，因此不再与明文值完全匹配。例如，`Jones` 的完整哈希值和截断后的哈希值可能如下所示。

**完整哈希值**

`2aa4e9b404c68182562b6ec761fcca5306de527826a69468885e59dc36d0c3f824bdd44cab45526f70a2a18322000264f5451acf75f9f817e2b35099d408c833`

**截断后的哈希值**

`b35099d408c833`

配置标准信标后，您可以在 `LastName` 字段上执行相等搜索。例如，如果要搜索`Jones`，请使用*LastName*信标执行以下查询。

```
LastName = Jones
```

 AWS 数据库加密 SDK 会自动过滤掉误报并返回查询的纯文本结果。

# 信标
<a name="beacons"></a>


****  

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

信标是一种截断的 HMAC 散列消息认证码（HMAC）标签，用于在写入字段的明文值和实际存储在数据库中的加密值之间创建映射。信标不会更改字段的加密状态。信标根据字段的明文值计算 HMAC，并将其与加密值一起存储。此 HMAC 输出与该字段的明文值进行一对一（1:1）匹配。HMAC 输出会被截断，以便多个不同的明文值映射到同一个被截断的 HMAC 标签中。这些误报限制了未经授权的用户识别有关明文值的区别信息的能力。

信标只能由您的[加密](concepts.md#crypt-actions)操作中标有`ENCRYPT_AND_SIGN``SIGN_ONLY`、或`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`的字段构建。信标本身未经过签名或加密。您无法使用标记为 `DO_NOTHING` 的字段构造信标。

您配置的信标类型决定了您能够执行的查询类型。支持可搜索加密的信标类型有两种。*标准信标*执行相等搜索。*复合信标*则组合文字明文字符串和标准信标来执行复杂的数据库操作。[配置信标](configure-beacons.md)后，必须先为每个信标配置二级索引，然后才能搜索加密的字段。有关更多信息，请参阅 [通过使用信标配置二级索引](ddb-searchable-encryption.md#ddb-beacon-indexes)。

**Topics**
+ [标准信标](#standard-beacon-overview)
+ [复合信标](#compound-beacon-overview)

## 标准信标
<a name="standard-beacon-overview"></a>

标准信标是在数据库中实现可搜索加密的最简单方法。他们只能对单个加密字段或虚拟字段执行相等搜索。要了解如何配置标准信标，请参阅[配置标准信标](configure-beacons.md#config-standard-beacons)。



构造标准信标所依据的字段称为*信标源*。它用于标识信标需要映射的数据的位置。信标源可以是加密的字段，也可以是*虚拟字段*。每个标准信标中的信标源都必须是唯一的。您不能使用相同的信标源来配置两个信标。

标准信标可用于对加密或虚拟字段执行相等搜索。或者，它们可以用来构造复合信标以执行更复杂的数据库操作。为了帮助您组织和管理标准信标， AWS 数据库加密 SDK 提供了以下可选*信标样式*，用于定义标准信标的预期用途。有关更多信息，请参阅[定义信标样式](configure-beacons.md#define-beacon-styles)。

您可以创建对单个加密字段执行相等搜索的标准信标，也可以通过创建虚拟字段来创建对多个`ENCRYPT_AND_SIGN``SIGN_ONLY`、和`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`字段的串联执行相等搜索的标准信标。



**虚拟字段**  
虚拟字段是由一个或多个源字段构成的概念字段。创建虚拟字段不会向记录中写入新字段。虚拟字段未明确存储在数据库中。它用于标准信标配置中，从而向信标提供有关如何识别字段的特定片段或连接记录中的多个字段以执行特定查询的指令。一个虚拟字段需要至少一个加密字段。  
以下示例演示了您可以使用虚拟字段执行的转换和查询的类型。在应用中，本示例中使用的示例字段可能不符合信标的[分布](searchable-encryption.md#searchable-encryption-distribution)和[关联](searchable-encryption.md#searchable-encryption-correlated-values)唯一性建议。
例如，如果要对 `FirstName` 和 `LastName` 字段的连接执行相等搜索，您可以创建以下虚拟字段之一。  
+ 一个虚拟 `NameTag` 字段，由 `FirstName` 字段的第一个字母后跟 `LastName` 字段构造而成，全部使用小写字母。此虚拟字段使您能够查询 `NameTag=mjones`。
+ 一个虚拟 `LastFirst` 字段，由 `LastName` 字段后跟 `FirstName` 字段构造而成。此虚拟字段使您能够查询 `LastFirst=JonesMary`。
或者，如果您要对加密字段的特定片段执行相等搜索，请创建一个用于标识您要查询的片段的虚拟字段。  
例如，如果您要使用 IP 地址的前三个片段查询加密的 `IPAddress` 字段，请创建以下虚拟字段。  
+ 一个虚拟 `IPSegment` 字段，由 `Segments(‘.’, 0, 3)` 构造而成。此虚拟字段使您能够查询 `IPSegment=192.0.2`。该查询返回 `IPAddress` 值以“192.0.2”开头的所有记录。
虚拟字段必须是唯一的。不能用完全相同的源字段构造两个虚拟字段。  
如需帮助配置虚拟字段和使用虚拟字段的信标，请参阅[创建虚拟字段](configure-beacons.md#create-virtual-field)。

## 复合信标
<a name="compound-beacon-overview"></a>

复合信标创建的索引可以提高查询性能，并且使您能够执行更复杂的数据库操作。您可以使用复合信标组合文字明文字符串和标准信标来对加密记录执行复杂的查询，例如从单个索引中查询两种不同的记录类型或使用排序键查询字段组合。有关更多复合信标解决方案示例，参阅[选择信标类型](choosing-beacon-type.md)。

复合信标可以由标准信标或标准信标和带符号字段的组合构建。它们由各部分的列表构造。所有复合信标都应包括用于识别信标中包含的 `ENCRYPT_AND_SIGN` 字段的[加密部分](configure-beacons.md#encrypted-parts)列表。每个 `ENCRYPT_AND_SIGN` 字段都必须用标准信标识别。更复杂的复合信标还可能包括标识信标中包含的纯文本`SIGN_ONLY`或`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`字段的[签](configure-beacons.md#signed-parts)名[部分列表，以及标识复合信标组装字段的所有可能方式的构造器部分](configure-beacons.md#constructor-parts)列表。

**注意**  
 AWS 数据库加密 SDK 还支持*签名信标*，这些信标可以完全通过纯文本`SIGN_ONLY`和字段进行配置。`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`签名信标是一种复合信标，用于对已签名但未加密的字段进行索引和执行复杂查询。有关更多信息，请参阅 [创建签名的信标](configure.md#signed-beacons)。

如需帮助配置复合信标，请参阅[配置复合信标](configure-beacons.md#config-compound-beacons)。

您配置复合信标的方式决定了您可以执行的查询类型。举例来说，您可以将一些已加密和签名的部分设为可选，以提高查询的灵活性。有关复合信标可以执行的查询类型的更多信息，请参阅 [查询信标](using-beacons.md#querying-beacons)。

# 计划信标
<a name="plan-searchable-encryption"></a>


****  

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

信标旨在在未填充的新数据库中实现。在现有数据库中配置的任何信标将只会映射写入数据库的新记录。信标是根据字段的明文值计算出来的，一旦字段被加密，信标就无法映射现有数据。使用信标写入新记录后，您将无法更新信标的配置。但是，您可以为添加到记录中的新字段添加新信标。

要实现可搜索的加密，必须使用 [AWS KMS 分层密钥环](use-hierarchical-keyring.md)来生成、加密和解密用于保护记录的数据密钥。有关更多信息，请参阅 [使用分层密钥环进行可搜索加密](use-hierarchical-keyring.md#searchable-encryption-hierarchical-keyrings)。

在为可搜索加密配置[信标](searchable-encryption.md#beacon-definition)之前，您需要查看加密要求、数据库访问模式和威胁模型，以确定适合您的数据库的最佳解决方案。

您配置的[信标类型](beacons.md)决定了您可以执行的查询类型。您在标准信标配置中指定的[信标长度](choosing-beacon-length.md)决定了给定信标产生的预期误报数量。强烈建议您在配置信标之前确定并计划需要执行的查询类型。一旦您使用了信标，就无法更新配置。

强烈建议您在配置任何信标之前查看并完成以下任务。
+ [确定信标是否适合您的数据集](searchable-encryption.md#are-beacons-right-for-me)
+ [选择信标类型](choosing-beacon-type.md)
+ [选择信标长度](choosing-beacon-length.md)
+ [选择信标名称](choosing-beacon-name.md)

在为数据库计划可搜索的加密解决方案时，请记住以下信标唯一性要求。
+ **每个标准信标都必须具有唯一的[信标源](beacons.md#beacon-source)**

  不能通过同一个加密字段或虚拟字段构造多个标准信标。

  但是，单个标准信标却可用于构造多个复合信标。
+ **避免利用与现有标准信标重叠的源字段创建虚拟字段**

  通过包含用于创建另一个标准信标的源字段的虚拟字段构造标准信标会同时降低两个信标的安全性。

  有关更多信息，请参阅 [虚拟字段的安全考虑因素](configure-beacons.md#virtual-field-considerations)。

## 多租户数据库的考虑因素
<a name="planning-multitenant-beacons"></a>

要查询在多租户数据库中配置的信标，必须包含用于存储与在查询中加密记录的租户关联的 `branch-key-id` 的字段。在[定义信标密钥源](use-hierarchical-keyring.md#beacon-key-source)时定义此字段。要使查询成功，此字段中的值必须确定重新计算信标所需的相应信标密钥材料。

在配置信标之前，必须决定如何计划在查询中包含 `branch-key-id`。有关在查询中包含 `branch-key-id` 的不同方式的更多信息，请参阅 [查询多租户数据库中的信标](searchable-encryption-multitenant.md#query-multitenant-beacons)。

# 选择信标类型
<a name="choosing-beacon-type"></a>


****  

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

使用可搜索的加密，您可以通过将加密字段中的明文值映射到*信标*来搜索加密记录。您配置的信标类型决定您可以执行的查询类型。

强烈建议您在配置信标之前确定并计划需要执行的查询类型。[配置信标](configure-beacons.md)后，必须先为每个信标配置二级索引，然后才能搜索加密的字段。有关更多信息，请参阅 [通过使用信标配置二级索引](ddb-searchable-encryption.md#ddb-beacon-indexes)。

信标在写入字段的明文值和实际存储在数据库中的加密值之间创建映射。您无法比较两个标准信标的值，即使其中包含相同的底层明文。两个标准信标将为相同的明文值生成两个不同的 HMAC 标签。因此，标准信标无法执行以下查询。
+ `beacon1 = beacon2`
+ `beacon1 IN (beacon2)`
+ `value IN (beacon1, beacon2, ...)`
+ `CONTAINS(beacon1, beacon2)`

只有比较复合信标的[签名部分](configure-beacons.md#signed-parts)，您才能执行上述查询，但 `CONTAINS` 运算符除外，您可以将其与复合信标一起使用，以识别组合信标所包含的加密或签名字段的完整值。比较签名的部分时，您可以选择包含[加密部分](configure-beacons.md#encrypted-parts)的前缀，但不能包括字段的加密值。有关标准信标和复合信标可以执行的查询类型的更多信息，参阅[查询信标](using-beacons.md#querying-beacons)。

查看数据库访问模式时，请考虑以下可搜索的加密解决方案。以下示例定义了满足不同的加密和查询要求需要配置哪个信标。

## 标准信标
<a name="plan-standard-beacon"></a>

[标准信标](beacons.md#standard-beacon-overview)只能执行相等搜索。您可以使用标准信标执行以下查询。

### 查询单个已加密字段
<a name="se-example1"></a>

如果您要识别包含加密字段特定值的记录，请创建标准信标。

#### 示例
<a name="example1"></a>

在以下示例中，考虑一个名为 `UnitInspection` 的数据库，该数据库用于跟踪生产设施的检查数据。数据库中的每条记录都包含名为 `work_id`、`inspection_date`、`inspector_id_last4` 和 `unit` 的字段。完整的检查人员 ID 是一个介于 0 到 99999999 之间的数字。但是，为了确保数据集的均匀分布，`inspector_id_last4` 只存储检查人员 ID 的最后四位数字。数据库中的每个字段都由主键 `work_id` 标识。`inspector_id_last4` 和 `unit` 字段在[加密操作](concepts.md#crypt-actions)中被标记为 `ENCRYPT_AND_SIGN`。

以下是 `UnitInspection` 数据库中明文条目的示例。

```
{
    "work_id": "1c7fcff3-6e74-41a8-b7f7-925dc039830b",
    "inspection_date": 2023-06-07,
    "inspector_id_last4": 8744,
    "unit": 229304973450   
}
```

**查询记录中的单个加密字段**  
如果需要对 `inspector_id_last4` 字段进行加密，但您仍需要查询它以获得完全匹配的字段，请通过 `inspector_id_last4` 字段构造一个标准信标。然后，请使用标准信标创建二级索引。您可以使用此二级索引来查询加密的 `inspector_id_last4` 字段。

要获得配置标准信标的帮助，请参阅[配置标准信标](configure-beacons.md#config-standard-beacons)。

### 查询虚拟字段
<a name="se-example2"></a>

[虚拟字段](beacons.md#virtual-field)是由一个或多个源字段构成的概念字段。如果要对加密字段的特定分段执行相等搜索，或者对多个字段的连接执行相等搜索，请使用虚拟字段构造标准信标。所有虚拟字段都必须至少包括一个加密的源字段。

#### 示例
<a name="example2"></a>

以下示例为 `Employees` 数据库创建虚拟字段。以下是 `Employees` 数据库中的明文记录示例。

```
{
    "EmployeeID": 101,
    "SSN": 000-00-0000,
    "LastName": "Jones",
    "FirstName": "Mary",
    "Address": {
                "Street": "123 Main",
                "City": "Anytown",
                "State": "OH",
                "ZIPCode": 12345
    }
}
```

**查询加密字段的某个分段**  
在本示例中，`SSN` 字段已加密。  
如果要使用社会保障号码的最后四位数字查询 `SSN` 字段，则请创建一个虚拟字段来标识您计划查询的分段。  
使用 `Suffix(4)` 构造的虚拟 `Last4SSN` 字段构造便于您查询 `Last4SSN=0000`。使用此虚拟字段来构造标准信标。然后，请使用标准信标创建二级索引。您可以使用此二级索引在虚拟字段上进行查询。此查询返回 `SSN` 值以您指定的最后四位数字结尾的所有记录。

**查询多个字段的连接**  
以下示例演示了您可以使用虚拟字段执行的转换和查询的类型。在应用中，本示例中使用的示例字段可能不符合信标的[分布](searchable-encryption.md#searchable-encryption-distribution)和[关联](searchable-encryption.md#searchable-encryption-correlated-values)唯一性建议。
如果要对 `FirstName` 和 `LastName` 字段的连接执行相等搜索，则可以创建一个虚拟 `NameTag` 字段，该字段由 `FirstName` 字段的第一个字母后跟 `LastName` 字段组成，全部用小写字母。使用此虚拟字段来构造标准信标。然后，请使用标准信标创建二级索引。您可以使用此二级索引在虚拟字段上查询 `NameTag=mjones`。  
必须至少对其中一个源字段进行加密。可以加密 `FirstName` 或 `LastName` 中的一个，或者同时加密此两者。任何纯文本源字段都必须标记为`SIGN_ONLY`或`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`在您的[加密](concepts.md#crypt-actions)操作中。

如需帮助配置虚拟字段和使用虚拟字段的信标，请参阅[创建虚拟字段](configure-beacons.md#create-virtual-field)。

## 复合信标
<a name="plan-compound-beacons"></a>

[复合信标](beacons.md#compound-beacon-overview)根据文字明文字符串和标准信标创建索引，以执行复杂的数据库操作。您可以使用复合信标执行以下查询。

### 在单个索引上查询加密字段组合
<a name="se-example3"></a>

如果您需要在单个索引上查询加密字段的组合，请创建一个复合信标，该信标将为每个加密字段构造的各个标准信标组合在一起，形成一个索引。

配置复合信标后，您可以创建一个二级索引，以将复合信标指定为分区键来执行完全匹配查询，或者使用排序键来执行更复杂的查询。将复合信标指定为排序键的二级索引可以执行完全匹配查询和更为自定义的复杂查询。

#### 示例
<a name="example3"></a>

在以下示例中，考虑一个名为 `UnitInspection` 的数据库，该数据库用于跟踪生产设施的检查数据。数据库中的每条记录都包含名为 `work_id`、`inspection_date`、`inspector_id_last4` 和 `unit` 的字段。完整的检查人员 ID 是一个介于 0 到 99999999 之间的数字。但是，为了确保数据集的均匀分布，`inspector_id_last4` 只存储检查人员 ID 的最后四位数字。数据库中的每个字段都由主键 `work_id` 标识。`inspector_id_last4` 和 `unit` 字段在[加密操作](concepts.md#crypt-actions)中被标记为 `ENCRYPT_AND_SIGN`。

以下是 `UnitInspection` 数据库中明文条目的示例。

```
{
    "work_id": "1c7fcff3-6e74-41a8-b7f7-925dc039830b",
    "inspection_date": 2023-06-07,
    "inspector_id_last4": 8744,
    "unit": 229304973450
}
```

**对已加密字段的组合执行相等搜索**  
如果要查询 `UnitInspection` 数据库以获得 `inspector_id_last4.unit` 上的完全匹配项，请先为 `inspector_id_last4` 和 `unit` 字段创建不同的标准信标。然后，通过两个标准信标创建一个复合信标。  
配置复合信标后，创建一个二级索引，以将复合信标指定为分区键。使用此二级索引查询 `inspector_id_last4.unit` 上的完全匹配项。例如，您可以查询此信标以查找检查人员对给定单位执行的检查列表。

**对已加密字段的组合执行复杂查询**  
如果要在 `inspector_id_last4` 和 `inspector_id_last4.unit` 上查询 `UnitInspection` 数据库，请先为 `inspector_id_last4` 和 `unit` 字段创建不同的标准信标。然后，通过两个标准信标创建一个复合信标。  
配置复合信标后，创建二级索引，以将复合信标指定为排序键。使用此二级索引查询 `UnitInspection` 数据库以获得以特定检查人员开头的条目，或者查询数据库以获得特定单位 ID 范围内由特定检查人员检查过的所有单元的列表。您还可以在 `inspector_id_last4.unit` 上执行完全匹配搜索。

如需帮助配置复合信标，请参阅[配置复合信标](configure-beacons.md#config-compound-beacons)。

### 在单个索引上查询加密字段和明文字段的组合
<a name="se-example4"></a>

如果您需要在单个索引上查询加密字段和明文字段的组合，请创建一个复合信标，该信标将各个标准信标和明文字段组合在一起，形成一个索引。用于构造复合信标的纯文本字段必须标记，`SIGN_ONLY`或者`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`在您的[加密](concepts.md#crypt-actions)操作中标记。

配置复合信标后，您可以创建一个二级索引，以将复合信标指定为分区键来执行完全匹配查询，或者使用排序键来执行更复杂的查询。将复合信标指定为排序键的二级索引可以执行完全匹配查询和更为自定义的复杂查询。

#### 示例
<a name="example4"></a>

在以下示例中，考虑一个名为 `UnitInspection` 的数据库，该数据库用于跟踪生产设施的检查数据。数据库中的每条记录都包含名为 `work_id`、`inspection_date`、`inspector_id_last4` 和 `unit` 的字段。完整的检查人员 ID 是一个介于 0 到 99999999 之间的数字。但是，为了确保数据集的均匀分布，`inspector_id_last4` 只存储检查人员 ID 的最后四位数字。数据库中的每个字段都由主键 `work_id` 标识。`inspector_id_last4` 和 `unit` 字段在[加密操作](concepts.md#crypt-actions)中被标记为 `ENCRYPT_AND_SIGN`。

以下是 `UnitInspection` 数据库中明文条目的示例。

```
{
    "work_id": "1c7fcff3-6e74-41a8-b7f7-925dc039830b",
    "inspection_date": 2023-06-07,
    "inspector_id_last4": 8744,
    "unit": 229304973450
}
```

**对字段的组合执行相等搜索**  
如果要查询 `UnitInspection` 数据库以获得特定检查人员在特定日期进行的检查，请先为 `inspector_id_last4` 字段创建标准信标。`inspector_id_last4` 字段在[加密操作](concepts.md#crypt-actions)中被标记为 `ENCRYPT_AND_SIGN`。所有已加密部分都需要自己的标准信标。`inspection_date` 字段被标记为 `SIGN_ONLY`，且不需要标准信标。接下来，从 `inspection_date` 字段和 `inspector_id_last4` 标准信标创建复合信标。  
配置复合信标后，创建一个二级索引，以将复合信标指定为分区键。使用此二级索引查询数据库以获得与特定检查人员和检查日期完全匹配的记录。例如，您可以查询数据库以获取 ID 结尾为 `8744` 的检查人员在特定日期进行的所有检查的列表。

**对字段的组合执行复杂查询**  
如果要查询数据库以获得在 `inspection_date` 范围内进行的检查，或者查询数据库以获得受 `inspector_id_last4` 或 `inspection_date` 约束的在特定 `inspector_id_last4.unit` 进行的检查，请先为 `inspector_id_last4` 和 `unit` 字段创建不同的标准信标。然后，从明文 `inspection_date` 字段和两个标准信标创建复合信标。  
配置复合信标后，创建二级索引，以将复合信标指定为排序键。使用此二级索引对特定检查人员在特定日期进行的检查执行查询。例如，您可以查询数据库中以获得同一日期检查的所有单位的列表。或者，您可以查询数据库以获取在给定的检查日期范围间对特定单位进行的所有检查的列表。

如需帮助配置复合信标，请参阅[配置复合信标](configure-beacons.md#config-compound-beacons)。

# 选择信标长度
<a name="choosing-beacon-length"></a>


****  

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

当您向配置为可搜索加密的加密字段写入新值时， AWS 数据库加密 SDK 会根据纯文本值计算 HMAC。此 HMAC 输出与该字段的明文值进行一对一（1:1）匹配。HMAC 输出会被截断，以便多个不同的明文值映射到同一个被截断的 HMAC 标签中。这些碰撞或*误报*限制了未经授权的用户识别有关明文值的区别信息的能力。

为每个信标生成的平均误报数通过截断后剩余的信标长度决定。配置标准信标时，只需要定义信标长度。复合信标使用构造它们的标准信标的信标长度。

信标不会更改字段的加密状态。但是，当您使用信标时，查询的效率和泄露的有关数据分布的信息量之间存在内在权衡。

可搜索加密的目标在于，使用信标对加密数据执行查询，从而降低与客户端加密数据库相关的性能成本。信标与计算信标所依据的加密字段一同存储。这意味着，它们可以揭示有关您的数据集分布的区别信息。在极端情况下，未经授权的用户可能能够分析披露的有关您的分布的信息，并用它来识别字段的明文值。选择适当的信标长度可以帮助降低这些风险并保护分发的机密性。

查看您的威胁模型，以确定所需的安全级别。例如，有权访问您的数据库但不应访问明文数据的人越多，您就越想保护数据集分布的机密性。为了提高机密性，信标需要产生更多的误报。提高机密性会导致查询性能降低。

**安全与性能**
+ 信标长度**过长**会导致产生的误报太少，并且可能会泄露有关数据集分布的区别信息。
+ 信标长度**过短**会导出产生的误报太多，并且会增加查询的性能成本，因为它需要对数据库进行更广泛的扫描。

在确定适合解决方案的信标长度时，必须找到一个能够充分保护数据安全性且不会对查询性能产生超出绝对必要影响的长度。信标保留的安全程度取决于数据集的[分布](searchable-encryption.md#searchable-encryption-distribution)以及构造信标所依据的字段的[相关性](searchable-encryption.md#searchable-encryption-correlated-values)。以下主题假设您的信标分布均匀，并且其中不包含相关数据。

**Topics**
+ [计算信标长度](#calculate-beacon-length)
+ [示例](#beacon-length-example)

## 计算信标长度
<a name="calculate-beacon-length"></a>

信标长度以*比特*为单位进行定义，是指截断后保留的 HMAC 标签的位数。建议的信标长度因数据集的分布、相关值的存在以及您的特定安全和性能要求而异。如果您的数据集分布均匀，您可以使用以下方程和过程来帮助确定最适合您的实现的信标长度。这些方程仅会估计信标将产生的平均误报数，并不能保证数据集中的每个唯一值都会产生特定数量的误报。

**注意**  
这些方程的有效性则取决于数据集的分布。如果您的数据集分布不均匀，请参阅 [信标是否适合我的数据集？](searchable-encryption.md#are-beacons-right-for-me)。  
通常情况下，您的数据集离均匀分布越远，您就越需要缩短信标长度。

1. 

   **估算总量**

   总量是指构造标准信标所依据的字段中的唯一值的预期数量，而不是字段中存储的值的预期总数。例如，考虑一个用于标识员工会议地点的加密 `Room` 字段。`Room` 字段预计将存储 100000 个总值，但员工只能预留 50 个不同的会议室用于会议。这意味着总量为 50，因为 `Room` 字段中只能存储 50 个可能的唯一值。
**注意**  
如果您的标准信标由[虚拟字段](beacons.md#virtual-field)构造，则用于计算信标长度的总量是虚拟字段创建的唯一组合数。

   在估算总量时，请务必考虑数据集的预计增长。使用信标写入新记录后，您将无法更新信标长度。查看您的威胁模型和任何现有的数据库解决方案，以估算您预计该字段在未来五年内将存储的唯一值的数量。

   您的总量不需要很精确。首先，确定当前数据库中唯一值的数量，或者估算第一年预计存储的唯一值的数量。接下来，通过以下问题来帮助您确定未来五年内唯一值的预计增长情况。
   + 您是否期望唯一值乘以 10？ 
   + 您是否期望唯一值乘以 100？ 
   + 您是否期望唯一值乘以 1000？ 

   50000 和 60000 个唯一值之间的差异并不显著，两者都将产生相同的建议信标长度。但是，50000 和 500000 个唯一值之间的差异将显著影响建议的信标长度。

   考虑查看常见数据类型（例如邮政编码或姓氏）出现频率的相关公共数据。举例来说，美国有 41707 个邮政编码。您使用的总量应与您自己的数据库成正比。如果数据库中的 `ZIPCode` 字段包含来自整个美国的数据，则即使 `ZIPCode` 字段*当前*没有 41707 个唯一值，也可以将总量定义为 41707。如果数据库中的 `ZIPCode` 字段仅包含来自单个州的数据，并且到目前为止仅包含来自单一州的数据，则可以将总量定义为该州的邮政编码总数，而不是 41704 个。

1. **计算建议的预期碰撞次数范围**

   要确定给定字段的适当信标长度，您必须首先确定预期碰撞次数的适当范围。预期的碰撞次数表示映射到特定 HMAC 标签的唯一明文值的平均预期数量。一个唯一的明文值的预期误报数比预期的碰撞次数少一。

   建议预期的碰撞次数大于或等于二，且小于总量的平方根。只有当您的总量具有 16 个或更多唯一值时，以下方程才有效。

   ```
   2 ≤ number of collisions < √(Population)
   ```

   如果碰撞次数少于两次，则信标产生的误报数将会太少。建议将两个作为预期碰撞的最小数量，因为这意味着，平均来说，字段中的每个唯一值都会通过映射到另一个唯一值来产生至少一个误报。

1. **计算信标长度的建议范围**

   确定预期碰撞的最小和最大次数后，使用以下公式来确定适当信标长度的范围。

   ```
   number of collisions = Population * 2-(beacon length)
   ```

   首先，求解**信标长度**，其中预期碰撞次数等于二（建议的最小预期碰撞数）。

   ```
   2 = Population * 2-(beacon length)
   ```

   然后，求解**信标长度**，其中预期碰撞次数等于总量的平方根（建议的最小预期碰撞数）。

   ```
   √(Population) = Population * 2-(beacon length)
   ```

   建议将此方程产生的输出向下舍入到较短的信标长度。举例来说，如果方程产生的信标长度为 15.6，则建议将该值向下舍入到 15 位，而不是四舍五入到 16 位。

1. **选择信标长度**

   这些方程仅确定您的字段的建议信标长度范围。建议尽量使用较短的信标长度，以保护数据集的安全性。但是，您实际使用的信标长度却由您的威胁模型决定。在审查威胁模型以确定字段的最佳信标长度时，请考虑您的性能要求。

   使用较短的信标长度会降低查询性能，而使用较长的信标长度则会降低安全性。通常，如果您的数据集[分布](searchable-encryption.md#searchable-encryption-distribution)不均匀，或者您根据[相关](searchable-encryption.md#searchable-encryption-correlated-values)字段构造不同的信标，则需要使用较短的信标长度来最大限度地减少泄露的有关数据集分布的信息量。

   如果您查看威胁模型，并确定泄露的有关字段分布的任何区别信息不会对您的整体安全构成威胁，则可以选择使用比您计算的建议范围更长的信标长度。例如，如果将某个字段的建议信标长度范围计算为 9—16 位，则可以选择使用 24 位的信标长度来避免任何性能损失。

   请谨慎选择信标长度。使用信标写入新记录后，您将无法更新信标长度。

## 示例
<a name="beacon-length-example"></a>

假设一个在[加密操作](concepts.md#crypt-actions)中将 `unit` 字段标记为 `ENCRYPT_AND_SIGN` 的数据库。要为 `unit` 字段配置标准信标，我们需要确定 `unit` 字段的预期误报数量和信标长度。

1. 估算总量

   在审查了威胁模型和当前的数据库解决方案之后，预计 `unit` 字段最终将有 100000 个唯一值。

   这意味着**总量 = 100000**。

1. 计算预期碰撞次数的建议范围。

   在此示例中，预期的碰撞次数应当介于 2—316 之间。

   ```
   2 ≤ number of collisions < √(Population)
   ```

   1. 

      ```
      2 ≤ number of collisions < √(100,000)
      ```

   1. 

      ```
      2 ≤ number of collisions < 316
      ```

1. 计算信标长度的建议范围。

   在本示例中，信标长度应当介于 9–16 位之间。

   ```
   number of collisions = Population * 2-(beacon length)
   ```

   1. 计算信标长度，其中预期的碰撞次数等于**步骤 2** 中确定的最小值。

      ```
      2 = 100,000 * 2-(beacon length)
      ```

      信标长度 = 15.6 或 15 位

   1. 计算信标长度，其中预期的碰撞次数等于**步骤 2** 中确定的最大值。

      ```
      316 = 100,000 * 2-(beacon length)
      ```

      信标长度 = 8.3 或 8 位

1. 确定适合您的安全和性能要求的信标长度。

   对于低于 15 的每一个位，性能成本和安全性会翻一番。
   + 16 位
     + 平均而言，每个唯一值将映射到 1.5 个其他单位。
     + 安全性：具有相同的截断 HMAC 标签的两条记录具有相同明文值的可能性为 66%。
     + 性能：查询将针对您实际请求的每 10 条记录检索 15 条记录。
   + 14 位
     + 平均而言，每个唯一值将映射到 6.1 个其他单位。
     + 安全性：具有相同的截断 HMAC 标签的两条记录具有相同明文值的可能性为 33%。
     + 性能：查询将针对您实际请求的每 10 条记录检索 30 条记录。

# 选择信标名称
<a name="choosing-beacon-name"></a>


****  

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

每个信标都由唯一的*信标名称*标识。配置信标后，信标名称就是您在查询加密的字段时使用的名称。信标名称可以与加密的字段或[虚拟字段](beacons.md#virtual-field)相同，但不能与未加密的字段相同。两个不同的信标不能具有相同的信标名称。

有关演示如何命名和配置信标的示例，请参阅[配置信标](configure-beacons.md)。



**命名标准信标**  
命名标准信标时，强烈建议尽可能将您的信标名称解析为[*信标源*](beacons.md#beacon-source)。这意味着信标名称和构造标准信标所依据的加密字段或[虚拟](beacons.md#virtual-field)字段的名称是相同的。例如，如果您要为名为 `LastName` 的加密字段创建标准信标，则您的信标名称也应当为 `LastName`。

当您的信标名称与信标源相同时，您可以从配置中省略信标源， AWS 数据库加密 SDK 将自动使用信标名称作为信标源。

# 配置信标
<a name="configure-beacons"></a>


****  

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

支持可搜索加密的信标类型有两种。标准信标执行相等搜索。它们是在数据库中实现可搜索加密的最简单方法。复合信标则组合文字明文字符串和标准信标来执行更复杂的查询。

信标旨在在未填充的新数据库中实现。在现有数据库中配置的任何信标将只会映射写入数据库的新记录。信标是根据字段的明文值计算出来的，一旦字段被加密，信标就无法映射现有数据。使用信标写入新记录后，您将无法更新信标的配置。但是，您可以为添加到记录中的新字段添加新信标。

确定访问模式后，配置信标应该是数据库实现的第二步。然后，在配置所有信标之后，您需要创建[AWS KMS 分层密钥环](use-hierarchical-keyring.md)、定义信标版本、[为每个信标配置二级索引](ddb-searchable-encryption.md#ddb-beacon-indexes)、定义[加密操作](concepts.md#crypt-actions)以及配置数据库和 AWS 数据库加密 SDK 客户端。有关更多信息，请参阅[使用信标](using-beacons.md)。

为了更便于定义信标版本，建议为标准信标和复合信标创建列表。在配置信标时，将您创建的每个信标添加到相应的标准或复合信标列表中。

**Topics**
+ [配置标准信标](#config-standard-beacons)
+ [配置复合信标](#config-compound-beacons)
+ [示例配置](beacon-config-examples.md)

## 配置标准信标
<a name="config-standard-beacons"></a>

[标准信标](beacons.md#standard-beacon-overview)是在数据库中实现可搜索加密的最简单方法。他们只能对单个加密字段或虚拟字段执行相等搜索。

### 示例配置语法
<a name="standard-config-syntax"></a>

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

```
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
    .name("beaconName")
    .length(beaconLengthInBits)
    .build();
standardBeaconList.add(exampleStandardBeacon);
```

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

```
var standardBeaconList = new List<StandardBeacon>();
StandardBeacon exampleStandardBeacon = new StandardBeacon
  {
    Name = "beaconName",
    Length = 10
  };
standardBeaconList.Add(exampleStandardBeacon);
```

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

```
let standard_beacon_list = vec![
    StandardBeacon::builder().name("beacon_name").length(beacon_length_in_bits).build()?,
```

------

要配置标准信标，请提供以下值。

**信标名称**  
您在查询已加密字段时使用的名称。  
信标名称可以与加密的字段或虚拟字段相同，但不能与未加密的字段相同。强烈建议尽可能使用构造标准信标所依据的加密字段或[虚拟字段](beacons.md#virtual-field)的名称。两个不同的信标不能具有相同的信标名称。如需帮助确定最适合您的实现的信标名称，请参阅[选择信标名称](choosing-beacon-name.md)。

**信标长度**  
截断后保留的信标哈希值的位数。  
信标长度决定了给定信标所产生的误报平均数。要获得确定适合您的实现的信标长度的更多信息和帮助，请参阅[确定信标长度](choosing-beacon-length.md)。

**信标源（可选）**  
构造标准信标所依据的字段。  
信标源必须是字段名称或引用嵌套字段值的索引。当您的信标名称与信标源相同时，您可以从配置中省略信标源， AWS 数据库加密 SDK 将自动使用该信标名称作为信标源。

### 创建虚拟字段
<a name="create-virtual-field"></a>

要创建[虚拟字段](beacons.md#virtual-field)，您必须提供虚拟字段的名称和源字段的列表。将源字段添加到虚拟部分列表的顺序决定了连接这些字段以构建虚拟字段的顺序。以下示例将两个源字段完全连接在一起以创建一个虚拟字段。

**注意**  
我们建议您在填充数据库之前验证您的虚拟字段是否产生了预期的结果。有关更多信息，请参阅[测试信标输出](ddb-searchable-encryption.md#ddb-beacon-testing)。

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

**参见完整的代码示例**：[VirtualBeaconSearchableEncryptionExample.java](https://github.com/aws/aws-database-encryption-sdk-dynamodb//blob/main/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/searchableencryption/VirtualBeaconSearchableEncryptionExample.java) 

```
List<VirtualPart> virtualPartList = new ArrayList<>();
    virtualPartList.add(sourceField1);
    virtualPartList.add(sourceField2);

VirtualField virtualFieldName = VirtualField.builder()
    .name("virtualFieldName")
    .parts(virtualPartList)
    .build();

List<VirtualField> virtualFieldList = new ArrayList<>();
    virtualFieldList.add(virtualFieldName);
```

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

**参见完整的代码示例**：[VirtualBeaconSearchableEncryptionExample.cs](https://github.com/aws/aws-database-encryption-sdk-dynamodb/tree/main/Examples/runtimes/net/src/searchableencryption/VirtualBeaconSearchableEncryptionExample.cs)

```
var virtualPartList = new List<VirtualPart> { sourceField1, sourceField2 };

var virtualFieldName = new VirtualField
{
    Name = "virtualFieldName",
    Parts = virtualPartList
};

var virtualFieldList = new List<VirtualField> { virtualFieldName };
```

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

**参见完整的代码示例**：[virtual](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/releases/rust/db_esdk/examples/searchableencryption/virtual_beacon_searchable_encryption.rs) \$1beacon\$1searchable\$1encryption.rs

```
let virtual_part_list = vec![source_field_one, source_field_two];

let state_and_has_test_result_field = VirtualField::builder()
    .name("virtual_field_name")
    .parts(virtual_part_list)
    .build()?;

let virtual_field_list = vec![virtual_field_name];
```

------

要使用源字段的特定片段创建虚拟字段，必须先定义该转换，然后再将源字段添加到虚拟部分列表中。

#### 虚拟字段的安全考虑因素
<a name="virtual-field-considerations"></a>

信标不会改变字段的加密状态。但是，当您使用信标时，查询的效率和泄露的有关数据分布的信息量之间存在内在权衡。您配置信标的方式决定了该信标所确保的安全级别。

避免利用与现有标准信标重叠的源字段创建虚拟字段。创建包含源字段（已用于创建标准信标）的虚拟字段可能会降低两个信标的安全级别。安全性的降低程度取决于其他源字段所添加的熵水平。熵水平由附加源字段中唯一值的分布以及附加源字段占虚拟字段整体大小的位数决定。

您可以使用总量和[信标长度](choosing-beacon-length.md)来确定虚拟字段的源字段是否确保了数据集的安全性。总量是字段中唯一值的预期数量。您的总量不需要很精确。如需帮助估算某个字段的总量，请参阅[估算总量](choosing-beacon-length.md#estimate-population)。

查看虚拟字段的安全性时，请考虑以下示例。
+ Beacon1 由 `FieldA` 构造而成。`FieldA` 具有大于 **2（Beacon1 长度）** 的总量。
+ Beacon2 由 `VirtualField` 构造而成，而后者由 `FieldA`、`FieldB`、`FieldC` 和 `FieldD` 构造而成。`FieldB`、`FieldC` 和 `FieldD` 加在一起的总量大于 **2N**

如果以下语句为真，则 Beacon2 将保留 Beacon1 和 Beacon2 的安全性：

```
N ≥ (Beacon1 length)/2
```

and

```
N ≥ (Beacon2 length)/2
```

### 定义信标样式
<a name="define-beacon-styles"></a>

标准信标可用于对加密或虚拟字段执行相等搜索。或者，它们可以用来构造复合信标以执行更复杂的数据库操作。为了帮助您组织和管理标准信标， AWS 数据库加密 SDK 提供了以下可选*信标样式*，用于定义标准信标的预期用途。

**注意**  
要定义信标样式，必须使用 3.2 版或更高版本的 AWS 数据库加密 SDK。在向信标配置中添加信标样式之前，请将新版本部署给所有读者。

------
#### [ PartOnly ]

定义为的标准信标`PartOnly`只能用于定义复合信标的[加密部分](#encrypted-parts)。您不能直接查询`PartOnly`标准信标。

**Java**  

```
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
    .name("beaconName")
    .length(beaconLengthInBits)
    .style(
        BeaconStyle.builder()
           .partOnly(PartOnly.builder().build())
        .build()
    )
    .build();
standardBeaconList.add(exampleStandardBeacon);
```

**C\$1 /.NET**  

```
new StandardBeacon
{
    Name = "beaconName",
    Length = beaconLengthInBits,
    Style = new BeaconStyle
    {
        PartOnly = new PartOnly()
    }
}
```

**Rust**  

```
StandardBeacon::builder()
    .name("beacon_name")
    .length(beacon_length_in_bits)
    .style(BeaconStyle::PartOnly(PartOnly::builder().build()?))
    .build()?
```

------
#### [ Shared ]

默认情况下，每个标准信标都会生成一个唯一的 HMAC 密钥用于信标计算。因此，您无法对来自两个独立标准信标的加密字段执行相等搜索。定义为的标准信标`Shared`使用来自另一个标准信标的 HMAC 密钥进行计算。

例如，如果您需要将`beacon1`字段与字段进行比较，请定义`beacon2`为使用来`beacon2`自 HMAC 密钥进行计算`beacon1`的`Shared`信标。

**注意**  
在配置任何`Shared`信标之前，请考虑您的安全和性能需求。 `Shared`信标可能会增加可以识别的有关数据集分布的统计信息量。例如，它们可能会揭示哪些共享字段包含相同的纯文本值。

**Java**  

```
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
    .name("beacon2")
    .length(beaconLengthInBits)
    .style(
        BeaconStyle.builder()
           .shared(Shared.builder().other("beacon1").build())
        .build()
    )
    .build();
standardBeaconList.add(exampleStandardBeacon);
```

**C\$1 /.NET**  

```
new StandardBeacon
{
    Name = "beacon2",
    Length = beaconLengthInBits,
    Style = new BeaconStyle
    {
        Shared = new Shared { Other = "beacon1" }
    }
}
```

**Rust**  

```
StandardBeacon::builder()
    .name("beacon2")
    .length(beacon_length_in_bits)
    .style(BeaconStyle::Shared(
       Shared::builder().other("beacon1").build()?,
    ))
    .build()?
```

------
#### [ AsSet ]

默认情况下，如果字段值是一个集合，则 AWS 数据库加密 SDK 会计算该集合的单个标准信标。因此，您无法执行查询加密字段`CONTAINS(a, :value)`在`a`哪里。定义为的标准信标`AsSet`计算集合中每个单独元素的单个标准信标值，并将信标值作为集合存储在项目中。这样， AWS 数据库加密 SDK 就可以执行查询`CONTAINS(a, :value)`。

要定义`AsSet`标准信标，集合中的元素必须来自相同的总体，这样它们才能使用相同的[信标长度](choosing-beacon-length.md)。如果在计算信标值时发生冲突，则信标集的元素可能少于纯文本集。

**注意**  
在配置任何`AsSet`信标之前，请考虑您的安全和性能需求。 `AsSet`信标可能会增加可以识别的有关数据集分布的统计信息量。例如，它们可能会显示纯文本集的大小。

**Java**  

```
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
    .name("beaconName")
    .length(beaconLengthInBits)
    .style(
        BeaconStyle.builder()
           .asSet(AsSet.builder().build())
        .build()
    )
    .build();
standardBeaconList.add(exampleStandardBeacon);
```

**C\$1 /.NET**  

```
new StandardBeacon
{
    Name = "beaconName",
    Length = beaconLengthInBits,
    Style = new BeaconStyle
    {
        AsSet = new AsSet()
    }
}
```

**Rust**  

```
StandardBeacon::builder()
    .name("beacon_name")
    .length(beacon_length_in_bits)
    .style(BeaconStyle::AsSet(AsSet::builder().build()?))
    .build()?
```

------
#### [ SharedSet ]

定义为的标准信标`SharedSet`结合了`Shared`和`AsSet`函数，因此您可以对集合和字段的加密值执行相等搜索。这样， AWS 数据库加密 SDK 就可以执行查询，`CONTAINS(a, b)`其中`a`是加密集和`b`加密字段。

**注意**  
在配置任何`Shared`信标之前，请考虑您的安全和性能需求。 `SharedSet`信标可能会增加可以识别的有关数据集分布的统计信息量。例如，它们可能会显示纯文本集的大小或哪些共享字段包含相同的纯文本值。

**Java**  

```
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
    .name("beacon2")
    .length(beaconLengthInBits)
    .style(
        BeaconStyle.builder()
           .sharedSet(SharedSet.builder().other("beacon1").build())
        .build()
    )
    .build();
standardBeaconList.add(exampleStandardBeacon);
```

**C\$1 /.NET**  

```
new StandardBeacon
{
    Name = "beacon2",
    Length = beaconLengthInBits,
    Style = new BeaconStyle
    {
        SharedSet = new SharedSet { Other = "beacon1" }
    }
}
```

**Rust**  

```
StandardBeacon::builder()
    .name("beacon2")
    .length(beacon_length_in_bits)
    .style(BeaconStyle::SharedSet(
        SharedSet::builder().other("beacon1").build()?,
    ))
    .build()?
```

------

## 配置复合信标
<a name="config-compound-beacons"></a>

复合信标组合文字明文字符串和标准信标来执行复杂的数据库操作，例如从单个索引中查询两种不同的记录类型或使用排序键查询字段组合。复合信标可以通过`ENCRYPT_AND_SIGN``SIGN_ONLY`、和`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`字段构建。您必须为复合信标中包含的每个加密字段创建标准信标。

**注意**  
我们建议您在填充数据库之前验证您的复合信标是否产生了预期的结果。有关更多信息，请参阅[测试信标输出](ddb-searchable-encryption.md#ddb-beacon-testing)。

### 示例配置语法
<a name="compound-config-syntax"></a>

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

**复合信标配置**

以下示例在复合信标配置中本地定义加密和签名的部件列表。

```
List<CompoundBeacon> compoundBeaconList = new ArrayList<>();
CompoundBeacon exampleCompoundBeacon = CompoundBeacon.builder()
    .name("compoundBeaconName")
    .split(".")
    .encrypted(encryptedPartList) 
    .signed(signedPartList)                       
    .constructors(constructorList) 
    .build();
compoundBeaconList.add(exampleCompoundBeacon);
```

**信标版本定义**

以下示例在信标版本中全局定义了加密和已签名的部件列表。有关定义信标版本的更多信息，请参阅[使用信标。](using-beacons.md)

```
 List<BeaconVersion> beaconVersions = new ArrayList<>();
beaconVersions.add(
    BeaconVersion.builder()
        .standardBeacons(standardBeaconList)
        .compoundBeacons(compoundBeaconList)
        .encryptedParts(encryptedPartList)
        .signedParts(signedPartList)
        .version(1) // MUST be 1
        .keyStore(keyStore)
        .keySource(BeaconKeySource.builder()
            .single(SingleKeyStore.builder()
                .keyId(branchKeyId)
                .cacheTTL(6000)
                .build())
            .build())
        .build()
);
```

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

**查看完整的代码示例**：[BeaconConfig.cs](https://github.com/aws/aws-database-encryption-sdk-dynamodb/tree/main/Examples/runtimes/net/src/searchableencryption/complexexample/BeaconConfig.cs)

**复合信标配置**

以下示例在复合信标配置中本地定义加密和签名的部件列表。

```
var compoundBeaconList = new List<CompoundBeacon>();       
var exampleCompoundBeacon = new CompoundBeacon
 {
    Name = "compoundBeaconName",
    Split = ".",
    Encrypted = encryptedPartList,
    Signed = signedPartList,                        
    Constructors = constructorList 
 };
compoundBeaconList.Add(exampleCompoundBeacon);
```

**信标版本定义**

以下示例在信标版本中全局定义了加密和已签名的部件列表。有关定义信标版本的更多信息，请参阅[使用信标。](using-beacons.md)

```
var beaconVersions = new List<BeaconVersion>
{
    new BeaconVersion
    {
        StandardBeacons = standardBeaconList,
        CompoundBeacons = compoundBeaconList,
        EncryptedParts = encryptedPartsList,
        SignedParts = signedPartsList,
        Version = 1, // MUST be 1
        KeyStore = keyStore,
        KeySource = new BeaconKeySource
        {
            Single = new SingleKeyStore
            {
                KeyId = branchKeyId,
                CacheTTL = 6000
            }
        }
    }
};
```

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

**查看完整的代码示例**：[beacon](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/releases/rust/db_esdk/examples/searchableencryption/complexexample/beacon_config.rs) \$1config.rs

**复合信标配置**

以下示例在复合信标配置中本地定义加密和签名的部件列表。

```
let compound_beacon_list = vec![
    CompoundBeacon::builder()
        .name("compound_beacon_name")
        .split(".")
        .encrypted(encrypted_parts_list)
        .signed(signed_parts_list)
        .constructors(constructor_list)
        .build()?
```

**信标版本定义**

以下示例在信标版本中全局定义了加密和已签名的部件列表。有关定义信标版本的更多信息，请参阅[使用信标。](using-beacons.md)

```
let beacon_versions = BeaconVersion::builder()
    .standard_beacons(standard_beacon_list)
    .compound_beacons(compound_beacon_list)
    .encrypted_parts(encrypted_parts_list)
    .signed_parts(signed_parts_list)
    .version(1) // MUST be 1
    .key_store(key_store.clone())
    .key_source(BeaconKeySource::Single(
        SingleKeyStore::builder()
            .key_id(branch_key_id)
            .cache_ttl(6000)
            .build()?,
    ))
    .build()?;
let beacon_versions = vec![beacon_versions];
```

------

您可以在本地或全局定义的列表中定义[加密部分](#encrypted-parts)[和签](#signed-parts)名部分。我们建议尽可能在[信标版本](using-beacons.md#beacon-version)的全局列表中定义加密和签名的部分。通过全局定义加密和签名的部件，您可以定义每个部分一次，然后在多个复合信标配置中重复使用这些部件。如果您只打算使用一次加密或已签名的部件，则可以在复合信标配置的本地列表中对其进行定义。你可以在[构造函数列表](#constructor-parts)中同时引用局部和全局部分。

如果您在全局范围内定义加密和签名的部件列表，则必须提供构造器部件列表，这些构造器部分标识复合信标可以在复合信标配置中组合字段的所有可能方式。

**注意**  
要全局定义加密和已签名的部件列表，必须使用 3.2 版或更高版本的 AWS 数据库加密 SDK。在全局定义任何新部分之前，先将新版本部署给所有读者。  
您无法更新现有信标配置以全局定义加密和已签名的部件列表。

要配置复合信标，请提供以下值。

**信标名称**  
您在查询已加密字段时使用的名称。  
信标名称可以与加密的字段或虚拟字段相同，但不能与未加密的字段相同。任何两个信标的名称都不能相同。如需帮助确定最适合您的实现的信标名称，请参阅[选择信标名称](choosing-beacon-name.md)。

**分割字符**  
用于分隔构成复合信标的各个部分的字符。  
分割字符不能出现在构成复合信标的任何字段的明文值中。

**加密部件清单**  
标识复合信标中包含的 `ENCRYPT_AND_SIGN` 字段。  
每个部分都必须包含名称和前缀。部分名称必须是根据加密字段构造的标准信标的名称。前缀可以是任何字符串，但它必须是唯一的。加密部分不能与已签名部分的前缀相同。建议使用简短的值，以将该部分与复合信标提供的其他部分区分开来。  
我们建议尽可能在全局范围内定义您的加密部分。如果您只打算在一个复合信标中使用加密部件，则可以考虑在本地定义加密部件。本地定义的加密部分不能与全局定义的加密部分具有相同的前缀或名称。  

```
List<EncryptedPart> encryptedPartList = new ArrayList<>();
EncryptedPart encryptedPartExample = EncryptedPart.builder()
    .name("standardBeaconName")
    .prefix("E-")
    .build();
encryptedPartList.add(encryptedPartExample);
```

```
var encryptedPartList = new List<EncryptedPart>();
var encryptedPartExample = new EncryptedPart
 {
    Name = "compoundBeaconName",
    Prefix = "E-"
 };
encryptedPartList.Add(encryptedPartExample);
```

```
let encrypted_parts_list = vec![
    EncryptedPart::builder()
        .name("standard_beacon_name")
        .prefix("E-")
        .build()?
];
```

**签名零件清单**  
标识复合信标中包含的签名字段。  
签名部分是可选的。您可以配置不引用任何签名部件的复合信标。
每个部分都必须包含名称、来源和前缀。来源是部件标识的`SIGN_ONLY`或`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`字段。来源必须是字段名称或引用嵌套字段值的索引。如果您的部件名称标识了来源，则可以省略来源， AWS 数据库加密 SDK 将自动使用该名称作为其来源。建议尽可能将来源指定为部分名称。前缀可以是任何字符串，但它必须是唯一的。已签名的部分不能与加密的部分具有相同的前缀。建议使用简短的值，以将该部分与复合信标提供的其他部分区分开来。  
我们建议尽可能在全局范围内定义您的签名部件。如果您只打算在一个复合信标中使用签名部件，则可以考虑在本地定义签名部件。本地定义的签名部分不能与全局定义的签名部分具有相同的前缀或名称。  

```
List<SignedPart> signedPartList = new ArrayList<>();
SignedPart signedPartExample = SignedPart.builder()
    .name("signedFieldName")
    .prefix("S-")
    .build();
signedPartList.add(signedPartExample);
```

```
var signedPartsList = new List<SignedPart>
{
    new SignedPart { Name = "signedFieldName1", Prefix = "S-" },
    new SignedPart { Name = "signedFieldName2", Prefix = "SF-" }
};
```

```
let signed_parts_list = vec![
    SignedPart::builder()
        .name("signed_field_name_1")
        .prefix("S-")
        .build()?,
   SignedPart::builder()
        .name("signed_field_name_2")
        .prefix("SF-")
        .build()?,     
];
```

**构造器列表**  
标识定义复合信标汇编加密和签名部分的不同方式的*构造函数*。你可以在构造函数列表中同时引用局部和全局部分。  
如果使用全局定义的加密和签名部分构造复合信标，则必须提供构造函数列表。  
如果您不使用任何全局定义的加密或签名部分来构造复合信标，则构造函数列表是可选的。如果您未指定构造函数列表，则 AWS 数据库加密 SDK 将使用以下默认构造函数组装复合信标。  
+ 所有已签名的部分均按添加到已签名部分列表的顺序排列
+ 所有已加密的部分均按添加到已加密部分列表的顺序排列
+ 所有部分均为必填项  
**构造函数**  
每个构造函数都是*构造函数部分*的有序列表，该列表定义了组合信标的一种汇编方式。构造函数部分按照添加到列表中的顺序连接在一起，其每个部分由指定的分割字符分隔。  
每个构造函数部分都命名加密部分或签名部分，并定义该部分在构造函数中是必填项还是可选项。例如，如果要在 `Field1`、`Field1.Field2` 和 `Field1.Field2.Field3` 上查询复合信标，请将 `Field2` 和 `Field3` 标记为可选并创建一个构造函数。  
每个构造函数必须具有至少一个必需部分。建议将每个构造函数的第一部分设为必填项，这样您就可以在查询中使用 `BEGINS_WITH` 运算符。  
如果一个构造函数的所有必需部分都存在于记录中，则该构造函数成功。当您编写一条新记录时，复合信标使用构造函数列表来确定信标是否可以根据提供的值进行汇编。它尝试按照构造函数添加到构造函数列表的顺序汇编信标，并使用第一个成功的构造函数。如果任何构造函数都没有成功，则信标不会写入记录。  
所有的读取者和写入者都应指定相同的构造函数顺序，以确保其查询结果正确无误。
使用以下过程指定您自己的构造函数列表。  

1. 为每个加密部分和签名部分创建一个构造函数部分，以定义该部分是否为比填项。

   构造函数部分名称必须是它所代表的标准信标或前面字段的名称。

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

   ```
   ConstructorPart field1ConstructorPart = ConstructorPart.builder()
           .name("Field1")
           .required(true)
           .build();
   ```

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

   ```
   var field1ConstructorPart = new ConstructorPart { Name = "Field1", Required = true };
   ```

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

   ```
   let field_1_constructor_part = ConstructorPart::builder()
       .name("field_1")
       .required(true)
       .build()?;
   ```

------

1. 使用您在**步骤 1** 中创建的构造函数部分为汇编复合信标的每种可能方式创建构造函数。

   例如，如果您要查询 `Field1.Field2.Field3` 和 `Field4.Field2.Field3`，则必须创建两个构造函数。`Field1` 和 `Field4` 都可以是必填项，因为它们是在两个单独的构造函数中定义的。

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

   ```
   // Create a list for Field1.Field2.Field3 queries
   List<ConstructorPart> field123ConstructorPartList = new ArrayList<>();
   field123ConstructorPartList.add(field1ConstructorPart);
   field123ConstructorPartList.add(field2ConstructorPart);
   field123ConstructorPartList.add(field3ConstructorPart);
   Constructor field123Constructor = Constructor.builder()
           .parts(field123ConstructorPartList)
           .build();
   // Create a list for Field4.Field2.Field1 queries
   List<ConstructorPart> field421ConstructorPartList = new ArrayList<>();
   field421ConstructorPartList.add(field4ConstructorPart);
   field421ConstructorPartList.add(field2ConstructorPart);
   field421ConstructorPartList.add(field1ConstructorPart);
   Constructor field421Constructor = Constructor.builder()
           .parts(field421ConstructorPartList)
           .build();
   ```

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

   ```
   // Create a list for Field1.Field2.Field3 queries
    var field123ConstructorPartList = new Constructor
   {
       Parts = new List<ConstructorPart> { field1ConstructorPart, field2ConstructorPart, field3ConstructorPart }
   };
   // Create a list for Field4.Field2.Field1 queries        
   var field421ConstructorPartList = new Constructor
   {
       Parts = new List<ConstructorPart> { field4ConstructorPart, field2ConstructorPart, field1ConstructorPart }
   };
   ```

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

   ```
   // Create a list for field1.field2.field3 queries
   let field1_field2_field3_constructor = Constructor::builder()
       .parts(vec![
           field1_constructor_part,
           field2_constroctor_part.clone(),
           field3_constructor_part,
       ])
       .build()?;
   
   // Create a list for field4.field2.field1 queries
   let field4_field2_field1_constructor = Constructor::builder()
       .parts(vec![
           field4_constructor_part,
           field2_constroctor_part.clone(),
           field1_constructor_part,
       ])
       .build()?;
   ```

------

1. 创建一个构造函数列表，其中包含您在**步骤 2** 中创建的所有构造函数。

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

   ```
   List<Constructor> constructorList = new ArrayList<>();
   constructorList.add(field123Constructor);
   constructorList.add(field421Constructor);
   ```

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

   ```
   var constructorList = new List<Constructor>
   {
       field123Constructor,
       field421Constructor
   };
   ```

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

   ```
   let constructor_list = vec![
       field1_field2_field3_constructor,
       field4_field2_field1_constructor,
   ];
   ```

------

1. 指定`constructorList`何时创建复合信标。

# 示例配置
<a name="beacon-config-examples"></a>


****  

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

以下示例演示如何配置标准信标和复合信标。以下配置不提供信标长度。如需帮助确定适合您的配置的信标长度，请参阅[选择信标长度](choosing-beacon-length.md)。

要查看演示如何配置和使用信标的完整代码示例，请参阅上的 aws-database-encryption-sdk-dynamodb 存储库[中的 Java](https://github.com/aws/aws-database-encryption-sdk-dynamodb//tree/main/Examples/runtimes/java/DynamoDbEncryption/src/main/java/software/amazon/cryptography/examples/searchableencryption)、[.NET](https://github.com/aws/aws-database-encryption-sdk-dynamodb/tree/main/Examples/runtimes/net/src/searchableencryption/) 和 [Rust](https://github.com/aws/aws-database-encryption-sdk-dynamodb/blob/main/releases/rust/db_esdk/examples/searchableencryption/) 可搜索加密示例。 GitHub

**Topics**
+ [标准信标](#standard-config-examples)
+ [复合信标](#compound-config-examples)

## 标准信标
<a name="standard-config-examples"></a>

如果要在 `inspector_id_last4` 字段中查询精确匹配项，请使用以下配置创建标准信标。

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

```
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
    .name("inspector_id_last4")
    .length(beaconLengthInBits)
    .build();
standardBeaconList.add(exampleStandardBeacon);
```

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

```
var standardBeaconList = new List<StandardBeacon>>);
StandardBeacon exampleStandardBeacon = new StandardBeacon
  {
    Name = "inspector_id_last4",
    Length = 10
  };
standardBeaconList.Add(exampleStandardBeacon);
```

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

```
let last4_beacon = StandardBeacon::builder()
    .name("inspector_id_last4")
    .length(10)
    .build()?;
                        
let unit_beacon = StandardBeacon::builder().name("unit").length(30).build()?;

let standard_beacon_list = vec![last4_beacon, unit_beacon];
```

------

## 复合信标
<a name="compound-config-examples"></a>

如果要在 `inspector_id_last4` 和 `inspector_id_last4.unit` 上查询 `UnitInspection` 数据库，请使用以下配置创建一个复合信标。此复合信标只需要[加密部分](configure-beacons.md#encrypted-parts)。

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

```
// 1. Create standard beacons for the inspector_id_last4 and unit fields.
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon inspectorBeacon = StandardBeacon.builder()
    .name("inspector_id_last4")
    .length(beaconLengthInBits)
    .build();
standardBeaconList.add(inspectorBeacon);

StandardBeacon unitBeacon = StandardBeacon.builder()
    .name("unit")
    .length(beaconLengthInBits)
    .build();
standardBeaconList.add(unitBeacon);        

// 2. Define the encrypted parts.
List<EncryptedPart> encryptedPartList = new ArrayList<>();

// Each encrypted part needs a name and prefix
// The name must be the name of the standard beacon
// The prefix must be unique
// For this example we use the prefix "I-" for "inspector_id_last4"
// and "U-" for "unit"
EncryptedPart encryptedPartInspector = EncryptedPart.builder()
    .name("inspector_id_last4")
    .prefix("I-")
    .build();
encryptedPartList.add(encryptedPartInspector);

EncryptedPart encryptedPartUnit = EncryptedPart.builder()
    .name("unit")
    .prefix("U-")
    .build();
encryptedPartList.add(encryptedPartUnit);   

// 3. Create the compound beacon.
// This compound beacon only requires a name, split character, 
// and list of encrypted parts
CompoundBeacon inspectorUnitBeacon = CompoundBeacon.builder()
    .name("inspectorUnitBeacon")
    .split(".")
    .sensitive(encryptedPartList)
    .build();
```

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

```
// 1. Create standard beacons for the inspector_id_last4 and unit fields.
StandardBeacon inspectorBeacon = new StandardBeacon
 {
   Name = "inspector_id_last4",
   Length = 10
 };
standardBeaconList.Add(inspectorBeacon);
StandardBeacon unitBeacon = new StandardBeacon
 {
    Name = "unit",
    Length = 30
 };  
standardBeaconList.Add(unitBeacon);
                
// 2. Define the encrypted parts.
var last4EncryptedPart = new EncryptedPart

// Each encrypted part needs a name and prefix
// The name must be the name of the standard beacon
// The prefix must be unique
// For this example we use the prefix "I-" for "inspector_id_last4"
// and "U-" for "unit"
var last4EncryptedPart = new EncryptedPart
 {
   Name = "inspector_id_last4",
   Prefix = "I-"
 };
encryptedPartList.Add(last4EncryptedPart);

var unitEncryptedPart = new EncryptedPart
 {
   Name = "unit",
   Prefix = "U-"
 };
encryptedPartList.Add(unitEncryptedPart); 

// 3. Create the compound beacon.
// This compound beacon only requires a name, split character, 
// and list of encrypted parts
var compoundBeaconList = new List<CompoundBeacon>>);
var inspectorCompoundBeacon = new CompoundBeacon
  {
      Name = "inspector_id_last4",
      Split = ".",
      Encrypted = encryptedPartList
  };
compoundBeaconList.Add(inspectorCompoundBeacon);
```

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

```
// 1. Create standard beacons for the inspector_id_last4 and unit fields.
let last4_beacon = StandardBeacon::builder()
    .name("inspector_id_last4")
    .length(10)
    .build()?;
                        
let unit_beacon = StandardBeacon::builder().name("unit").length(30).build()?;

let standard_beacon_list = vec![last4_beacon, unit_beacon];
                        
// 2. Define the encrypted parts.
// The name must be the name of the standard beacon
// The prefix must be unique
// For this example we use the prefix "I-" for "inspector_id_last4"
// and "U-" for "unit"
let encrypted_parts_list = vec![
    EncryptedPart::builder()
        .name("inspector_id_last4")
        .prefix("I-")
        .build()?,
    EncryptedPart::builder().name("unit").prefix("U-").build()?,
];

// 3. Create the compound beacon
// This compound beacon only requires a name, split character, 
// and list of encrypted parts
let compound_beacon_list = vec![CompoundBeacon::builder()
    .name("last4UnitCompound")
    .split(".")
    .encrypted(encrypted_parts_list)
    .build()?];
```

------

# 使用信标
<a name="using-beacons"></a>


****  

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

信标使您无需解密查询中的整个数据库即可搜索加密的记录。信标旨在在未填充的新数据库中实现。在现有数据库中配置的任何信标将只会映射写入数据库的新记录。信标是根据字段的明文值计算出来的，一旦字段被加密，信标就无法映射现有数据。使用信标写入新记录后，您将无法更新信标的配置。但是，您可以为添加到记录中的新字段添加新信标。

配置信标后，您必须先完成以下步骤，然后才能开始填充数据库并对信标执行查询。

1. **创建 AWS KMS 分层密钥环**

   要使用可搜索的加密，必须使用 [AWS KMS 分层密钥环](use-hierarchical-keyring.md)来生成、加密和解密用于保护记录的[数据密钥](concepts.md#data-key)。

   配置信标后，汇编[分层密钥环先决条件](use-hierarchical-keyring.md#hierarchical-keyring-prereqs)并[创建您的分层密钥环](use-hierarchical-keyring.md#initialize-hierarchical-keyring)。

   有关为什么需要分层密钥环的更多详细信息，请参阅[使用分层密钥环进行可搜索的加密](use-hierarchical-keyring.md#searchable-encryption-hierarchical-keyrings)。

1. 

   **定义信标版本**

   指定您的`keyStore``keySource`、、您配置的所有标准信标的列表、您配置的所有复合信标的列表、加密部分的列表、签名部分的列表和信标版本。必须为信标版本指定 `1`。有关定义您的 `keySource` 的指导，请参阅 [定义您的信标密钥源](use-hierarchical-keyring.md#beacon-key-source)。

   以下 Java 示例定义单租户数据库的信标版本。如需帮助定义多租户数据库的信标版本，请参阅[多租户数据库的可搜索加密](searchable-encryption-multitenant.md)。

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

   ```
    List<BeaconVersion> beaconVersions = new ArrayList<>();
   beaconVersions.add(
       BeaconVersion.builder()
           .standardBeacons(standardBeaconList)
           .compoundBeacons(compoundBeaconList)
           .encryptedParts(encryptedPartsList)
           .signedParts(signedPartsList)
           .version(1) // MUST be 1
           .keyStore(keyStore)
           .keySource(BeaconKeySource.builder()
               .single(SingleKeyStore.builder()
                   .keyId(branchKeyId)
                   .cacheTTL(6000)
                   .build())
               .build())
           .build()
   );
   ```

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

   ```
   var beaconVersions = new List<BeaconVersion>
   {
       new BeaconVersion
       {
           StandardBeacons = standardBeaconList,
           CompoundBeacons = compoundBeaconList,
           EncryptedParts = encryptedPartsList,
           SignedParts = signedPartsList,
           Version = 1, // MUST be 1
           KeyStore = branchKeyStoreName,
           KeySource = new BeaconKeySource
           {
               Single = new SingleKeyStore
               {
                   KeyId = branch-key-id,
                   CacheTTL = 6000
               }
           }
       }
   };
   ```

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

   ```
   let beacon_version = BeaconVersion::builder()
       .standard_beacons(standard_beacon_list)
       .compound_beacons(compound_beacon_list)
       .version(1) // MUST be 1
       .key_store(key_store.clone())
       .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()?,
       ))
       .build()?;
   let beacon_versions = vec![beacon_version];
   ```

------

1. **配置二级索引**

   [配置信标](configure-beacons.md)后，必须先配置反映每个信标的二级索引，然后才能搜索加密的字段。有关更多信息，请参阅 [通过使用信标配置二级索引](ddb-searchable-encryption.md#ddb-beacon-indexes)。

1. **定义您的[加密操作](concepts.md#crypt-actions)**

   必须将用于构造标准信标的所有字段标记为 `ENCRYPT_AND_SIGN`。用于构造信标的所有其他字段都必须标记`SIGN_ONLY`或`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`。

1. **配置 AWS 数据库加密 SDK 客户端**

   要配置保护您的 DynamoDB 表中的表项目的 AWS 数据库加密 SDK 客户端，[请参阅 DynamoDB 的 Java 客户端加密](ddb-java.md)库。

## 查询信标
<a name="querying-beacons"></a>

您配置的信标类型决定了您能够执行的查询类型。标准信标使用筛选条件表达式来执行相等搜索。复合信标则组合文字明文字符串和标准信标来执行复杂的查询。查询加密的数据时，您可以搜索信标名称。

您无法比较两个标准信标的值，即使其中包含相同的底层明文。两个标准信标将为相同的明文值生成两个不同的 HMAC 标签。因此，标准信标无法执行以下查询。
+ `beacon1 = beacon2`
+ `beacon1 IN (beacon2)`
+ `value IN (beacon1, beacon2, ...)`
+ `CONTAINS(beacon1, beacon2)`

复合信标可以执行以下查询。
+ `BEGINS_WITH(a)`，其中 `a` 反映了汇编的复合信标开头的字段的完整值。您不能使用 `BEGINS_WITH` 运算符标识以特定子字符串开头的值。但是，您可以使用 `BEGINS_WITH(S_)`，其中 `S_` 反映了汇编的复合信标开头的部分的前缀。
+ `CONTAINS(a)`，其中 `a` 反映了汇编的复合信标包含的字段的完整值。您不能使用 `CONTAINS` 运算符标识包含特定子字符串或某个集中的值记录。

  例如，您不能执行查询 `CONTAINS(path, "a"`，其中 `a` 反映了某个集中的值。
+ 您可以比较复合信标的[签名部分](configure-beacons.md#signed-parts)。比较签名的部分时，您可以选择将[加密部分](configure-beacons.md#encrypted-parts)的前缀附加到一个或多个签名部分中，但不能在任何查询中包括加密字段的值。

  例如，您可以比较已签名的部分并对 `signedField1 = signedField2` 或 `value IN (signedField1, signedField2, ...)` 进行查询。

  您还可以通过查询 `signedField1.A_ = signedField2.B_` 来比较已签名部分和加密部分的前缀。
+ `field BETWEEN a AND b`，其中 `a` 和 `b` 是签名部分。您可以选择将加密部分的前缀附加到一个或多个签名部分中，但不能在任何查询中包括加密字段的值。

您必须为对复合信标的查询中包含的每个部分添加前缀。例如，如果您从两个字段 `encryptedField` 和 `signedField` 构造了一个复合信标 `compoundBeacon`，则在查询信标时必须包含为这两个部分配置的前缀。

```
compoundBeacon = E_encryptedFieldValue.S_signedFieldValue
```

# 多租户数据库的可搜索加密
<a name="searchable-encryption-multitenant"></a>


****  

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

要在数据库中实现可搜索的加密，必须使用 [AWS KMS 分层密钥环](use-hierarchical-keyring.md)。分 AWS KMS 层密钥环生成、加密和解密用于保护记录的数据密钥。它还创建用于生成信标的信标密钥。在多租户数据库中使用 AWS KMS 分层密钥环时，每个租户都有不同的分支密钥和信标密钥。要查询多租户数据库中的加密数据，必须确定用于生成所查询信标的信标密钥材料。有关更多信息，请参阅 [使用分层密钥环进行可搜索加密](use-hierarchical-keyring.md#searchable-encryption-hierarchical-keyrings)。

在为多租户数据库定义[信标版本](using-beacons.md#beacon-version)时，请指定您配置的所有标准信标的列表、您配置的所有复合信标的列表、信标版本和 `keySource`。您必须[将信标密钥源定义](use-hierarchical-keyring.md#beacon-key-source)为 `MultiKeyStore`，并包括 `keyFieldName`、一个本地信标密钥缓存的缓存生存时间和本地信标密钥缓存的最大缓存大小。

如果您已配置任何[已签名的信标](configure.md#signed-beacons)，则必须将其包含在您的 `compoundBeaconList` 中。签名信标是一种复合信标，用于对和`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`字段进行索引`SIGN_ONLY`和执行复杂查询。

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

```
List<BeaconVersion> beaconVersions = new ArrayList<>();
    beaconVersions.add(
        BeaconVersion.builder()
                .standardBeacons(standardBeaconList)
                .compoundBeacons(compoundBeaconList)
                .version(1) // MUST be 1
                .keyStore(branchKeyStoreName)
                .keySource(BeaconKeySource.builder()
                        .multi(MultiKeyStore.builder()
                                .keyFieldName(keyField)
                                .cacheTTL(6000)
                                .maxCacheSize(10)
                        .build())
                .build())
        .build()
    );
```

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

```
var beaconVersions = new List<BeaconVersion>
{
    new BeaconVersion
    {
        StandardBeacons = standardBeaconList,
        CompoundBeacons = compoundBeaconList,
        EncryptedParts = encryptedPartsList,
        SignedParts = signedPartsList,
        Version = 1, // MUST be 1
        KeyStore = branchKeyStoreName,
        KeySource = new BeaconKeySource
        {
            Multi = new MultiKeyStore
            {
                KeyId = branch-key-id,
                CacheTTL = 6000,
                MaxCacheSize = 10
            }
        }
    }
};
```

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

```
let beacon_version = BeaconVersion::builder()
    .standard_beacons(standard_beacon_list)
    .compound_beacons(compound_beacon_list)
    .version(1) // MUST be 1
    .key_store(key_store.clone())
    .key_source(BeaconKeySource::Multi(
        MultiKeyStore::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)
            .max_cache_size(10)
            .build()?,
    ))
    .build()?;
let beacon_versions = vec![beacon_version];
```

------

**keyFieldName**  
[`keyFieldName`](use-hierarchical-keyring.md#keyFieldName) 定义存储与信标密钥关联的 `branch-key-id` 的字段名称，该信标密钥用于为给定租户生成信标。  
当您将新记录写入数据库时，标识用于为该记录生成任何信标的信标密钥的 `branch-key-id` 将存储在此字段中。  
默认情况下，`keyField` 是不显式存储在数据库中的概念字段。Dat AWS abase Encryption SDK `branch-key-id` 从[材料描述](concepts.md#material-description)中的加密[数据密钥](concepts.md#data-key)中识别出来，并将该值存储在概念中，`keyField`供您在复合信标和[签名信](configure.md#signed-beacons)标中引用。由于材料描述已签名，因此概念 `keyField` 被视为已签名的部分。  
您也可以将作为`SIGN_ONLY`或`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`字段包含`keyField`在加密操作中，以将该字段显式存储在数据库中。如果这样做，则当您每次向数据库写入记录时都必须将 `branch-key-id` 手动包含在 `keyField` 中。

## 查询多租户数据库中的信标
<a name="query-multitenant-beacons"></a>

要查询信标，您必须在查询中包含 `keyField`，以确定重新计算信标所需的相应信标密钥材料。必须指定与用于生成记录信标的信标密钥关联的 `branch-key-id`。您不能在分支密钥 ID 供应程序中指定用于标识租户的 `branch-key-id` 的[易记名称](use-hierarchical-keyring.md#branch-key-id-supplier)。您可以通过以下方式将 `keyField` 包含在查询中。

**复合信标**  
无论您是否明确将 `keyField` 存储在记录中，您都可以将 `keyField` 作为签名部分直接包含在复合信标中。`keyField` 签名部分必须为必填项。  
例如，如果要从两个字段 `encryptedField` 和 `signedField` 中构造复合信标 `compoundBeacon`，则还必须将 `keyField` 作为签名部分包含在内。这使您能够对 `compoundBeacon` 执行以下查询。  

```
compoundBeacon = E_encryptedFieldValue.S_signedFieldValue.K_branch-key-id
```

**签名信标**  
 AWS 数据库加密 SDK 使用标准信标和复合信标来提供可搜索的加密解决方案。这些信标必须至少包括一个加密字段。但是， AWS 数据库加密 SDK 还支持[签名信标](configure.md#signed-beacons)，这些信标可以完全通过纯文本`SIGN_ONLY`和字段进行配置。`SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT`  
签名信标可以由单个部分构造。无论您是否明确将 `keyField` 存储在记录中，您都可以从 `keyField` 中构造签名信标，并使用它来创建复合查询，该查询将对 `keyField` 签名信标的查询与对其他信标的查询组合在一起。例如，您可以执行以下查询。  

```
keyField = K_branch-key-id AND compoundBeacon = E_encryptedFieldValue.S_signedFieldValue
```
如需帮助配置签名信标，请参阅 [创建签名的信标](configure.md#signed-beacons)

**直接在 `keyField` 上查询**  
如果您在加密操作中指定了 `keyField` 并将该字段显式存储在记录中，则可以创建一个复合查询，该查询将对信标的查询与对 `keyField` 的查询组合在一起。如果要查询标准信标，可以选择直接在 `keyField` 上查询。例如，您可以执行以下查询。  

```
keyField = branch-key-id AND standardBeacon = S_standardBeaconValue
```