本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
支持可搜索加密的信标类型有两种。标准信标执行相等搜索。它们是在数据库中实现可搜索加密的最简单方法。复合信标则组合文字明文字符串和标准信标来执行更复杂的查询。
信标旨在在未填充的新数据库中实现。在现有数据库中配置的任何信标将只会映射写入数据库的新记录。信标是根据字段的明文值计算出来的,一旦字段被加密,信标就无法映射现有数据。使用信标写入新记录后,您将无法更新信标的配置。但是,您可以为添加到记录中的新字段添加新信标。
确定访问模式后,配置信标应该是数据库实现的第二步。然后,在配置所有信标之后,您需要创建AWS KMS 分层密钥环、定义信标版本、为每个信标配置二级索引、定义加密操作以及配置数据库和 AWS 数据库加密 SDK 客户端。有关更多信息,请参阅使用信标。
为了更便于定义信标版本,建议为标准信标和复合信标创建列表。在配置信标时,将您创建的每个信标添加到相应的标准或复合信标列表中。
配置标准信标
标准信标是在数据库中实现可搜索加密的最简单方法。他们只能对单个加密字段或虚拟字段执行相等搜索。
示例配置语法
- Java
-
List<StandardBeacon> standardBeaconList = new ArrayList<>();
StandardBeacon exampleStandardBeacon = StandardBeacon.builder()
.name("beaconName
")
.length(beaconLengthInBits)
.build();
standardBeaconList.add(exampleStandardBeacon);
- C# / .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()?,
要配置标准信标,请提供以下值。
- 信标名称
-
您在查询已加密字段时使用的名称。
信标名称可以与加密的字段或虚拟字段相同,但不能与未加密的字段相同。强烈建议尽可能使用构造标准信标所依据的加密字段或虚拟字段的名称。两个不同的信标不能具有相同的信标名称。如需帮助确定最适合您的实现的信标名称,请参阅选择信标名称。
- 信标长度
-
截断后保留的信标哈希值的位数。
信标长度决定了给定信标所产生的误报平均数。要获得确定适合您的实现的信标长度的更多信息和帮助,请参阅确定信标长度。
- 信标源(可选)
-
构造标准信标所依据的字段。
信标源必须是字段名称或引用嵌套字段值的索引。当您的信标名称与信标源相同时,您可以从配置中省略信标源, AWS 数据库加密 SDK 将自动使用该信标名称作为信标源。
创建虚拟字段
要创建虚拟字段,您必须提供虚拟字段的名称和源字段的列表。将源字段添加到虚拟部分列表的顺序决定了连接这些字段以构建虚拟字段的顺序。以下示例将两个源字段完全连接在一起以创建一个虚拟字段。
我们建议您在填充数据库之前验证您的虚拟字段是否产生了预期的结果。有关更多信息,请参阅测试信标输出。
- Java
-
参见完整的代码示例: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# / .NET
-
参见完整的代码示例: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 _beacon_searchable_encryption.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
];
要使用源字段的特定片段创建虚拟字段,必须先定义该转换,然后再将源字段添加到虚拟部分列表中。
虚拟字段的安全考虑因素
信标不会改变字段的加密状态。但是,当您使用信标时,查询的效率和泄露的有关数据分布的信息量之间存在内在权衡。您配置信标的方式决定了该信标所确保的安全级别。
避免利用与现有标准信标重叠的源字段创建虚拟字段。创建包含源字段(已用于创建标准信标)的虚拟字段可能会降低两个信标的安全级别。安全性的降低程度取决于其他源字段所添加的熵水平。熵水平由附加源字段中唯一值的分布以及附加源字段占虚拟字段整体大小的位数决定。
您可以使用总量和信标长度来确定虚拟字段的源字段是否确保了数据集的安全性。总量是字段中唯一值的预期数量。您的总量不需要很精确。如需帮助估算某个字段的总量,请参阅估算总量。
查看虚拟字段的安全性时,请考虑以下示例。
如果以下语句为真,则 Beacon2 将保留 Beacon1 和 Beacon2 的安全性:
N ≥ (Beacon1 length)/2
以及
N ≥ (Beacon2 length)/2
定义信标样式
标准信标可用于对加密或虚拟字段执行相等搜索。或者,它们可以用来构造复合信标以执行更复杂的数据库操作。为了帮助您组织和管理标准信标, AWS 数据库加密 SDK 提供了以下可选信标样式,用于定义标准信标的预期用途。
要定义信标样式,必须使用 3.2 版或更高版本的 AWS 数据库加密 SDK。在向信标配置中添加信标样式之前,请将新版本部署给所有读者。
- PartOnly
-
定义为的标准信标PartOnly
只能用于定义复合信标的加密部分。您不能直接查询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# /.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# /.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
标准信标,集合中的元素必须来自相同的总体,这样它们才能使用相同的信标长度。如果在计算信标值时发生冲突,则信标集的元素可能少于纯文本集。
在配置任何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# /.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# /.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()?
配置复合信标
复合信标组合文字明文字符串和标准信标来执行复杂的数据库操作,例如从单个索引中查询两种不同的记录类型或使用排序键查询字段组合。复合信标可以通过ENCRYPT_AND_SIGN
SIGN_ONLY
、和SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT
字段构建。您必须为复合信标中包含的每个加密字段创建标准信标。
我们建议您在填充数据库之前验证您的复合信标是否产生了预期的结果。有关更多信息,请参阅测试信标输出。
示例配置语法
- Java
-
复合信标配置
以下示例在复合信标配置中本地定义加密和签名的部件列表。
List<CompoundBeacon> compoundBeaconList = new ArrayList<>();
CompoundBeacon exampleCompoundBeacon = CompoundBeacon.builder()
.name("compoundBeaconName
")
.split(".
")
.encrypted(encryptedPartList)
.signed(signedPartList)
.constructors(constructorList)
.build();
compoundBeaconList.add(exampleCompoundBeacon);
信标版本定义
以下示例在信标版本中全局定义了加密和已签名的部件列表。有关定义信标版本的更多信息,请参阅使用信标。
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# / .NET
-
查看完整的代码示例:BeaconConfig.cs
复合信标配置
以下示例在复合信标配置中本地定义加密和签名的部件列表。
var compoundBeaconList = new List<CompoundBeacon>();
var exampleCompoundBeacon = new CompoundBeacon
{
Name = "compoundBeaconName
",
Split = ".
",
Encrypted = encryptedPartList,
Signed = signedPartList,
Constructors = constructorList
};
compoundBeaconList.Add(exampleCompoundBeacon);
信标版本定义
以下示例在信标版本中全局定义了加密和已签名的部件列表。有关定义信标版本的更多信息,请参阅使用信标。
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 _config.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()?
信标版本定义
以下示例在信标版本中全局定义了加密和已签名的部件列表。有关定义信标版本的更多信息,请参阅使用信标。
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];
您可以在本地或全局定义的列表中定义加密部分和签名部分。我们建议尽可能在信标版本的全局列表中定义加密和签名的部分。通过全局定义加密和签名的部件,您可以定义每个部分一次,然后在多个复合信标配置中重复使用这些部件。如果您只打算使用一次加密或已签名的部件,则可以在复合信标配置的本地列表中对其进行定义。你可以在构造函数列表中同时引用局部和全局部分。
如果您在全局范围内定义加密和签名的部件列表,则必须提供构造器部件列表,这些构造器部分标识复合信标可以在复合信标配置中组合字段的所有可能方式。
要全局定义加密和已签名的部件列表,必须使用 3.2 版或更高版本的 AWS 数据库加密 SDK。在全局定义任何新部分之前,先将新版本部署给所有读者。
您无法更新现有信标配置以全局定义加密和已签名的部件列表。
要配置复合信标,请提供以下值。
- 信标名称
-
您在查询已加密字段时使用的名称。
信标名称可以与加密的字段或虚拟字段相同,但不能与未加密的字段相同。任何两个信标的名称都不能相同。如需帮助确定最适合您的实现的信标名称,请参阅选择信标名称。
- 分割字符
-
用于分隔构成复合信标的各个部分的字符。
分割字符不能出现在构成复合信标的任何字段的明文值中。
- 加密部件清单
-
标识复合信标中包含的 ENCRYPT_AND_SIGN
字段。
每个部分都必须包含名称和前缀。部分名称必须是根据加密字段构造的标准信标的名称。前缀可以是任何字符串,但它必须是唯一的。加密部分不能与已签名部分的前缀相同。建议使用简短的值,以将该部分与复合信标提供的其他部分区分开来。
我们建议尽可能在全局范围内定义您的加密部分。如果您只打算在一个复合信标中使用加密部件,则可以考虑在本地定义加密部件。本地定义的加密部分不能与全局定义的加密部分具有相同的前缀或名称。
- Java
-
List<EncryptedPart> encryptedPartList = new ArrayList<>);
EncryptedPart encryptedPartExample = EncryptedPart.builder()
.name("standardBeaconName
")
.prefix("E-")
.build();
encryptedPartList.add(encryptedPartExample);
- C# / .NET
-
var encryptedPartList = new List<EncryptedPart>();
var encryptedPartExample = new EncryptedPart
{
Name = "compoundBeaconName
",
Prefix = "E-"
};
encryptedPartList.Add(encryptedPartExample);
- Rust
-
let encrypted_parts_list = vec![
EncryptedPart::builder()
.name("standard_beacon_name
")
.prefix("E-")
.build()?
];
- 签名零件清单
-
标识复合信标中包含的签名字段。
签名部分是可选的。您可以配置不引用任何签名部件的复合信标。
每个部分都必须包含名称、来源和前缀。来源是部件标识的SIGN_ONLY
或SIGN_AND_INCLUDE_IN_ENCRYPTION_CONTEXT
字段。来源必须是字段名称或引用嵌套字段值的索引。如果您的部件名称标识了来源,则可以省略来源, AWS 数据库加密 SDK 将自动使用该名称作为其来源。建议尽可能将来源指定为部分名称。前缀可以是任何字符串,但它必须是唯一的。已签名的部分不能与加密的部分具有相同的前缀。建议使用简短的值,以将该部分与复合信标提供的其他部分区分开来。
我们建议尽可能在全局范围内定义您的签名部件。如果您只打算在一个复合信标中使用签名部件,则可以考虑在本地定义签名部件。本地定义的签名部分不能与全局定义的签名部分具有相同的前缀或名称。
- Java
-
List<SignedPart> signedPartList = new ArrayList<>);
SignedPart signedPartExample = SignedPart.builder()
.name("signedFieldName
")
.prefix("S-")
.build();
signedPartList.add(signedPartExample);
- C# / .NET
-
var signedPartsList = new List<SignedPart>
{
new SignedPart { Name = "signedFieldName1
", Prefix = "S-" },
new SignedPart { Name = "signedFieldName2
", Prefix = "SF-" }
};
- Rust
-
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
运算符。
如果一个构造函数的所有必需部分都存在于记录中,则该构造函数成功。当您编写一条新记录时,复合信标使用构造函数列表来确定信标是否可以根据提供的值进行汇编。它尝试按照构造函数添加到构造函数列表的顺序汇编信标,并使用第一个成功的构造函数。如果任何构造函数都没有成功,则信标不会写入记录。
所有的读取者和写入者都应指定相同的构造函数顺序,以确保其查询结果正确无误。
使用以下过程指定您自己的构造函数列表。
-
为每个加密部分和签名部分创建一个构造函数部分,以定义该部分是否为比填项。
构造函数部分名称必须是它所代表的标准信标或前面字段的名称。
- Java
-
ConstructorPart field1ConstructorPart = ConstructorPart.builder()
.name("Field1
")
.required(true
)
.build();
- C# / .NET
-
var field1ConstructorPart = new ConstructorPart { Name = "Field1
", Required = true
};
- Rust
-
let field_1_constructor_part = ConstructorPart::builder()
.name("field_1
")
.required(true
)
.build()?;
-
使用您在步骤 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# / .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()?;
-
创建一个构造函数列表,其中包含您在步骤 2 中创建的所有构造函数。
- Java
-
List<Constructor> constructorList = new ArrayList<>();
constructorList.add(field123Constructor)
constructorList.add(field421Constructor)
- C# / .NET
-
var constructorList = new List<Constructor>
{
field123Constructor,
field421Constructor
};
- Rust
-
let constructor_list = vec![
field1_field2_field3_constructor,
field4_field2_field1_constructor,
];
-
指定constructorList
何时创建复合信标。