AWS KMS 分层钥匙圈 - AWS Encryption SDK

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

AWS KMS 分层钥匙圈

重要

只有版本 4 支持 AWS KMS 分层密钥环。 .NET AWS Encryption SDK 的 x 和版本 3。 的 x 个 AWS Encryption SDK for Java。

使用 AWS KMS 分层密钥环,您可以在对称加密 KMS 密钥下保护您的加密材料,而无需在 AWS KMS 每次加密或解密数据时都调用。对于需要最大程度地减少对 AWS KMS的调用的应用程序,以及能够在不违反应用程序的安全性要求的情况下重复使用某些加密材料的应用程序,这是个很好的选择。

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

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

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

本地缓存由两个分区组成,一个用于加密操作,另一个用于解密操作。加密分区存储从活动分支密钥中收集的分支密钥材料,并在缓存限制到期之前将其重复用于所有加密操作。解密分区存储为在解密操作中标识的其他分支密钥版本汇编的分支密钥材料。解密分区可以一次存储多个活动分支密钥材料版本。若解密分区配置为在多租户环境中使用分支密钥 ID 提供程序,其还可一次存储多个分支密钥材料版本。有关更多信息,请参阅 在多租户环境中使用分层密钥环

注意

中所有提及分层密钥环的 AWS Encryption SDK 内容均指 AWS KMS 分层密钥环。

工作方式

以下演练描述了分层密钥环如何汇编加密和解密材料,以及密钥环对加密和解密操作的不同调用。有关包装密钥派生和明文数据密钥加密过程的技术详细信息,请参阅 AWS KMS 分层密钥环技术详细信息

加密并签名

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

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

  2. 如果不存在有效分支密钥材料,则分层密钥环会在分支密钥存储中查询活动分支密钥。

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

    2. 分支密钥存储返回明文分支密钥和标识该密钥的数据,例如分支密钥版本。

  3. 分层密钥环汇编分支密钥材料(明文分支密钥和分支密钥版本),并将其副本存储在本地缓存中。

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

此加密方法使用加密材料加密数据。有关更多信息,请参阅 AWS Encryption SDK 如何加密数据

解密并验证

以下演练描述了分层密钥环如何组装解密材料并解密加密数据密钥。

  1. 该解密方法标识来自加密消息的加密数据密钥,并将其传递给分层密钥环。

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

    有关更多信息,请参阅 AWS KMS 分层钥匙圈技术细节

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

  4. 如果不存在有效分支密钥材料,则分层密钥环会在分支密钥存储中查询与步骤 2 标识的分支密钥版本相匹配的分支密钥。

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

    2. 分支密钥存储返回明文分支密钥和标识该密钥的数据,例如分支密钥版本。

  5. 分层密钥环汇编分支密钥材料(明文分支密钥和分支密钥版本),并将其副本存储在本地缓存中。

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

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

该解密方法使用解密材料和明文数据密钥解密加密消息。有关更多信息,请参阅如何 AWS Encryption SDK 解密加密邮件

先决条件

AWS Encryption SDK 不需要 AWS 账户 ,也不依赖于任何一个 AWS 服务。但是,分层密钥环依赖于 Amazon Dyn AWS KMS amoDB。

要使用分层密钥环,您需要使用具有 KMS: Dec r ypt 权限的对称 AWS KMS key 加密。您也可以使用对称加密多区域密钥。有关权限的详细信息 AWS KMS keys,请参阅《AWS Key Management Service 开发人员指南》中的身份验证和访问控制

在创建和使用分层密钥环之前,必须创建分支密钥存储并使用首个活动分支密钥填充该密钥。

步骤 1:配置新的密钥存储服务

密钥存储服务提供多个 API 操作(例如 CreateKeyStoreCreateKey),可帮助您组装分层密钥环先决条件并管理分支密钥存储。

以下示例创建了密钥存储服务。您必须指定 DynamoDB 表名作为分支密钥存储的名称、分支密钥存储的逻辑名称以及用于标识保护分支密钥的 KMS 密钥的 KMS 密钥 ARN。

为简化 DynamoDB 还原操作,逻辑密钥存储名称以加密方式绑定到表中存储的所有数据。逻辑密钥存储名称可以与 DynamoDB 表名相同,但不必相同。我们建议您在首次配置密钥存储服务时将 DynamoDB 表名指定为逻辑表名。必须始终指定相同的逻辑表名。如果从备份中恢复 DynamoDB 表之后分支密钥存储名称发生变化,逻辑密钥存储名称则将映射到您指定的 DynamoDB 表名称,以此确保分层密钥环仍可访问您的分支密钥存储。

注意

逻辑密钥存储名称包含在所有调用 AWS KMS的密钥存储服务 API 操作的加密上下文中。加密上下文不是秘密的,其值(包括逻辑密钥存储名称)以纯文本形式出现在日志中。 AWS CloudTrail

C# / .NET
var kmsConfig = new KMSConfiguration { KmsKeyArn = kmsKeyArn }; var keystoreConfig = new KeyStoreConfig { KmsClient = new AmazonKeyManagementServiceClient(), KmsConfiguration = kmsConfig, DdbTableName = keyStoreName, DdbClient = new AmazonDynamoDBClient(), LogicalKeyStoreName = logicalKeyStoreName }; var keystore = new KeyStore(keystoreConfig);
Java
final KeyStore keystore = KeyStore.builder().KeyStoreConfig( KeyStoreConfig.builder() .ddbClient(DynamoDbClient.create()) .ddbTableName(keyStoreName) .logicalKeyStoreName(logicalKeyStoreName) .kmsClient(KmsClient.create()) .kmsConfiguration(KMSConfiguration.builder() .kmsKeyArn(kmsKeyArn) .build()) .build()).build();
步骤 2:调用 CreateKeyStore 创建分支密钥存储

以下操作创建分支密钥库,该分支密钥存储库将保留并保护您的分支密钥。

C# / .NET
var createKeyStoreOutput = keystore.CreateKeyStore(new CreateKeyStoreInput());
Java
keystore.CreateKeyStore(CreateKeyStoreInput.builder().build());

CreateKeyStore 操作使用您在步骤 1 指定的表名和以下必需值创建 DynamoDB 表。

分区键 排序键
基表 branch-key-id type
注意

您可以手动创建用作分支密钥存储的 DynamoDB 表,而不必使用该操作。CreateKeyStore如果您选择手动创建分支密钥存储,则必须为分区和排序键指定以下字符串值:

  • 分区键:branch-key-id

  • 排序键:type

步骤 3:调用 CreateKey 创建新的活动分支密钥

以下操作使用您在步骤 1 中指定的 KMS 密钥创建新的活动分支密钥,并将活动分支密钥添加到您在步骤 2 中创建的 DynamoDB 表中。

调用 CreateKey 时,您可以选择指定以下可选值。

C# / .NET
var additionalEncryptionContext = new Dictionary<string, string>(); additionalEncryptionContext.Add("Additional Encryption Context for", "custom branch key id"); var branchKeyId = keystore.CreateKey(new CreateKeyInput { BranchKeyIdentifier = "custom-branch-key-id", // OPTIONAL EncryptionContext = additionalEncryptionContext // OPTIONAL });
Java
final Map<String, String> additionalEncryptionContext = Collections.singletonMap("Additional Encryption Context for", "custom branch key id"); final String BranchKey = keystore.CreateKey( CreateKeyInput.builder() .branchKeyIdentifier(custom-branch-key-id) //OPTIONAL .encryptionContext(additionalEncryptionContext) //OPTIONAL .build()).branchKeyIdentifier();

首先,CreateKey 操作生成以下值。

  • 适用于 branch-key-id 的版本 4 通用唯一标识(UUID)(除非您指定了自定义 branch-key-id)。

  • 适用于分支密钥版本的版本 4 UUID

  • timestamp 必须采用协调世界时(UTC)ISO 8601 日期和时间格式

然后,该CreateKey操作GenerateDataKeyWithoutPlaintext使用以下请求调用 kms:

{ "EncryptionContext": { "branch-key-id" : "branch-key-id", "type" : "type", "create-time" : "timestamp", "logical-key-store-name" : "the logical table name for your branch key store", "kms-arn" : the KMS key ARN, "hierarchy-version" : "1", "aws-crypto-ec:contextKey": "contextValue" }, "KeyId": "the KMS key ARN you specified in Step 1", "NumberOfBytes": "32" }

接下来,该CreateKey操作调用 km ReEncrypt s:,通过更新加密上下文为分支密钥创建活动记录。

最后,该CreateKey操作调用 ddb: TransactWriteItems 来编写一个新项目,该项目将保留您在步骤 2 中创建的表中的分支密钥。项目具有以下属性。

{ "branch-key-id" : branch-key-id, "type" : "branch:ACTIVE", "enc" : the branch key returned by the GenerateDataKeyWithoutPlaintext call, "version": "branch:version:the branch key version UUID", "create-time" : "timestamp", "kms-arn" : "the KMS key ARN you specified in Step 1", "hierarchy-version" : "1", "aws-crypto-ec:contextKey": "contextValue" }

创建分层密钥环

要初始化分层密钥环,必须提供以下值:

  • 分支密钥存储名称

    您创建的用作分支密钥存储的 DynamoDB 表的名称。

  • 缓存限制生存时间(TTL)

    本地缓存中的分支密钥材料条目在过期之前可使用的时长(以秒为单位)。该值必须大于零。当缓存限制 TTL 到期时,该条目将从本地缓存中移出。

  • 分支密钥标识符

    标识分支密钥存储中的活动分支密钥的 branch-key-id

    注意

    要初始化多租户使用的分层密钥环,必须指定分支密钥 ID 提供程序而非 branch-key-id。有关更多信息,请参阅 在多租户环境中使用分层密钥环

  • (可选)缓存

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

    缓存类型定义了线程模型。分层密钥环提供三种支持多租户环境的缓存类型:默认、、 MultiThreaded。 StormTracking

    如果未指定缓存,则分层密钥环会自动使用默认缓存类型并将条目容量设置为 1000。

    Default (Recommended)

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

    要使用默认缓存初始化分层密钥环,请指定以下值:

    • 条目容量:限制可以存储在本地缓存中的分支密钥材料条目的数量。

    C# /.NET
    CacheType defaultCache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} };
    Java
    .cache(CacheType.builder() .Default(DefaultCache.builder() .entryCapacity(100) .build())

    默认值和 StormTracking 缓存支持相同的线程模型,但您只需要指定入口容量即可使用默认缓存初始化分层密钥环。要进行更精细的缓存自定义,请使用缓存。 StormTracking

    MultiThreaded

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

    要使用 MultiThreaded 缓存初始化分层密钥环,请指定以下值:

    • 条目容量:限制可以存储在本地缓存中的分支密钥材料条目的数量。

    • 条目修剪尾部大小:定义在达到条目容量时要修剪的条目数量。

    C# /.NET
    CacheType multithreadedCache = new CacheType { MultiThreaded = new MultiThreadedCache { EntryCapacity = 100, EntryPruningTailSize = 1 } };
    Java
    .cache(CacheType.builder() .MultiThreaded(MultiThreadedCache.builder() .entryCapacity(100) .entryPruningTailSize(1) .build())
    StormTracking

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

    要使用 StormTracking 缓存初始化分层密钥环,请指定以下值:

    • 条目容量:限制可以存储在本地缓存中的分支密钥材料条目的数量。

    • 条目修剪尾部大小:定义一次要修剪的分支密钥材料条目的数量。

      默认值:1 个条目

    • 宽限期:定义在到期前尝试刷新分支密钥材料的秒数。

      默认值:10 秒

    • 宽限间隔:定义两次尝试刷新分支密钥材料间隔的秒数。

      默认值:1 秒

    • 扇出:定义可以同时尝试刷新分支密钥材料的次数。

      默认值:20 次尝试

    • 传输中生存时间(TTL):定义在分支密钥材料刷新尝试超时之前的秒数。每当缓存为响应 GetCacheEntry 而返回 NoSuchEntry 时,分支密钥均视为传输中,直至相同密钥与 PutCache 条目一起写入。

      默认值:20 秒

    • 睡眠:定义超过 fanOut 时线程应睡眠的秒数。

      默认值:20 毫秒

    C# /.NET
    CacheType stormTrackingCache = new CacheType { StormTracking = new StormTrackingCache { EntryCapacity = 100, EntryPruningTailSize = 1, FanOut = 20, GraceInterval = 1, GracePeriod = 10, InFlightTTL = 20, SleepMilli = 20 } };
    Java
    .cache(CacheType.builder() .MultiThreaded(MultiThreadedCache.builder() .entryCapacity(100) .entryPruningTailSize(1) .gracePeriod(10) .graceInterval(1) .fanOut(20) .inFlightTTL(20) .sleepMilli(20) .build())
  • (可选)授权令牌列表

    如果您通过授权控制对分层密钥环中 KMS 密钥的访问权限,则必须在初始化密钥环时提供所有必要的授权令牌。

以下示例初始化分层密钥环,缓存限制 TLL 为 600 秒,条目容量为 1000。

C# / .NET
// Instantiate the AWS Encryption SDK and material providers var mpl = new MaterialProviders(new MaterialProvidersConfig()); var esdk = new ESDK(new AwsEncryptionSdkConfig()); // Instantiate the keyring var createKeyringInput = new CreateAwsKmsHierarchicalKeyringInput { KeyStore = branchKeyStoreName, BranchKeyId = branch-key-id, Cache = new CacheType { Default = new DefaultCache{EntryCapacity = 1000} }, TtlSeconds = 600 };
Java
final MaterialProviders matProv = MaterialProviders.builder() .MaterialProvidersConfig(MaterialProvidersConfig.builder().build()) .build(); final CreateAwsKmsHierarchicalKeyringInput keyringInput = CreateAwsKmsHierarchicalKeyringInput.builder() .keyStore(branchKeyStoreName) .branchKeyId(branch-key-id) .ttlSeconds(600) .cache(CacheType.builder() //OPTIONAL .Default(DefaultCache.builder() .entryCapacity(1000) .build()) .build(); final Keyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);

轮换您的活动分支密钥

每个分支密钥一次仅能有一个活动版本。分层密钥环通常使用各活动分支密钥版本满足多个请求。但是您可以控制活动分支密钥的重复使用程度,并确定活动分支密钥的轮换频率。

分支密钥不用于加密明文数据密钥。它们用于派生对明文数据密钥进行加密的唯一包装密钥。包装密钥派生过程生成唯一的 32 字节包装密钥,其随机掩码为 28 字节。这意味着,在发生加密损耗之前,分支密钥可以派生出超过 79 万亿或 296 个唯一的包装密钥。尽管耗尽风险非常低,但由于业务或合同规则或政府法规,您可能需要轮换活动分支密钥。

在您轮换之前,分支密钥的活动版本会一直处于活动状态。活动分支密钥的先前版本既不会用于执行加密操作,也不能用于派生新的包装密钥。但是其仍可查询,并为解密其在活动状态下加密的数据密钥提供包装密钥。

使用密钥存储服务 VersionKey 操作轮换您的活动分支密钥。轮换活动分支密钥时,系统会创建新的分支密钥代替先前版本。当您轮换活动分支密钥时,branch-key-id 不会改变。在调用 branch-key-id 时,必须指定用于标识当前活动分支密钥的 VersionKey

C# / .NET
keystore.VersionKey(new VersionKeyInput{BranchKeyIdentifier = branchKeyId});
Java
keystore.VersionKey( VersionKeyInput.builder() .branchKeyIdentifier("branch-key-id") .build() );

在多租户环境中使用分层密钥环

通过为环境中的每个租户创建分支密钥,您可以使用在活动分支密钥及其派生包装密钥之间建立的密钥层次结构来支持多租户环境。然后,分层密钥环使用其不同分支密钥加密给定租户的所有数据。这样,您就可以按分支密钥隔离租户数据。

每个租户均有自己的分支密钥,该密钥由唯一的 branch-key-id 定义。每个 branch-key-id 一次仅能有一个活动版本。

在初始化分层密钥环以供多租户使用之前,必须为每个租户创建分支密钥并创建分支密钥 ID 提供程序。使用分支密钥 ID 提供程序为您的 branch-key-ids 创建易记名称,以便更轻松地识别租户的正确 branch-key-id。例如,易记名称使您可以将分支密钥引用为 tenant1 而非 b3f61619-4d35-48ad-a275-050f87e15122

对于解密操作,您可以静态配置单个分层密钥环以限制对单个租户进行解密,也可以使用分支密钥 ID 提供程序确定哪个租户负责解密消息。

首先,按照先决条件程序的步骤 1 和步骤 2 进行操作。然后,参照以下过程为每个租户创建分支密钥,创建分支密钥 ID 提供程序,并初始化分层密钥环以供多租户使用。

步骤 1:为环境中的每个租户创建分支密钥

为每个租户调用 CreateKey

以下操作使用您在创建密钥存储服务时指定的 KMS 密钥创建两个分支密钥,并将分支密钥添加到您创建的用作分支密钥存储的 DynamoDB 表中。必须使用相同 KMS 密钥保护所有分支密钥。

C# / .NET
var branchKeyId1 = keystore.CreateKey(new CreateKeyInput()); var branchKeyId2 = keystore.CreateKey(new CreateKeyInput());
Java
CreateKeyOutput branchKeyId1 = keystore.CreateKey(CreateKeyInput.builder().build()); CreateKeyOutput branchKeyId2 = keystore.CreateKey(CreateKeyInput.builder().build());
步骤 2:创建分支密钥 ID 提供程序

以下示例创建了分支密钥 ID 供应商。

C# / .NET
var branchKeySupplier = new ExampleBranchKeySupplier(branchKeyId1.BranchKeyIdentifier, branchKeyId2.BranchKeyIdentifier);
Java
IBranchKeyIdSupplier branchKeyIdSupplier = new ExampleBranchKeyIdSupplier( branchKeyId1.branchKeyIdentifier(), branchKeyId2.branchKeyIdentifier());
步骤 3:使用分支密钥 ID 提供程序初始化分层密钥环

要初始化分层密钥环,必须提供以下值:

  • 分支密钥存储名称

  • 缓存限制生存时间(TTL)

  • 分支密钥 ID 提供程序

  • (可选)缓存

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

    缓存类型定义了线程模型。分层密钥环提供三种支持多租户环境的缓存类型:默认、、 MultiThreaded。 StormTracking

    如果未指定缓存,则分层密钥环会自动使用默认缓存类型并将条目容量设置为 1000。

    Default (Recommended)

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

    要使用默认缓存初始化分层密钥环,请指定以下值:

    • 条目容量:限制可以存储在本地缓存中的分支密钥材料条目的数量。

    C# /.NET
    CacheType defaultCache = new CacheType { Default = new DefaultCache{EntryCapacity = 100} };
    Java
    .cache(CacheType.builder() .Default(DefaultCache.builder() .entryCapacity(100) .build())

    默认值和 StormTracking 缓存支持相同的线程模型,但您只需要指定入口容量即可使用默认缓存初始化分层密钥环。要进行更精细的缓存自定义,请使用缓存。 StormTracking

    MultiThreaded

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

    要使用 MultiThreaded 缓存初始化分层密钥环,请指定以下值:

    • 条目容量:限制可以存储在本地缓存中的分支密钥材料条目的数量。

    • 条目修剪尾部大小:定义在达到条目容量时要修剪的条目数量。

    C# /.NET
    CacheType multithreadedCache = new CacheType { MultiThreaded = new MultiThreadedCache { EntryCapacity = 100, EntryPruningTailSize = 1 } };
    Java
    .cache(CacheType.builder() .MultiThreaded(MultiThreadedCache.builder() .entryCapacity(100) .entryPruningTailSize(1) .build())
    StormTracking

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

    要使用 StormTracking 缓存初始化分层密钥环,请指定以下值:

    • 条目容量:限制可以存储在本地缓存中的分支密钥材料条目的数量。

    • 条目修剪尾部大小:定义一次要修剪的分支密钥材料条目的数量。

      默认值:1 个条目

    • 宽限期:定义在到期前尝试刷新分支密钥材料的秒数。

      默认值:10 秒

    • 宽限间隔:定义两次尝试刷新分支密钥材料间隔的秒数。

      默认值:1 秒

    • 扇出:定义可以同时尝试刷新分支密钥材料的次数。

      默认值:20 次尝试

    • 传输中生存时间(TTL):定义在分支密钥材料刷新尝试超时之前的秒数。每当缓存为响应 GetCacheEntry 而返回 NoSuchEntry 时,分支密钥均视为传输中,直至相同密钥与 PutCache 条目一起写入。

      默认值:20 秒

    • 睡眠:定义超过 fanOut 时线程应睡眠的秒数。

      默认值:20 毫秒

    C# /.NET
    CacheType stormTrackingCache = new CacheType { StormTracking = new StormTrackingCache { EntryCapacity = 100, EntryPruningTailSize = 1, FanOut = 20, GraceInterval = 1, GracePeriod = 10, InFlightTTL = 20, SleepMilli = 20 } };
    Java
    .cache(CacheType.builder() .MultiThreaded(MultiThreadedCache.builder() .entryCapacity(100) .entryPruningTailSize(1) .gracePeriod(10) .graceInterval(1) .fanOut(20) .inFlightTTL(20) .sleepMilli(20) .build())
  • (可选)授权令牌列表

    如果您通过授权控制对分层密钥环中 KMS 密钥的访问权限,则必须在初始化密钥环时提供所有必要的授权令牌。

以下示例使用步骤 2 中创建的分支密钥 ID 供应商初始化分层密钥环,缓存限制 TLL 为 600 秒,条目容量为 1000。

C# / .NET
var createKeyringInput = new CreateAwsKmsHierarchicalKeyringInput { KeyStore = keystore, BranchKeyIdSupplier = branchKeySupplier, Cache = new CacheType { Default = new DefaultCache{EntryCapacity = 1000} }, TtlSeconds = 600 }; var keyring = mpl.CreateAwsKmsHierarchicalKeyring(createKeyringInput);
Java
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(100) .build()) .build(); final IKeyring hierarchicalKeyring = matProv.CreateAwsKmsHierarchicalKeyring(keyringInput);
步骤 4:为每个分支密钥创建易记名称

以下示例为步骤 1 中创建的两个分支密钥创建友好名称。 AWS Encryption SDK 使用加密上下文将您定义的易记名称映射到关联的 branch-key-id

C# / .NET
// Create encryption contexts for the two branch keys created in Step 1 var encryptionContextA = new Dictionary<string, string>() { // We will encrypt with branchKeyTenantA {"tenant", "TenantA"}, {"encryption", "context"}, {"is not", "secret"}, {"but adds", "useful metadata"}, {"that can help you", "be confident that"}, {"the data you are handling", "is what you think it is"} }; var encryptionContextB = new Dictionary<string, string>() { // We will encrypt with branchKeyTenantB {"tenant", "TenantB"}, {"encryption", "context"}, {"is not", "secret"}, {"but adds", "useful metadata"}, {"that can help you", "be confident that"}, {"the data you are handling", "is what you think it is"} }; // Instantiate the AWS Encryption SDK var esdk = new ESDK(new AwsEncryptionSdkConfig()); var encryptInputA = new EncryptInput { Plaintext = plaintext, Keyring = keyring, // Encrypt with branchKeyId1 EncryptionContext = encryptionContextA }; var encryptInputB = new EncryptInput { Plaintext = plaintext, Keyring = keyring, // Encrypt with branchKeyId2 EncryptionContext = encryptionContextB }; var encryptOutput = esdk.Encrypt(encryptInputA); encryptOutput = esdk.Encrypt(encryptInputB); // Use the encryption contexts to define friendly names for each branch key public class ExampleBranchKeySupplier : IBranchKeyIdSupplier { private string branchKeyTenantA; private string branchKeyTenantB; public ExampleBranchKeySupplier(string branchKeyTenantA, string branchKeyTenantB) { this.branchKeyTenantA = branchKeyTenantA; this.branchKeyTenantB = branchKeyTenantB; } public GetBranchKeyIdOutput GetBranchKeyId(GetBranchKeyIdInput input) { Dictionary<string, string> encryptionContext = input.EncryptionContext; if (!encryptionContext.ContainsKey("tenant")) { throw new Exception("EncryptionContext invalid, does not contain expected tenant key value pair."); } string tenant = encryptionContext["tenant"]; string branchkeyId; if (tenant.Equals("TenantA")) { GetBranchKeyIdOutput output = new GetBranchKeyIdOutput(); output.BranchKeyId = branchKeyTenantA; return output; } else if (tenant.Equals("TenantB")) { GetBranchKeyIdOutput output = new GetBranchKeyIdOutput(); output.BranchKeyId = branchKeyTenantB; return output; } else { throw new Exception("Item does not have a valid tenantID."); } } }
Java
// Create encryption context for branchKeyTenantA Map<String, String> encryptionContextA = new HashMap<>(); encryptionContextA.put("tenant", "TenantA"); encryptionContextA.put("encryption", "context"); encryptionContextA.put("is not", "secret"); encryptionContextA.put("but adds", "useful metadata"); encryptionContextA.put("that can help you", "be confident that"); encryptionContextA.put("the data you are handling", "is what you think it is"); // Create encryption context for branchKeyTenantB Map<String, String> encryptionContextB = new HashMap<>(); encryptionContextB.put("tenant", "TenantB"); encryptionContextB.put("encryption", "context"); encryptionContextB.put("is not", "secret"); encryptionContextB.put("but adds", "useful metadata"); encryptionContextB.put("that can help you", "be confident that"); encryptionContextB.put("the data you are handling", "is what you think it is"); // Instantiate the AWS Encryption SDK final AwsCrypto crypto = AwsCrypto.builder().build(); final CryptoResult<byte[], ?> encryptResultA = crypto.encryptData(keyring, plaintext, encryptionContextA); final CryptoResult<byte[], ?> encryptResultB = crypto.encryptData(keyring, plaintext, encryptionContextB); // Use the encryption contexts to define friendly names for each branch key public class ExampleBranchKeyIdSupplier implements IBranchKeyIdSupplier { private static String branchKeyIdForTenantA; private static String branchKeyIdForTenantB; public ExampleBranchKeyIdSupplier(String tenant1Id, String tenant2Id) { this.branchKeyIdForTenantA = tenant1Id; this.branchKeyIdForTenantB = tenant2Id; } @Override public GetBranchKeyIdOutput GetBranchKeyId(GetBranchKeyIdInput input) { Map<String, String> encryptionContext = input.encryptionContext(); if (!encryptionContext.containsKey("tenant")) { throw new IllegalArgumentException("EncryptionContext invalid, does not contain expected tenant key value pair."); } String tenantKeyId = encryptionContext.get("tenant"); String branchKeyId; if (tenantKeyId.equals("TenantA")) { branchKeyId = branchKeyIdForTenantA; } else if (tenantKeyId.equals("TenantB")) { branchKeyId = branchKeyIdForTenantB; } else { throw new IllegalArgumentException("Item does not contain valid tenant ID"); } return GetBranchKeyIdOutput.builder().branchKeyId(branchKeyId).build(); } }