AWS KMS 階層式鑰匙圈 - AWS Encryption SDK

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

AWS KMS 階層式鑰匙圈

重要

階 AWS KMS 層式鑰匙圈僅由第 4 版支援。 AWS Encryption SDK 適用於 .NET 和版本 3 的 x。 的 x 的 適用於 JAVA 的 AWS Encryption SDK。

使用 AWS KMS 階層式金鑰圈,您可以使用對稱式加密 KMS 金鑰保護加密資料,無需 AWS KMS 每次加密或解密資料時都呼叫。對於需要盡量減少呼叫的應用程式,以及可重複使用某些加密資料而不違反其安全性需求的應用程式來說 AWS KMS,這是一個不錯的選擇。

階層式金鑰圈是一種加密材料快取解決方案,可透過使用保存在 Amazon DynamoDB 表格中的 AWS KMS 受保護分支金鑰,然後在本機快取用於加密和解密作業的分支金鑰材料,以減少 AWS KMS 呼叫次數。DynamoDB 表可做為管理和保護分支金鑰的分支金鑰存放區。它存儲活動分支密鑰和分支密鑰的所有以前版本。動分支密鑰是最新的分支密鑰版本。分層密鑰環使用唯一的數據密鑰來加密每個消息,並使用從活動分支密鑰派生的唯一包裝密鑰對每個數據密鑰進行加密。分層密鑰環取決於活動分支密鑰及其派生包裝密鑰之間建立的層次結構。

分層密鑰環通常使用每個分支密鑰版本來滿足多個請求。但是您可以控制重複使用活動分支密鑰的程度,並確定活動分支鍵的旋轉頻率。分支索引鍵的作用中版本會保持作用中狀態,直到您旋轉它為止。以前版本的 Active 分支密鑰將不會用於執行加密操作,但仍然可以在解密操作中查詢和使用它們。

當您實例化分層密鑰環時,它會創建一個本地緩存。您可以指定一個快取限制,以定義分支索引鍵材料在到期並從快取中逐出之前儲存在本機快取中的時間上限。在作業中第一次指定 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 以解密活動分支密鑰並返回明文活動分支密鑰。識別活動分支密鑰的數據被序列化,以在解密調用中提供額外的身份驗證數據(AAD)。 AWS KMS

    2. 分支密鑰存儲庫返回明文分支密鑰和標識它的數據,例如分支密鑰版本。

  3. 階層式金鑰圈會組合分支金鑰材料 (純文字分支金鑰和分支金鑰版本),並將它們的副本儲存在本機快取中。

  4. 分層密鑰環從明文分支密鑰和 16 字節隨機鹽派生一個唯一的包裝密鑰。它使用派生的包裝密鑰來加密純文本數據密鑰的副本。

加密方法使用加密材料對數據進行加密。如需詳細資訊,請參閱如何 AWS Encryption SDK 加密資料

解密和驗證

以下逐步解說說明「階層式金鑰圈」如何組合解密材料並解密加密的資料金鑰。

  1. 解密方法可識別加密訊息中的加密資料金鑰,並將其傳遞至階層式金鑰圈。

  2. 階層式金鑰環還原序列化識別加密資料金鑰的資料,包括分支金鑰版本、16 位元組 salt,以及說明資料金鑰加密方式的其他資訊。

    如需詳細資訊,請參閱 AWS KMS 分層鑰匙圈技術細節

  3. 階層式金鑰圈會檢查本機快取中是否有符合步驟 2 中所識別的分支金鑰版本的有效分支金鑰材料。如果有有效的分支金鑰材料,金鑰圈會進入步驟 6

  4. 如果沒有有效的分支金鑰材料,階層式金鑰圈會查詢分支金鑰存放區,找出與步驟 2 中所識別的分支金鑰版本相符的分支金鑰存放區。

    1. 分支密鑰存儲調用 AWS KMS 解密分支密鑰並返回明文活動分支密鑰。識別活動分支密鑰的數據被序列化,以在解密調用中提供額外的身份驗證數據(AAD)。 AWS KMS

    2. 分支密鑰存儲庫返回明文分支密鑰和標識它的數據,例如分支密鑰版本。

  5. 階層式金鑰圈會組合分支金鑰材料 (純文字分支金鑰和分支金鑰版本),並將它們的副本儲存在本機快取中。

  6. 階層式金鑰圈使用組裝好的分支金鑰材料和步驟 2 中識別的 16 位元組鹽來重現加密資料金鑰的唯一包裝金鑰。

  7. 階層式金鑰圈使用複製的包裝金鑰來解密資料金鑰,並傳回純文字資料金鑰。

解密方法使用解密材料和純文本數據密鑰來解密加密的消息。如需詳細資訊,請參閱如何 AWS Encryption SDK 解密加密的郵件

必要條件

AWS Encryption SDK 不需要一個 AWS 帳戶 ,它不依賴於任何 AWS 服務. 不過,階層式金鑰圈取決於 AWS KMS 和 Amazon DynamoDB。

若要使用階層式金鑰圈,您需要使用 K MS: 解密權限的對稱 AWS KMS key 加密。您也可以使用對稱加密多區域金鑰。如需有關權限的詳細資訊 AWS KMS keys,請參閱AWS Key Management Service 開發人員指南中的驗證和存取控制

在建立和使用階層式金鑰圈之前,您必須先建立分支金鑰存放區,並使用第一個使用中的分支金鑰來填入該分支金鑰存放區。

步驟 1:設定新的金鑰存放區服務

金鑰存放區服務提供數種 API 作業,例如CreateKeyStoreCreateKey,可協助您組合階層式金鑰圈必要條件並管理分支金鑰存放區。

下列範例會建立金鑰存放區服務。您必須指定 DynamoDB 資料表名稱做為分支金鑰存放區的名稱、分支金鑰存放區的邏輯名稱,以及識別可保護分支金鑰之 KMS 金鑰的 KMS 金鑰 ARN。

邏輯金鑰存放區名稱會以密碼方式繫結至儲存在資料表中的所有資料,以簡化 DynamoDB 還原作業。邏輯金鑰存放區名稱可以與 DynamoDB 表名稱相同,但不一定要這樣做。建議您在第一次設定金鑰存放區服務時,將 DynamoDB 表名稱指定為邏輯資料表名稱。您必須永遠指定相同的邏輯資料表名稱。如果您的分支金鑰存放區名稱在從備份還原 DynamoDB 表後變更,邏輯金鑰存放區名稱會對應至您指定的 DynamoDB 表名稱,以確保階層式金鑰圈仍可存取您的分支金鑰存放區。

注意

邏輯金鑰存放區名稱會包含在呼叫的所有金鑰存放區服務 API 作業的加密內容中 AWS KMS。加密內容並非秘密,其值 (包括邏輯金鑰存放區名稱) 會出現在記錄檔中的純文字中。 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業會產生下列值。

然後,作CreateKey業會呼叫 kms:GenerateDataKeyWithoutPlaintext使用下列要求。

{ "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作業會呼叫 kms: ReEncrypt 透過更新加密內容來建立分支金鑰的使用中記錄。

最後,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#/.
    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#/.
    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):定義嘗試重新整理分支金鑰材料逾時之前的秒數。每當緩存返回NoSuchEntry以響應 a 時GetCacheEntry,該分支密鑰都會被視為處於飛行狀態,直到用條目寫PutCache入相同的密鑰為止。

      預設值:20 秒

    • 睡眠:定義如果超過執行緒應該睡眠的秒數。fanOut

      預設值:20 毫秒

    C#/.
    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);

旋轉您的活動分支密鑰

每個分支鍵一次只能有一個活動版本。分層密鑰環通常使用每個活動分支密鑰版本來滿足多個請求。但是您可以控制重複使用活動分支密鑰的程度,並確定活動分支鍵的旋轉頻率。

分支密鑰不用於加密純文本數據密鑰。它們被用來派生加密明文數據密鑰的唯一包裝密鑰。包裝密鑰派生過程會產生具有 28 個字節隨機性的唯一 32 字節包裝密鑰。這意味著在發生密碼編譯耗損之前,分支密鑰可以導出超過 79 個八進制或 2 96 個唯一包裝密鑰。儘管這種用盡風險非常低,但由於業務或合約規則或政府法規,您可能需要輪換活動中的分支密鑰。

分支索引鍵的作用中版本會保持作用中狀態,直到您旋轉它為止。以前版本的 Active 分支密鑰將不會用於執行加密操作,也無法用於導出新的包裝密鑰。但是仍然可以查詢它們並提供包裝密鑰以解密它們在活動時加密的數據密鑰。

使用金鑰存放區服務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

針對解密作業,您可以靜態設定單一階層式金鑰環,將解密限制為單一承租人,或使用分支金鑰識別碼供應商來識別負責解密訊息的承租人。

首先,遵循先決條件程序的步驟 1 和步驟 2。然後,使用下列程序為每個承租人建立分支金鑰、建立分支金鑰識別碼供應商,並初始化階層式金鑰圈以供多租戶使用。

步驟 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#/.
    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#/.
    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):定義嘗試重新整理分支金鑰材料逾時之前的秒數。每當緩存返回NoSuchEntry以響應 a 時GetCacheEntry,該分支密鑰都會被視為處於飛行狀態,直到用條目寫PutCache入相同的密鑰為止。

      預設值:20 秒

    • 睡眠:定義如果超過執行緒應該睡眠的秒數。fanOut

      預設值:20 毫秒

    C#/.
    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(); } }