

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

# 資料金鑰快取
<a name="data-key-caching"></a>

*資料金鑰快取*會將[資料金鑰](concepts.md#DEK)和[相關加密資料](data-caching-details.md#cache-entries)全部儲存到快取中。當您加密或解密資料時， 會在快取中 AWS Encryption SDK 尋找相符的資料金鑰。如果找到符合的金鑰，它就會使用該快取資料金鑰，而不會產生新的金鑰。資料金鑰快取可以提升效能、降低成本，並且讓您在應用程式規模不斷擴展時維持不超過服務用量。

應用程式在下列條件下能發揮資料金鑰快取優勢：
+ 它可以重複使用資料金鑰。
+ 它會產生大量的資料金鑰。
+ 您的加密操作會異常地變慢速度、成本昂貴、效能受限或過度使用資源。

快取可以減少您對密碼編譯服務的使用，例如 AWS Key Management Service (AWS KMS)。如果服務到達 [AWS KMS requests-per-second limit](https://docs.aws.amazon.com/kms/latest/developerguide/limits.html#requests-per-second)，這時快取就能助您一臂之力。您的應用程式可以使用快取金鑰來服務某些資料金鑰請求，而不是呼叫 AWS KMS。（您也可以在[AWS 支援中心](https://console.aws.amazon.com/support/home#/)建立案例，以提高帳戶的限額。)

 AWS Encryption SDK 可協助您建立和管理資料金鑰快取。它提供[本機快取](data-caching-details.md#simplecache)和[快取密碼編譯資料管理員](data-caching-details.md#caching-cmm) （快取 CMM)，其會與快取互動並強制執行您設定[的安全閾值](thresholds.md)。這些元件在整合運作之後，能夠讓您透過重複使用資料金鑰提高產能效率，同時維護系統安全性。

資料金鑰快取是 的選用功能 AWS Encryption SDK ，您應謹慎使用。根據預設， 會為每個加密操作 AWS Encryption SDK 產生新的資料金鑰。這項技術能支援加密操作的最佳實務，而這種做法並不鼓勵過度重複使用資料金鑰。一般而言，資料金鑰快取只會在為了滿足效能目標時才會啟用。接著，請使用資料金鑰快取[安全性閾值](thresholds.md)，確保您是使用最低快取數來達成成本和效能目標。

3.*x* 版 適用於 JAVA 的 AWS Encryption SDK 僅支援使用舊版主金鑰提供者界面的快取 CMM，不支援 keyring 界面。不過， AWS Encryption SDK 適用於 .NET 的 4.*x* 版和更新版本、適用於 Rust **的 3.*x* 適用於 JAVA 的 AWS Encryption SDK版 適用於 Python 的 AWS Encryption SDK、 AWS Encryption SDK 適用於 Rust 的 1.*x* 版和 AWS Encryption SDK 適用於 Go 的 0.1.*x* 版或更新版本支援[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。使用 AWS KMS 階層式 keyring 加密的內容只能使用階層式 keyring AWS KMS 解密。

如需這些安全權衡的詳細討論，請參閱 AWS 安全部落格中的[AWS Encryption SDK：如何判斷資料金鑰快取是否適合您的應用程式](https://aws.amazon.com/blogs/security/aws-encryption-sdk-how-to-decide-if-data-key-caching-is-right-for-your-application/)。

**Topics**
+ [如何使用資料金鑰快取](implement-caching.md)
+ [設定快取安全性閾值](thresholds.md)
+ [資料金鑰快取詳細資訊](data-caching-details.md)
+ [資料金鑰快取範例](sample-cache-example.md)

# 如何使用資料金鑰快取
<a name="implement-caching"></a>

此主題說明如何在應用程式中使用資料金鑰快取。它會逐步引導您完成程序的每個步驟。然後，將步驟結合為簡單範例，在操作中使用資料金鑰快取來加密字串。

本節中的範例示範如何使用 [2.0.*x* 版](about-versions.md)和更新版本 AWS Encryption SDK。如需使用舊版的範例，請在[程式設計語言](programming-languages.md)的 GitHub 儲存庫版本清單中尋找您的[版本](https://github.com/aws/aws-encryption-sdk-c/releases)。

如需在 中使用資料金鑰快取的完整和測試範例 AWS Encryption SDK，請參閱：
+ C/C\$1\$1：[caching\$1cmm.cpp](https://github.com/aws/aws-encryption-sdk-c/blob/master/examples/caching_cmm.cpp)
+ Java：[SimpleDataKeyCachingExample.java](https://github.com/aws/aws-encryption-sdk-java/blob/master/src/examples/java/com/amazonaws/crypto/examples/v2/SimpleDataKeyCachingExample.java)
+ JavaScript 瀏覽器：[caching\$1cmm.ts](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-browser/src/caching_cmm.ts)
+ JavaScript Node.js：[caching\$1cmm.ts](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-node/src/caching_cmm.ts)
+ Python：[data\$1key\$1caching\$1basic.py](https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/legacy/data_key_caching_basic.py)

[AWS Encryption SDK for .NET](dot-net.md) 不支援資料金鑰快取。

**Topics**
+ [使用資料金鑰快取：逐步操作](#implement-caching-steps)
+ [資料金鑰快取範例：加密字串](#caching-example-encrypt-string)

## 使用資料金鑰快取：逐步操作
<a name="implement-caching-steps"></a>

這些逐步指示說明如何建立實作資料金鑰快取所需的元件。
+ [建立資料金鑰快取](data-caching-details.md#simplecache)。在這些範例中，我們使用 AWS Encryption SDK 提供的本機快取。我們將快取限制為 10 個資料金鑰。

   

------
#### [ C ]

  ```
  // Cache capacity (maximum number of entries) is required
  size_t cache_capacity = 10; 
  struct aws_allocator *allocator = aws_default_allocator();
  
  struct aws_cryptosdk_materials_cache *cache = aws_cryptosdk_materials_cache_local_new(allocator, cache_capacity);
  ```

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

  下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。使用 3.*x* 版時，您也可以使用[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

  ```
  // Cache capacity (maximum number of entries) is required
  int MAX_CACHE_SIZE = 10; 
  
  CryptoMaterialsCache cache = new LocalCryptoMaterialsCache(MAX_CACHE_SIZE);
  ```

------
#### [ JavaScript Browser ]

  ```
  const capacity = 10
  
  const cache = getLocalCryptographicMaterialsCache(capacity)
  ```

------
#### [ JavaScript Node.js ]

  ```
  const capacity = 10
  
  const cache = getLocalCryptographicMaterialsCache(capacity)
  ```

------
#### [ Python ]

  ```
  # Cache capacity (maximum number of entries) is required
  MAX_CACHE_SIZE = 10
  
  cache = aws_encryption_sdk.LocalCryptoMaterialsCache(MAX_CACHE_SIZE)
  ```

------

   
+ 建立[主金鑰提供者](concepts.md#master-key-provider) (Java 和 Python) 或 [keyring](concepts.md#keyring) (C 和 JavaScript)。這些範例使用 AWS Key Management Service (AWS KMS) 主金鑰提供者或相容的 [AWS KMS keyring](use-kms-keyring.md)。

   

------
#### [ C ]

  ```
  // Create an AWS KMS keyring
  //   The input is the Amazon Resource Name (ARN) 
  //   of an AWS KMS key
  struct aws_cryptosdk_keyring *kms_keyring = Aws::Cryptosdk::KmsKeyring::Builder().Build(kms_key_arn);
  ```

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

  下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。使用 3.*x* 版時，您也可以使用[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

  ```
  // Create an AWS KMS master key provider
  //   The input is the Amazon Resource Name (ARN) 
  //   of an AWS KMS key
  MasterKeyProvider<KmsMasterKey> keyProvider = KmsMasterKeyProvider.builder().buildStrict(kmsKeyArn);
  ```

------
#### [ JavaScript Browser ]

  在瀏覽器中，您必須安全地注入您的登入資料。此範例定義會在執行階段解析登入資料的 webpack (kms.webpack.config) 中的登入資料。它會從 AWS KMS 用戶端和登入資料建立 AWS KMS 用戶端提供者執行個體。然後，當它建立 keyring 時，它會將用戶端提供者與 AWS KMS key () 一起傳遞給建構函式`generatorKeyId)`。

  ```
  const { accessKeyId, secretAccessKey, sessionToken } = credentials
  
  const clientProvider = getClient(KMS, {
      credentials: {
        accessKeyId,
        secretAccessKey,
        sessionToken
      }
    })
  
  /*  Create an AWS KMS keyring
   *  You must configure the AWS KMS keyring with at least one AWS KMS key
  *  The input is the Amazon Resource Name (ARN) 
   */ of an AWS KMS key
  const keyring = new KmsKeyringBrowser({
      clientProvider,
      generatorKeyId,
      keyIds,
    })
  ```

------
#### [ JavaScript Node.js ]

  ```
  /* Create an AWS KMS keyring
   *   The input is the Amazon Resource Name (ARN) 
  */   of an AWS KMS key
  const keyring = new KmsKeyringNode({ generatorKeyId })
  ```

------
#### [ Python ]

  ```
  # Create an AWS KMS master key provider
  #  The input is the Amazon Resource Name (ARN) 
  #  of an AWS KMS key
  key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[kms_key_arn])
  ```

------

   
+ [建立快取密碼編譯資料管理員 ](data-caching-details.md#caching-cmm)（快取 CMM)。

   

  將快取 CMM 與快取和主金鑰提供者或 keyring 建立關聯。然後，在快取 CMM 上[設定快取安全閾值](thresholds.md)。

   

------
#### [ C ]

  在 中 適用於 C 的 AWS Encryption SDK，您可以從基礎 CMM 建立快取 CMM，例如預設 CMM，或從 keyring。此範例會從 keyring 建立快取 CMM。

  建立快取 CMM 之後，您可以釋出對 keyring 和快取的參考。如需詳細資訊，請參閱[參考計數](c-language-using.md#c-language-using-release)。

  ```
  // Create the caching CMM
  //   Set the partition ID to NULL.
  //   Set the required maximum age value to 60 seconds.
  struct aws_cryptosdk_cmm *caching_cmm = aws_cryptosdk_caching_cmm_new_from_keyring(allocator, cache, kms_keyring, NULL, 60, AWS_TIMESTAMP_SECS);
  
  // Add an optional message threshold
  //   The cached data key will not be used for more than 10 messages.
  aws_status = aws_cryptosdk_caching_cmm_set_limit_messages(caching_cmm, 10);
  
  // Release your references to the cache and the keyring.
  aws_cryptosdk_materials_cache_release(cache);
  aws_cryptosdk_keyring_release(kms_keyring);
  ```

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

  下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版 適用於 JAVA 的 AWS Encryption SDK 不支援資料金鑰快取，但確實支援[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

  ```
  /*
   * Security thresholds
   *   Max entry age is required. 
   *   Max messages (and max bytes) per entry are optional
   */
  int MAX_ENTRY_AGE_SECONDS = 60;
  int MAX_ENTRY_MSGS = 10;
         
  //Create a caching CMM
  CryptoMaterialsManager cachingCmm =
      CachingCryptoMaterialsManager.newBuilder().withMasterKeyProvider(keyProvider)
                                   .withCache(cache)
                                   .withMaxAge(MAX_ENTRY_AGE_SECONDS, TimeUnit.SECONDS)
                                   .withMessageUseLimit(MAX_ENTRY_MSGS)
                                   .build();
  ```

------
#### [ JavaScript Browser ]

  ```
  /*
   * Security thresholds
   *   Max age (in milliseconds) is required.
   *   Max messages (and max bytes) per entry are optional.
   */
  const maxAge = 1000 * 60
  const maxMessagesEncrypted = 10
  
  /* Create a caching CMM from a keyring  */
  const cachingCmm = new WebCryptoCachingMaterialsManager({
    backingMaterials: keyring,
    cache,
    maxAge,
    maxMessagesEncrypted
  })
  ```

------
#### [ JavaScript Node.js ]

  ```
  /*
   * Security thresholds
   *   Max age (in milliseconds) is required.
   *   Max messages (and max bytes) per entry are optional.
   */
  const maxAge = 1000 * 60
  const maxMessagesEncrypted = 10
  
  /* Create a caching CMM from a keyring  */
  const cachingCmm = new NodeCachingMaterialsManager({
    backingMaterials: keyring,
    cache,
    maxAge,
    maxMessagesEncrypted
  })
  ```

------
#### [ Python ]

  ```
  # Security thresholds
  #   Max entry age is required. 
  #   Max messages (and max bytes) per entry are optional
  #
  MAX_ENTRY_AGE_SECONDS = 60.0
  MAX_ENTRY_MESSAGES = 10
         
  # Create a caching CMM
  caching_cmm = CachingCryptoMaterialsManager(
      master_key_provider=key_provider,
      cache=cache,
      max_age=MAX_ENTRY_AGE_SECONDS,
      max_messages_encrypted=MAX_ENTRY_MESSAGES
  )
  ```

------

您只需進行這些操作。然後，讓 為您 AWS Encryption SDK 管理快取，或新增您自己的快取管理邏輯。

當您想要在呼叫中使用資料金鑰快取來加密或解密資料時，請指定快取 CMM，而不是主金鑰提供者或其他 CMM。

**注意**  
如果您正在加密資料串流或任何未知大小的資料，請務必在請求中指定資料大小。加密未知大小的資料時 AWS Encryption SDK ， 不會使用資料金鑰快取。

------
#### [ C ]

在 中 適用於 C 的 AWS Encryption SDK，您會使用快取 CMM 建立工作階段，然後處理工作階段。

根據預設，當訊息大小未知且無限制時， AWS Encryption SDK 不會快取資料金鑰。若要在不知道確切資料大小時允許快取，請使用 `aws_cryptosdk_session_set_message_bound` 方法來設定的訊息大小上限。將限制設定為大於估計的訊息大小。如果實際訊息大小超過限制，則加密操作會失敗。

```
/* Create a session with the caching CMM. Set the session mode to encrypt. */
struct aws_cryptosdk_session *session = aws_cryptosdk_session_new_from_cmm_2(allocator, AWS_CRYPTOSDK_ENCRYPT, caching_cmm);

/* Set a message bound of 1000 bytes */
aws_status = aws_cryptosdk_session_set_message_bound(session, 1000);

/* Encrypt the message using the session with the caching CMM */
aws_status = aws_cryptosdk_session_process(
             session, output_buffer, output_capacity, &output_produced, input_buffer, input_len, &input_consumed);

/* Release your references to the caching CMM and the session. */
aws_cryptosdk_cmm_release(caching_cmm);
aws_cryptosdk_session_destroy(session);
```

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

下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。使用 3.*x* 版時，您也可以使用[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

```
// When the call to encryptData specifies a caching CMM,
// the encryption operation uses the data key cache
final AwsCrypto encryptionSdk = AwsCrypto.standard();
return encryptionSdk.encryptData(cachingCmm, plaintext_source).getResult();
```

------
#### [ JavaScript Browser ]

```
const { result } = await encrypt(cachingCmm, plaintext)
```

------
#### [ JavaScript Node.js ]

當您在 適用於 JavaScript 的 AWS Encryption SDK for Node.js 中使用快取 CMM 時， `encrypt`方法需要純文字的長度。如果您不提供該資料，就不會快取資料金鑰。如果您提供長度，但您提供的純文字資料超過該長度，則加密操作會失敗。如果您不知道純文字的確切長度，例如當您串流資料時，請提供最大的預期值。

```
const { result } = await encrypt(cachingCmm, plaintext, { plaintextLength: plaintext.length })
```

------
#### [ Python ]

```
# Set up an encryption client
client = aws_encryption_sdk.EncryptionSDKClient()

# When the call to encrypt specifies a caching CMM,
# the encryption operation uses the data key cache
#
encrypted_message, header = client.encrypt(
    source=plaintext_source,
    materials_manager=caching_cmm
)
```

------

## 資料金鑰快取範例：加密字串
<a name="caching-example-encrypt-string"></a>

這個簡單的程式碼範例在加密字串時使用資料金鑰快取。它將來自[逐步程序](#implement-caching-steps)的程式碼合併至您可以執行的測試程式碼。

此範例會為 建立[本機快取](data-caching-details.md#simplecache)和[主金鑰提供者](concepts.md#master-key-provider)或 [keyring](concepts.md#keyring) AWS KMS key。然後，它會使用本機快取和主金鑰提供者或 keyring 來建立具有適當[安全閾值的](thresholds.md)快取 CMM。在 Java 和 Python 中，加密請求會指定快取 CMM、要加密的純文字資料，以及[加密內容](data-caching-details.md#caching-encryption-context)。在 C 中，快取 CMM 會在工作階段中指定，並將該工作階段提供給加密請求。

若要執行這些範例，您需要提供 [的 Amazon Resource Name (ARN) AWS KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html)。請確定您擁有[使用 AWS KMS key的許可](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default-allow-users)，以產生資料金鑰。

如需建立和使用資料金鑰快取的更詳細真實範例，請參閱 [資料金鑰快取範例程式碼](sample-cache-example-code.md)。

------
#### [ C ]

```
/*
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use
 * this file except in compliance with the License. A copy of the License is
 * located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <aws/cryptosdk/cache.h>
#include <aws/cryptosdk/cpp/kms_keyring.h>
#include <aws/cryptosdk/session.h>

void encrypt_with_caching(
    uint8_t *ciphertext,     // output will go here (assumes ciphertext_capacity bytes already allocated)
    size_t *ciphertext_len,  // length of output will go here
    size_t ciphertext_capacity,
    const char *kms_key_arn,
    int max_entry_age,
    int cache_capacity) {
    const uint64_t MAX_ENTRY_MSGS = 100;

    struct aws_allocator *allocator = aws_default_allocator();
    
    // Load error strings for debugging
    aws_cryptosdk_load_error_strings();

    // Create a keyring
    struct aws_cryptosdk_keyring *kms_keyring = Aws::Cryptosdk::KmsKeyring::Builder().Build(kms_key_arn);

    // Create a cache
    struct aws_cryptosdk_materials_cache *cache = aws_cryptosdk_materials_cache_local_new(allocator, cache_capacity);

    // Create a caching CMM
    struct aws_cryptosdk_cmm *caching_cmm = aws_cryptosdk_caching_cmm_new_from_keyring(
        allocator, cache, kms_keyring, NULL, max_entry_age, AWS_TIMESTAMP_SECS);
    if (!caching_cmm) abort();

    if (aws_cryptosdk_caching_cmm_set_limit_messages(caching_cmm, MAX_ENTRY_MSGS)) abort();

    // Create a session
    struct aws_cryptosdk_session *session =        
        aws_cryptosdk_session_new_from_cmm_2(allocator, AWS_CRYPTOSDK_ENCRYPT, caching_cmm);
    if (!session) abort();

    // Encryption context
    struct aws_hash_table *enc_ctx = aws_cryptosdk_session_get_enc_ctx_ptr_mut(session);
    if (!enc_ctx) abort();
    AWS_STATIC_STRING_FROM_LITERAL(enc_ctx_key, "purpose");
    AWS_STATIC_STRING_FROM_LITERAL(enc_ctx_value, "test");
    if (aws_hash_table_put(enc_ctx, enc_ctx_key, (void *)enc_ctx_value, NULL)) abort();

    // Plaintext data to be encrypted
    const char *my_data = "My plaintext data";
    size_t my_data_len  = strlen(my_data);
    if (aws_cryptosdk_session_set_message_size(session, my_data_len)) abort();

    // When the session uses a caching CMM, the encryption operation uses the data key cache
    // specified in the caching CMM.
    size_t bytes_read;
    if (aws_cryptosdk_session_process(
            session,
            ciphertext,
            ciphertext_capacity,
            ciphertext_len,
            (const uint8_t *)my_data,
            my_data_len,
            &bytes_read))
        abort();
    if (!aws_cryptosdk_session_is_done(session) || bytes_read != my_data_len) abort();

    aws_cryptosdk_session_destroy(session);
    aws_cryptosdk_cmm_release(caching_cmm);
    aws_cryptosdk_materials_cache_release(cache);
    aws_cryptosdk_keyring_release(kms_keyring);
}
```

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

下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。使用 3.*x* 版時，您也可以使用[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

```
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package com.amazonaws.crypto.examples;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CryptoMaterialsManager;
import com.amazonaws.encryptionsdk.MasterKeyProvider;
import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
import com.amazonaws.encryptionsdk.caching.CryptoMaterialsCache;
import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache;
import com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKey;
import com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * Encrypts a string using an &KMS; key and data key caching
 *
 * <p>
 * Arguments:
 * <ol>
 * <li>KMS Key ARN: To find the Amazon Resource Name of your &KMS; key,
 *     see 'Find the key ID and ARN' at https://docs.aws.amazon.com/kms/latest/developerguide/find-cmk-id-arn.html
 * <li>Max entry age: Maximum time (in seconds) that a cached entry can be used
 * <li>Cache capacity: Maximum number of entries in the cache
 * </ol>
 */
public class SimpleDataKeyCachingExample {

    /*
     * Security thresholds
     *   Max entry age is required.
     *   Max messages (and max bytes) per data key are optional
     */
    private static final int MAX_ENTRY_MSGS = 100;

    public static byte[] encryptWithCaching(String kmsKeyArn, int maxEntryAge, int cacheCapacity) {
        // Plaintext data to be encrypted
        byte[] myData = "My plaintext data".getBytes(StandardCharsets.UTF_8);

        // Encryption context
        // Most encrypted data should have an associated encryption context
        // to protect integrity. This sample uses placeholder values.
        // For more information see:
        // blogs.aws.amazon.com/security/post/Tx2LZ6WBJJANTNW/How-to-Protect-the-Integrity-of-Your-Encrypted-Data-by-Using-AWS-Key-Management
        final Map<String, String> encryptionContext = Collections.singletonMap("purpose", "test");

        // Create a master key provider
        MasterKeyProvider<KmsMasterKey> keyProvider = KmsMasterKeyProvider.builder()
            .buildStrict(kmsKeyArn);

        // Create a cache
        CryptoMaterialsCache cache = new LocalCryptoMaterialsCache(cacheCapacity);

        // Create a caching CMM
        CryptoMaterialsManager cachingCmm =
            CachingCryptoMaterialsManager.newBuilder().withMasterKeyProvider(keyProvider)
                .withCache(cache)
                .withMaxAge(maxEntryAge, TimeUnit.SECONDS)
                .withMessageUseLimit(MAX_ENTRY_MSGS)
                .build();

        // When the call to encryptData specifies a caching CMM,
        // the encryption operation uses the data key cache
        final AwsCrypto encryptionSdk = AwsCrypto.standard();
        return encryptionSdk.encryptData(cachingCmm, myData, encryptionContext).getResult();
    }
}
```

------
#### [ JavaScript Browser ]

```
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

/* This is a simple example of using a caching CMM with a KMS keyring
 * to encrypt and decrypt using the AWS Encryption SDK for Javascript in a browser.
 */

import {
  KmsKeyringBrowser,
  KMS,
  getClient,
  buildClient,
  CommitmentPolicy,
  WebCryptoCachingMaterialsManager,
  getLocalCryptographicMaterialsCache,
} from '@aws-crypto/client-browser'
import { toBase64 } from '@aws-sdk/util-base64-browser'

/* This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
 * which enforces that this client only encrypts using committing algorithm suites
 * and enforces that this client
 * will only decrypt encrypted messages
 * that were created with a committing algorithm suite.
 * This is the default commitment policy
 * if you build the client with `buildClient()`.
 */
const { encrypt, decrypt } = buildClient(
  CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
)

/* This is injected by webpack.
 * The webpack.DefinePlugin or @aws-sdk/karma-credential-loader will replace the values when bundling.
 * The credential values are pulled from @aws-sdk/credential-provider-node
 * Use any method you like to get credentials into the browser.
 * See kms.webpack.config
 */
declare const credentials: {
  accessKeyId: string
  secretAccessKey: string
  sessionToken: string
}

/* This is done to facilitate testing. */
export async function testCachingCMMExample() {
  /* This example uses an &KMS; keyring. The generator key in a &KMS; keyring generates and encrypts the data key.
   * The caller needs kms:GenerateDataKey permission on the &KMS; key in generatorKeyId.
   */
  const generatorKeyId =
    'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt'

  /* Adding additional KMS keys that can decrypt.
   * The caller must have kms:Encrypt permission for every &KMS; key in keyIds.
   * You might list several keys in different AWS Regions.
   * This allows you to decrypt the data in any of the represented Regions.
   * In this example, the generator key
   * and the additional key are actually the same &KMS; key.
   * In `generatorId`, this &KMS; key is identified by its alias ARN.
   * In `keyIds`, this &KMS; key is identified by its key ARN.
   * In practice, you would specify different &KMS; keys,
   * or omit the `keyIds` parameter.
   * This is *only* to demonstrate how the &KMS; key ARNs are configured.
   */
  const keyIds = [
    'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f',
  ]

  /* Need a client provider that will inject correct credentials.
   * The credentials here are injected by webpack from your environment bundle is created
   * The credential values are pulled using @aws-sdk/credential-provider-node.
   * See kms.webpack.config
   * You should inject your credential into the browser in a secure manner
   * that works with your application.
   */
  const { accessKeyId, secretAccessKey, sessionToken } = credentials

  /* getClient takes a KMS client constructor
   * and optional configuration values.
   * The credentials can be injected here,
   * because browsers do not have a standard credential discovery process the way Node.js does.
   */
  const clientProvider = getClient(KMS, {
    credentials: {
      accessKeyId,
      secretAccessKey,
      sessionToken,
    },
  })

  /* You must configure the KMS keyring with your &KMS; keys */
  const keyring = new KmsKeyringBrowser({
    clientProvider,
    generatorKeyId,
    keyIds,
  })

  /* Create a cache to hold the data keys (and related cryptographic material).
   * This example uses the local cache provided by the Encryption SDK.
   * The `capacity` value represents the maximum number of entries
   * that the cache can hold.
   * To make room for an additional entry,
   * the cache evicts the oldest cached entry.
   * Both encrypt and decrypt requests count independently towards this threshold.
   * Entries that exceed any cache threshold are actively removed from the cache.
   * By default, the SDK checks one item in the cache every 60 seconds (60,000 milliseconds).
   * To change this frequency, pass in a `proactiveFrequency` value
   * as the second parameter. This value is in milliseconds.
   */
  const capacity = 100
  const cache = getLocalCryptographicMaterialsCache(capacity)

  /* The partition name lets multiple caching CMMs share the same local cryptographic cache.
   * By default, the entries for each CMM are cached separately. However, if you want these CMMs to share the cache,
   * use the same partition name for both caching CMMs.
   * If you don't supply a partition name, the Encryption SDK generates a random name for each caching CMM.
   * As a result, sharing elements in the cache MUST be an intentional operation.
   */
  const partition = 'local partition name'

  /* maxAge is the time in milliseconds that an entry will be cached.
   * Elements are actively removed from the cache.
   */
  const maxAge = 1000 * 60

  /* The maximum number of bytes that will be encrypted under a single data key.
   * This value is optional,
   * but you should configure the lowest practical value.
   */
  const maxBytesEncrypted = 100

  /* The maximum number of messages that will be encrypted under a single data key.
   * This value is optional,
   * but you should configure the lowest practical value.
   */
  const maxMessagesEncrypted = 10

  const cachingCMM = new WebCryptoCachingMaterialsManager({
    backingMaterials: keyring,
    cache,
    partition,
    maxAge,
    maxBytesEncrypted,
    maxMessagesEncrypted,
  })

  /* Encryption context is a *very* powerful tool for controlling
   * and managing access.
   * When you pass an encryption context to the encrypt function,
   * the encryption context is cryptographically bound to the ciphertext.
   * If you don't pass in the same encryption context when decrypting,
   * the decrypt function fails.
   * The encryption context is ***not*** secret!
   * Encrypted data is opaque.
   * You can use an encryption context to assert things about the encrypted data.
   * The encryption context helps you to determine
   * whether the ciphertext you retrieved is the ciphertext you expect to decrypt.
   * For example, if you are are only expecting data from 'us-west-2',
   * the appearance of a different AWS Region in the encryption context can indicate malicious interference.
   * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
   *
   * Also, cached data keys are reused ***only*** when the encryption contexts passed into the functions are an exact case-sensitive match.
   * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/data-caching-details.html#caching-encryption-context
   */
  const encryptionContext = {
    stage: 'demo',
    purpose: 'simple demonstration app',
    origin: 'us-west-2',
  }

  /* Find data to encrypt. */
  const plainText = new Uint8Array([1, 2, 3, 4, 5])

  /* Encrypt the data.
   * The caching CMM only reuses data keys
   * when it know the length (or an estimate) of the plaintext.
   * However, in the browser,
   * you must provide all of the plaintext to the encrypt function.
   * Therefore, the encrypt function in the browser knows the length of the plaintext
   * and does not accept a plaintextLength option.
   */
  const { result } = await encrypt(cachingCMM, plainText, { encryptionContext })

  /* Log the plain text
   * only for testing and to show that it works.
   */
  console.log('plainText:', plainText)
  document.write('</br>plainText:' + plainText + '</br>')

  /* Log the base64-encoded result
   * so that you can try decrypting it with another AWS Encryption SDK implementation.
   */
  const resultBase64 = toBase64(result)
  console.log(resultBase64)
  document.write(resultBase64)

  /* Decrypt the data.
   * NOTE: This decrypt request will not use the data key
   * that was cached during the encrypt operation.
   * Data keys for encrypt and decrypt operations are cached separately.
   */
  const { plaintext, messageHeader } = await decrypt(cachingCMM, result)

  /* Grab the encryption context so you can verify it. */
  const { encryptionContext: decryptedContext } = messageHeader

  /* Verify the encryption context.
   * If you use an algorithm suite with signing,
   * the Encryption SDK adds a name-value pair to the encryption context that contains the public key.
   * Because the encryption context might contain additional key-value pairs,
   * do not include a test that requires that all key-value pairs match.
   * Instead, verify that the key-value pairs that you supplied to the `encrypt` function are included in the encryption context that the `decrypt` function returns.
   */
  Object.entries(encryptionContext).forEach(([key, value]) => {
    if (decryptedContext[key] !== value)
      throw new Error('Encryption Context does not match expected values')
  })

  /* Log the clear message
   * only for testing and to show that it works.
   */
  document.write('</br>Decrypted:' + plaintext)
  console.log(plaintext)

  /* Return the values to make testing easy. */
  return { plainText, plaintext }
}
```

------
#### [ JavaScript Node.js ]

```
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import {
  KmsKeyringNode,
  buildClient,
  CommitmentPolicy,
  NodeCachingMaterialsManager,
  getLocalCryptographicMaterialsCache,
} from '@aws-crypto/client-node'

/* This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
 * which enforces that this client only encrypts using committing algorithm suites
 * and enforces that this client
 * will only decrypt encrypted messages
 * that were created with a committing algorithm suite.
 * This is the default commitment policy
 * if you build the client with `buildClient()`.
 */
const { encrypt, decrypt } = buildClient(
  CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
)

export async function cachingCMMNodeSimpleTest() {
  /* An &KMS; key is required to generate the data key.
   * You need kms:GenerateDataKey permission on the &KMS; key in generatorKeyId.
   */
  const generatorKeyId =
    'arn:aws:kms:us-west-2:658956600833:alias/EncryptDecrypt'

  /* Adding alternate &KMS; keys that can decrypt.
   * Access to kms:Encrypt is required for every &KMS; key in keyIds.
   * You might list several keys in different AWS Regions.
   * This allows you to decrypt the data in any of the represented Regions.
   * In this example, the generator key
   * and the additional key are actually the same &KMS; key.
   * In `generatorId`, this &KMS; key is identified by its alias ARN.
   * In `keyIds`, this &KMS; key is identified by its key ARN.
   * In practice, you would specify different &KMS; keys,
   * or omit the `keyIds` parameter.
   * This is *only* to demonstrate how the &KMS; key ARNs are configured.
   */
  const keyIds = [
    'arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f',
  ]

  /* The &KMS; keyring must be configured with the desired &KMS; keys
   * This example passes the keyring to the caching CMM
   * instead of using it directly.
   */
  const keyring = new KmsKeyringNode({ generatorKeyId, keyIds })

  /* Create a cache to hold the data keys (and related cryptographic material).
   * This example uses the local cache provided by the Encryption SDK.
   * The `capacity` value represents the maximum number of entries
   * that the cache can hold.
   * To make room for an additional entry,
   * the cache evicts the oldest cached entry.
   * Both encrypt and decrypt requests count independently towards this threshold.
   * Entries that exceed any cache threshold are actively removed from the cache.
   * By default, the SDK checks one item in the cache every 60 seconds (60,000 milliseconds).
   * To change this frequency, pass in a `proactiveFrequency` value
   * as the second parameter. This value is in milliseconds.
   */
  const capacity = 100
  const cache = getLocalCryptographicMaterialsCache(capacity)

  /* The partition name lets multiple caching CMMs share the same local cryptographic cache.
   * By default, the entries for each CMM are cached separately. However, if you want these CMMs to share the cache,
   * use the same partition name for both caching CMMs.
   * If you don't supply a partition name, the Encryption SDK generates a random name for each caching CMM.
   * As a result, sharing elements in the cache MUST be an intentional operation.
   */
  const partition = 'local partition name'

  /* maxAge is the time in milliseconds that an entry will be cached.
   * Elements are actively removed from the cache.
   */
  const maxAge = 1000 * 60

  /* The maximum amount of bytes that will be encrypted under a single data key.
   * This value is optional,
   * but you should configure the lowest value possible.
   */
  const maxBytesEncrypted = 100

  /* The maximum number of messages that will be encrypted under a single data key.
   * This value is optional,
   * but you should configure the lowest value possible.
   */
  const maxMessagesEncrypted = 10

  const cachingCMM = new NodeCachingMaterialsManager({
    backingMaterials: keyring,
    cache,
    partition,
    maxAge,
    maxBytesEncrypted,
    maxMessagesEncrypted,
  })

  /* Encryption context is a *very* powerful tool for controlling
   * and managing access.
   * When you pass an encryption context to the encrypt function,
   * the encryption context is cryptographically bound to the ciphertext.
   * If you don't pass in the same encryption context when decrypting,
   * the decrypt function fails.
   * The encryption context is ***not*** secret!
   * Encrypted data is opaque.
   * You can use an encryption context to assert things about the encrypted data.
   * The encryption context helps you to determine
   * whether the ciphertext you retrieved is the ciphertext you expect to decrypt.
   * For example, if you are are only expecting data from 'us-west-2',
   * the appearance of a different AWS Region in the encryption context can indicate malicious interference.
   * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
   *
   * Also, cached data keys are reused ***only*** when the encryption contexts passed into the functions are an exact case-sensitive match.
   * See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/data-caching-details.html#caching-encryption-context
   */
  const encryptionContext = {
    stage: 'demo',
    purpose: 'simple demonstration app',
    origin: 'us-west-2',
  }

  /* Find data to encrypt.  A simple string. */
  const cleartext = 'asdf'

  /* Encrypt the data.
   * The caching CMM only reuses data keys
   * when it know the length (or an estimate) of the plaintext.
   * If you do not know the length,
   * because the data is a stream
   * provide an estimate of the largest expected value.
   *
   * If your estimate is smaller than the actual plaintext length
   * the AWS Encryption SDK will throw an exception.
   *
   * If the plaintext is not a stream,
   * the AWS Encryption SDK uses the actual plaintext length
   * instead of any length you provide.
   */
  const { result } = await encrypt(cachingCMM, cleartext, {
    encryptionContext,
    plaintextLength: 4,
  })

  /* Decrypt the data.
   * NOTE: This decrypt request will not use the data key
   * that was cached during the encrypt operation.
   * Data keys for encrypt and decrypt operations are cached separately.
   */
  const { plaintext, messageHeader } = await decrypt(cachingCMM, result)

  /* Grab the encryption context so you can verify it. */
  const { encryptionContext: decryptedContext } = messageHeader

  /* Verify the encryption context.
   * If you use an algorithm suite with signing,
   * the Encryption SDK adds a name-value pair to the encryption context that contains the public key.
   * Because the encryption context might contain additional key-value pairs,
   * do not include a test that requires that all key-value pairs match.
   * Instead, verify that the key-value pairs that you supplied to the `encrypt` function are included in the encryption context that the `decrypt` function returns.
   */
  Object.entries(encryptionContext).forEach(([key, value]) => {
    if (decryptedContext[key] !== value)
      throw new Error('Encryption Context does not match expected values')
  })

  /* Return the values so the code can be tested. */
  return { plaintext, result, cleartext, messageHeader }
}
```

------
#### [ Python ]

```
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
"""Example of encryption with data key caching."""
import aws_encryption_sdk
from aws_encryption_sdk import CommitmentPolicy


def encrypt_with_caching(kms_key_arn, max_age_in_cache, cache_capacity):
    """Encrypts a string using an &KMS; key and data key caching.

    :param str kms_key_arn: Amazon Resource Name (ARN) of the &KMS; key
    :param float max_age_in_cache: Maximum time in seconds that a cached entry can be used
    :param int cache_capacity: Maximum number of entries to retain in cache at once
    """
    # Data to be encrypted
    my_data = "My plaintext data"

    # Security thresholds
    #   Max messages (or max bytes per) data key are optional
    MAX_ENTRY_MESSAGES = 100

    # Create an encryption context
    encryption_context = {"purpose": "test"}

    # Set up an encryption client with an explicit commitment policy. Note that if you do not explicitly choose a
    # commitment policy, REQUIRE_ENCRYPT_REQUIRE_DECRYPT is used by default.
    client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)

    # Create a master key provider for the &KMS; key
    key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(key_ids=[kms_key_arn])

    # Create a local cache
    cache = aws_encryption_sdk.LocalCryptoMaterialsCache(cache_capacity)

    # Create a caching CMM
    caching_cmm = aws_encryption_sdk.CachingCryptoMaterialsManager(
        master_key_provider=key_provider,
        cache=cache,
        max_age=max_age_in_cache,
        max_messages_encrypted=MAX_ENTRY_MESSAGES,
    )

    # When the call to encrypt data specifies a caching CMM,
    # the encryption operation uses the data key cache specified
    # in the caching CMM
    encrypted_message, _header = client.encrypt(
        source=my_data, materials_manager=caching_cmm, encryption_context=encryption_context
    )

    return encrypted_message
```

------

# 設定快取安全性閾值
<a name="thresholds"></a>

當您實作資料金鑰快取時，您需要設定[快取 CMM](data-caching-details.md#caching-cmm) 強制執行的安全閾值。

這類安全性閾值能協助您限制每個已快取資料金鑰可使用的時間長短，以及依據每個資料金鑰可以保護的資料數量。快取 CMM 只會在快取項目符合所有安全閾值時傳回快取的資料金鑰。如果快取項目超過任何一個閾值，該項目就不能用於目前的操作，並將盡快從快取中移出。每個資料金鑰的初次使用 (在快取之前) 不會納入這些閾值中。

根據經驗，最好是使用數量最低、但能滿足成本和效能目標的快取數量。

 AWS Encryption SDK 唯一的 會快取使用金鑰[衍生函數加密的資料金鑰](https://en.wikipedia.org/wiki/Key_derivation_function)。此外，它會建立某些閾值的上限值。這些限制能確保，資料金鑰重複使用數量不會超過其加密限制。不過，因為您的純文字資料金鑰已經過快取處理 (預設是記憶體內快取)，所以請嘗試將金鑰儲存時間降到最短。此外，請嘗試限制會在金鑰遭洩時可能受到暴露的資料。

如需設定快取安全閾值的範例，請參閱 AWS 安全部落格中的[AWS Encryption SDK：如何判斷資料金鑰快取是否適合您的應用程式](https://aws.amazon.com/blogs/security/aws-encryption-sdk-how-to-decide-if-data-key-caching-is-right-for-your-application/)。

**注意**  
快取 CMM 會強制執行下列所有閾值。如果您未指定選用值，則快取 CMM 會使用預設值。  
若要暫時停用資料金鑰快取， 的 Java 和 Python 實作 AWS Encryption SDK 會提供 *null 密碼編譯資料快取* (null 快取）。Null 快取會為每個 `GET` 請求傳回錯過，而且不會回應任何 `PUT` 請求。建議您使用 null 快取，而不要將[快取容量](data-caching-details.md#simplecache)或安全性閾值設定為 0。如需詳細資訊，請參閱 [Java](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/caching/NullCryptoMaterialsCache.html) 和 [Python](https://aws-encryption-sdk-python.readthedocs.io/en/latest/generated/aws_encryption_sdk.caches.null.html) 中的 null 快取。

**最大存留期 (必要)**  
決定快取項目可以使用的時間長度，從項目加入起開始計時。此值為必填。輸入大於 0 的數值。 AWS Encryption SDK 不會限制最長存留期值。  
的所有語言實作都會以秒為單位 AWS Encryption SDK 定義最長存留期，但 適用於 JavaScript 的 AWS Encryption SDK使用毫秒的 除外。  
使用可讓應用程式繼續發揮快取優勢的最短間隔。您可以使用像金鑰輪換政策的最大存留期閾值。使用它來限制資料金鑰的重複使用，大幅降低加密資料暴露的風險，以及移出在受到快取時可能造成政策改變的資料金鑰。

**最大加密訊息數 (選用)**  
指定快取資料金鑰可以加密的最大訊息數。此值是選用的。請輸入介於 1 到 2^32 之間的訊息數。預設值為 2^32 則訊息。  
每個快取金鑰可以保護之訊息數量的設定值，應該要大至能夠取得重複使用次數值、但又能小至能夠限制當金鑰遭到洩漏時可能受到暴露的訊息數。

**最大加密位元組數 (選用)**  
指定快取資料金鑰可以加密的最大位元組數。此值是選用的。請輸入介於 0 到 2^63 - 1 之間的值。預設值為 2^63 - 1。值為 0 只允許您在加密空的訊息字串時使用資料金鑰快取。  
評估此閾值時，將會納入目前請求中的位元組。如果已處理的位元組加上目前位元組超過該閾值，則快取的資料金鑰從會從快取移出，即使該金鑰已用於較小的請求。

# 資料金鑰快取詳細資訊
<a name="data-caching-details"></a>

大多數應用程式可以使用預設的資料金鑰快取實作，無須撰寫自訂程式碼。本節說明預設實作和一些選項詳細資訊。

**Topics**
+ [資料金鑰快取的運作方式](#how-caching-works)
+ [建立密碼編譯資料快取](#simplecache)
+ [建立快取密碼編譯資料管理員](#caching-cmm)
+ [資料金鑰快取項目中有什麼項目？](#cache-entries)
+ [加密內容：如何選擇快取項目](#caching-encryption-context)
+ [我的應用程式是否使用快取的資料金鑰？](#caching-effect)

## 資料金鑰快取的運作方式
<a name="how-caching-works"></a>

當您在請求中使用資料金鑰快取來加密或解密資料時， AWS Encryption SDK 會先搜尋快取中是否有符合請求的資料金鑰。如果找到有效的相符項目，則使用快取的資料金鑰來加密資料。否則，它會產生一個新的資料金鑰，就像沒有快取一樣。

資料金鑰快取不會用於不明大小的資料，例如串流資料。這可讓快取 CMM 正確強制執行[最大位元組閾值](thresholds.md)。若要避免這種行為，請將訊息大小新增至加密請求。

除了快取之外，資料金鑰快取使用[快取密碼編譯資料管理員](#caching-cmm) （快取 CMM)。快取 CMM 是專門[的密碼編譯資料管理員 (CMM)](concepts.md#crypt-materials-manager)，可與[快取](#simplecache)和基礎 [CMM](concepts.md#crypt-materials-manager) 互動。（當您指定[主金鑰提供者](concepts.md#master-key-provider)或 keyring 時， AWS Encryption SDK 會為您建立預設 CMM。) 快取 CMM 會快取其基礎 CMM 傳回的資料金鑰。快取 CMM 也會強制執行您設定的快取安全閾值。

為避免從快取選取錯誤的資料金鑰，所有相容的快取 CMM 都要求快取的密碼編譯資料的下列屬性符合資料請求。
+ [演算法套件](concepts.md#crypto-algorithm)
+ [加密內容](#caching-encryption-context) (即使為空)
+ 分割區名稱 （識別快取 CMM 的字串）
+ (僅限解密) 加密的資料金鑰

**注意**  
只有在[演算法套件](concepts.md#crypto-algorithm)使用金鑰衍生函數時， 才會 AWS Encryption SDK 快取資料金鑰。 [https://en.wikipedia.org/wiki/Key_derivation_function](https://en.wikipedia.org/wiki/Key_derivation_function)

以下工作流程示範在有和沒有資料金鑰快取的情形下，請求如何處理加密資料。它們顯示如何在程序中使用您建立的快取元件，包括快取和快取 CMM。

### 加密資料，不使用快取
<a name="workflow-wo-cache"></a>

若要取得加密資料，但不透過快取：

1. 應用程式 AWS Encryption SDK 會要求 加密資料。

   請求會指定主金鑰提供者或 keyring。 AWS Encryption SDK 會建立與您的主金鑰提供者或 keyring 互動的預設 CMM。

1.  AWS Encryption SDK 會向 CMM 要求加密資料 （取得密碼編譯資料）。

1. CMM 會要求其 [keyring](concepts.md#keyring) (C 和 JavaScript) 或[主金鑰提供者](concepts.md#master-key-provider) (Java 和 Python) 提供密碼編譯資料。這可能包括呼叫密碼編譯服務，例如 AWS Key Management Service (AWS KMS)。CMM 會將加密資料傳回至 AWS Encryption SDK。

1.  AWS Encryption SDK 使用純文字資料金鑰來加密資料。它將加密的資料和加密的資料金鑰存放它傳回給使用者的[已加密訊息](concepts.md#message)中。

![\[加密資料，不使用快取\]](http://docs.aws.amazon.com/zh_tw/encryption-sdk/latest/developer-guide/images/encrypt-workflow-no-cache.png)


### 加密資料，使用快取
<a name="workflow-with-cache"></a>

若要透過資料金鑰快取來取得加密資料：

1. 應用程式 AWS Encryption SDK 會要求 加密資料。

   請求會指定[與基礎密碼編譯資料管理員 (CMM) 相關聯的快取密碼編譯資料管理員 （快取](#caching-cmm) CMM)。當您指定主金鑰提供者或 keyring 時， AWS Encryption SDK 會為您建立預設 CMM。

1. SDK 會向指定的快取 CMM 要求加密資料。

1. 快取 CMM 會從快取請求加密資料。

   1. 如果快取找到相符項目，則會更新存留期並使用相符快取項目的值，並將快取的加密資料傳回快取 CMM。

      如果快取項目符合其[安全閾值](thresholds.md)，快取 CMM 會將其傳回 SDK。否則，它會通知快取移出項目，並依沒有相符項目的情形繼續進行。

   1. 如果快取找不到有效的相符項目，快取 CMM 會要求其基礎 CMM 產生新的資料金鑰。

      基礎 CMM 會從其 keyring (C 和 JavaScript) 或主金鑰提供者 (Java 和 Python) 取得密碼編譯資料。這可能牽涉到呼叫服務，例如 AWS Key Management Service。基礎 CMM 會將資料金鑰的純文字和加密複本傳回快取 CMM。

      快取 CMM 會將新的加密資料儲存在快取中。

1. 快取 CMM 會將加密資料傳回 AWS Encryption SDK。

1.  AWS Encryption SDK 使用純文字資料金鑰來加密資料。它將加密的資料和加密的資料金鑰存放它傳回給使用者的[已加密訊息](concepts.md#message)中。

![\[加密資料，使用資料金鑰快取\]](http://docs.aws.amazon.com/zh_tw/encryption-sdk/latest/developer-guide/images/encrypt-workflow-with-cache.png)


## 建立密碼編譯資料快取
<a name="simplecache"></a>

 AWS Encryption SDK 定義用於資料金鑰快取的密碼編譯資料快取需求。它還提供本機快取，這是可設定、記憶體內、[最近最少使用的 (LRU) 快取](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29)。若要建立本機快取的執行個體，請使用 Java `LocalCryptoMaterialsCache` 和 Python 中的建構函數、JavaScript 中的 getLocalCryptographicMaterialsCache 函數，或 C `aws_cryptosdk_materials_cache_local_new` 中的建構函數。

本機快取包含基本快取管理的邏輯，包括新增、移出和比對快取項目，以及維護快取。您不需要撰寫任何自訂快取管理邏輯。您可以正常使用本機快取、自訂快取，或取代任何相容的快取。

建立本機快取時，您可以設定其*容量*，也就是快取可保留的項目數量上限。此設定可協助您以有限的資料金鑰重複使用來設計有效快取。

 適用於 JAVA 的 AWS Encryption SDK 和 適用於 Python 的 AWS Encryption SDK 也提供 *null 密碼編譯資料快取* (NullCryptoMaterialsCache)。NullCryptoMaterialsCache 會傳回所有`GET`操作的遺漏，而不會回應`PUT`操作。您可以使用 NullCryptoMaterialsCache 進行測試，或暫時停用包含快取程式碼之應用程式中的快取。

在 中 AWS Encryption SDK，每個密碼編譯資料快取都與[快取密碼編譯資料管理員](#caching-cmm) （快取 CMM) 相關聯。快取 CMM 會從快取取得資料金鑰、將資料金鑰放入快取，以及強制執行您設定[的安全閾值](thresholds.md)。當您建立快取 CMM 時，您可以指定其使用的快取，以及產生其快取之資料金鑰的基礎 CMM 或主金鑰提供者。

## 建立快取密碼編譯資料管理員
<a name="caching-cmm"></a>

若要啟用資料金鑰快取，您可以建立[快取](#simplecache)和*快取密碼編譯資料管理員* （快取 CMM)。然後，在加密或解密資料的請求中，您可以指定快取 CMM，而不是標準[密碼編譯資料管理器 (CMM)](concepts.md#crypt-materials-manager)，或[主金鑰提供者](concepts.md#master-key-provider)或 [keyring](concepts.md#keyring)。

CMMs有兩種類型。兩者都會取得資料金鑰 (以及相關密碼編譯資料)，但使用的方法不同，如下所示：
+ CMM 與 keyring (C 或 JavaScript) 或主金鑰提供者 (Java 和 Python) 相關聯。當 SDK 向 CMM 要求加密或解密資料時，CMM 會從其 keyring 或主金鑰提供者取得資料。在 Java 和 Python 中，CMM 使用主金鑰來產生、加密或解密資料金鑰。在 C 和 JavaScript 中， keyring 會產生、加密和傳回密碼編譯資料。
+ 快取 CMM 與一個快取相關聯，例如[本機快取](#simplecache)和基礎 CMM。當 SDK 向快取 CMM 要求密碼編譯資料時，快取 CMM 會嘗試從快取取得資料。如果找不到相符項目，快取 CMM 會向其基礎 CMM 要求材料。接著，它在將新的密碼編譯資料傳回給發起人之前會快取資料。

快取 CMM 也會強制執行您為每個快取項目設定[的安全閾值](thresholds.md)。由於安全閾值是在 中設定並由快取 CMM 強制執行，因此即使快取不是針對敏感材料而設計，您也可以使用任何相容的快取。

## 資料金鑰快取項目中有什麼項目？
<a name="cache-entries"></a>

資料金鑰快取會在快取中存放資料金鑰和相關密碼編譯資料。每個項目都包含下面列出的元素。當您決定是否使用資料金鑰快取功能，以及在快取密碼編譯資料管理員 （快取 CMM) 上設定安全閾值時，您可能會發現此資訊很有用。

**加密請求的快取項目**  
由於加密操作而加入資料金鑰快取的項目包含下列元素：
+ 純文字資料金鑰
+ 加密的資料金鑰 (一或多個)
+ [加密內容](#caching-encryption-context) 
+ 訊息簽署金鑰 (如果使用)
+ [演算法套件](concepts.md#crypto-algorithm)
+ 用於強制執行安全性閾值的中繼資料，包括用量計數器

**解密請求的快取項目**  
由於解密操作而加入資料金鑰快取的項目包含下列元素：
+ 純文字資料金鑰
+ 簽章驗證金鑰 (如果使用)
+ 用於強制執行安全性閾值的中繼資料，包括用量計數器

## 加密內容：如何選擇快取項目
<a name="caching-encryption-context"></a>

您可以在加密資料的請求中指定加密內容。不過，加密內容在資料金鑰快取中扮演特殊角色。它可讓您在快取中建立資料金鑰的子群組，即使資料金鑰來自相同的快取 CMM。

[加密內容](concepts.md#encryption-context)是一組金鑰/值對，其中包含任意非私密資料。在加密期間，加密內容會以密碼演算法繫結至加密的資料，因此在解密資料時需要相同的加密內容。在 中 AWS Encryption SDK，加密內容會存放在具有加密資料和資料金鑰的加密[訊息](concepts.md#message)中。

使用資料金鑰快取時，您也可以使用快取內容來為您的加密操作選擇特定的快取資料金鑰。加密內容會與會資料金鑰一起儲存在快取項目中 (屬於快取項目 ID 的一部分)。快取的資料金鑰只在其加密內容符合時才會重複使用。如果您想對加密請求重複使用特定的資料金鑰，請指定相同的加密內容。如果您想避免使用這些資料金鑰，請指定不同的加密內容。

加密內容一律是選用的，但建議使用。如果您不在請求中指定加密內容，則會在快取項目識別符中加入空的加密內容，並符合每個請求。

## 我的應用程式是否使用快取的資料金鑰？
<a name="caching-effect"></a>

資料金鑰快取是對某些應用程式和工作負載非常有效的最佳化策略。不過，因為它需要一些風險，請務必判斷它對您的情況如何有效，然後決定優點是否大於風險。

因為資料金鑰快取會重複使用資料金鑰，最明顯的效果就是減少產生新資料金鑰的呼叫次數。實作資料金鑰快取時， 只會 AWS Encryption SDK 呼叫 AWS KMS `GenerateDataKey`操作來建立初始資料金鑰，以及快取遺漏時。但是，快取只有在會產生多個具有相同特性 (包括相同加密內容和演算法套件) 的資料金鑰應用程式中，才能明顯地改善效能。

若要判斷您的 實作是否 AWS Encryption SDK 實際使用快取中的資料金鑰，請嘗試下列技術。
+ 在主金鑰基礎設施的日誌中，檢查呼叫的頻率，以建立新的資料金鑰。當資料金鑰快取有效時，建立新金鑰的呼叫次數應該會明顯下降。例如，如果您使用 AWS KMS 主金鑰提供者或 keyring，請在 CloudTrail 日誌中搜尋 [GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) 呼叫。
+ 比較 AWS Encryption SDK 傳回以回應不同加密請求的加密[訊息](concepts.md#message)。例如，如果您使用的是 適用於 JAVA 的 AWS Encryption SDK，請比較來自不同加密呼叫的 [ParsedCiphertext](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/ParsedCiphertext.html) 物件。在 中 適用於 JavaScript 的 AWS Encryption SDK，比較 [MessageHeader](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/serialize/src/types.ts#L21) `encryptedDataKeys` 屬性的內容。重複使用資料金鑰時，加密訊息中的加密資料金鑰是相同的。

# 資料金鑰快取範例
<a name="sample-cache-example"></a>

此範例使用[資料金鑰快取](data-key-caching.md)搭配[本機快取](data-caching-details.md#simplecache)，以加速應用程式，其中由多個裝置產生的資料會加密並儲存在不同的 區域中。

在此案例中，多個資料生產者會產生資料、加密資料，並寫入每個區域中的 [Kinesis 串流](https://aws.amazon.com/kinesis/streams/)。 [AWS Lambda](https://aws.amazon.com/lambda/)函數 （取用者） 會解密串流，並將純文字資料寫入區域中的 DynamoDB 資料表。資料生產者和消費者使用 AWS Encryption SDK 和 [AWS KMS 主金鑰提供者](concepts.md#master-key-provider)。為了減少對 KMS 的呼叫，每個生產者和消費者都有自己的本機快取。

您可以在 [Java 和 Python](sample-cache-example-code.md) 中找到這些範例的原始程式碼。此範例也包含定義範例資源的 CloudFormation 範本。

![\[此圖表顯示資料生產者和消費者如何使用 AWS KMS、Amazon Kinesis Data Streams 和 Amazon DynamoDB。\]](http://docs.aws.amazon.com/zh_tw/encryption-sdk/latest/developer-guide/images/simplecache-example.png)


## 本機快取結果
<a name="caching-example-impact"></a>

下表顯示本機快取會將此範例中對 KMS 的呼叫總數 （每個區域每秒） 減少為原始值的 1%。


**製作者請求**  
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/encryption-sdk/latest/developer-guide/sample-cache-example.html)


**消費者請求**  
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/encryption-sdk/latest/developer-guide/sample-cache-example.html)

# 資料金鑰快取範例程式碼
<a name="sample-cache-example-code"></a>

此程式碼範例會在 Java 和 Python 中使用[本機快取](data-caching-details.md#simplecache)建立簡單的資料金鑰快取實作。此程式碼會建立本機快取的兩個執行個體：一個用於加密[資料的資料生產者](#caching-producer)，另一個用於解密資料[的資料消費者](#caching-consumer) (AWS Lambda 函數）。如需在每種語言中實作資料金鑰快取的詳細資訊，請參閱 的 [Javadoc](https://aws.github.io/aws-encryption-sdk-java/) 和 [Python 文件](https://aws-encryption-sdk-python.readthedocs.io/en/latest/) AWS Encryption SDK。

資料金鑰快取適用於 AWS Encryption SDK 支援的所有[程式設計語言](programming-languages.md)。

如需在 中使用資料金鑰快取的完整和測試範例 AWS Encryption SDK，請參閱：
+ C/C\$1\$1：[caching\$1cmm.cpp](https://github.com/aws/aws-encryption-sdk-c/blob/master/examples/caching_cmm.cpp) 
+ Java：[SimpleDataKeyCachingExample.java](https://github.com/aws/aws-encryption-sdk-java/blob/master/src/examples/java/com/amazonaws/crypto/examples/v2/SimpleDataKeyCachingExample.java)
+ JavaScript 瀏覽器：[caching\$1cmm.ts](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-browser/src/caching_cmm.ts) 
+ JavaScript Node.js：[caching\$1cmm.ts](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-node/src/caching_cmm.ts) 
+ Python：[data\$1key\$1caching\$1basic.py](https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/legacy/data_key_caching_basic.py)

## 生產者
<a name="caching-producer"></a>

生產者取得映射、將其轉換為 JSON、使用 AWS Encryption SDK 來加密它，並將加密文字記錄推送至每個 中的 [Kinesis 串流](https://aws.amazon.com/kinesis/streams/) AWS 區域。

此程式碼會定義[快取密碼編譯資料管理員](data-caching-details.md#caching-cmm) （快取 CMM)，並將其與[本機快取](data-caching-details.md#simplecache)和基礎[AWS KMS 主金鑰提供者](concepts.md#master-key-provider)建立關聯。快取 CMM 會從主金鑰提供者快取資料金鑰 （和[相關的密碼編譯資料](data-caching-details.md#cache-entries))。它也會代表開發套件與快取互動，並強制執行您設定的安全性閾值。

由於呼叫加密方法會指定快取 CMM，而不是一般[密碼編譯資料管理員 (CMM)](concepts.md#crypt-materials-manager) 或主金鑰提供者，因此加密將使用資料金鑰快取。

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

下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。使用 3.*x* 版時，您也可以使用[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

```
/*
 * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
 * in compliance with the License. A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.amazonaws.crypto.examples.kinesisdatakeycaching;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CommitmentPolicy;
import com.amazonaws.encryptionsdk.CryptoResult;
import com.amazonaws.encryptionsdk.MasterKeyProvider;
import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache;
import com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKey;
import com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider;
import com.amazonaws.encryptionsdk.multi.MultipleProviderFactory;
import com.amazonaws.util.json.Jackson;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisClient;
import software.amazon.awssdk.services.kms.KmsClient;

/**
 * Pushes data to Kinesis Streams in multiple Regions.
 */
public class MultiRegionRecordPusher {

    private static final long MAX_ENTRY_AGE_MILLISECONDS = 300000;
    private static final long MAX_ENTRY_USES = 100;
    private static final int MAX_CACHE_ENTRIES = 100;
    private final String streamName_;
    private final ArrayList<KinesisClient> kinesisClients_;
    private final CachingCryptoMaterialsManager cachingMaterialsManager_;
    private final AwsCrypto crypto_;

    /**
     * Creates an instance of this object with Kinesis clients for all target Regions and a cached
     * key provider containing KMS master keys in all target Regions.
     */
    public MultiRegionRecordPusher(final Region[] regions, final String kmsAliasName,
        final String streamName) {
        streamName_ = streamName;
        crypto_ = AwsCrypto.builder()
            .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt)
            .build();
        kinesisClients_ = new ArrayList<>();

        AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.builder().build();

        // Build KmsMasterKey and AmazonKinesisClient objects for each target region
        List<KmsMasterKey> masterKeys = new ArrayList<>();
        for (Region region : regions) {
            kinesisClients_.add(KinesisClient.builder()
                .credentialsProvider(credentialsProvider)
                .region(region)
                .build());

            KmsMasterKey regionMasterKey = KmsMasterKeyProvider.builder()
                .defaultRegion(region)
                .builderSupplier(() -> KmsClient.builder().credentialsProvider(credentialsProvider))
                .buildStrict(kmsAliasName)
                .getMasterKey(kmsAliasName);

            masterKeys.add(regionMasterKey);
        }

        // Collect KmsMasterKey objects into single provider and add cache
        MasterKeyProvider<?> masterKeyProvider = MultipleProviderFactory.buildMultiProvider(
            KmsMasterKey.class,
            masterKeys
        );

        cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder()
            .withMasterKeyProvider(masterKeyProvider)
            .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES))
            .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS)
            .withMessageUseLimit(MAX_ENTRY_USES)
            .build();
    }

    /**
     * JSON serializes and encrypts the received record data and pushes it to all target streams.
     */
    public void putRecord(final Map<Object, Object> data) {
        String partitionKey = UUID.randomUUID().toString();
        Map<String, String> encryptionContext = new HashMap<>();
        encryptionContext.put("stream", streamName_);

        // JSON serialize data
        String jsonData = Jackson.toJsonString(data);

        // Encrypt data
        CryptoResult<byte[], ?> result = crypto_.encryptData(
            cachingMaterialsManager_,
            jsonData.getBytes(),
            encryptionContext
        );
        byte[] encryptedData = result.getResult();

        // Put records to Kinesis stream in all Regions
        for (KinesisClient regionalKinesisClient : kinesisClients_) {
            regionalKinesisClient.putRecord(builder ->
                builder.streamName(streamName_)
                    .data(SdkBytes.fromByteArray(encryptedData))
                    .partitionKey(partitionKey));
        }
    }
}
```

------
#### [ Python ]

```
"""
Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
in compliance with the License. A copy of the License is located at
 
https://aws.amazon.com/apache-2-0/
 
or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
import json
import uuid
 
from aws_encryption_sdk import EncryptionSDKClient, StrictAwsKmsMasterKeyProvider, CachingCryptoMaterialsManager, LocalCryptoMaterialsCache, CommitmentPolicy
from aws_encryption_sdk.key_providers.kms import KMSMasterKey
import boto3
 
 
class MultiRegionRecordPusher(object):
    """Pushes data to Kinesis Streams in multiple Regions."""
    CACHE_CAPACITY = 100
    MAX_ENTRY_AGE_SECONDS = 300.0
    MAX_ENTRY_MESSAGES_ENCRYPTED = 100
 
    def __init__(self, regions, kms_alias_name, stream_name):
        self._kinesis_clients = []
        self._stream_name = stream_name
 
        # Set up EncryptionSDKClient
        _client = EncryptionSDKClient(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)
 
        # Set up KMSMasterKeyProvider with cache
        _key_provider = StrictAwsKmsMasterKeyProvider(kms_alias_name)
 
        # Add MasterKey and Kinesis client for each Region
        for region in regions:
            self._kinesis_clients.append(boto3.client('kinesis', region_name=region))
            regional_master_key = KMSMasterKey(
                client=boto3.client('kms', region_name=region),
                key_id=kms_alias_name
            )
            _key_provider.add_master_key_provider(regional_master_key)
 
        cache = LocalCryptoMaterialsCache(capacity=self.CACHE_CAPACITY)
        self._materials_manager = CachingCryptoMaterialsManager(
            master_key_provider=_key_provider,
            cache=cache,
            max_age=self.MAX_ENTRY_AGE_SECONDS,
            max_messages_encrypted=self.MAX_ENTRY_MESSAGES_ENCRYPTED
        )
 
    def put_record(self, record_data):
        """JSON serializes and encrypts the received record data and pushes it to all target streams.
 
        :param dict record_data: Data to write to stream
        """
        # Kinesis partition key to randomize write load across stream shards
        partition_key = uuid.uuid4().hex
 
        encryption_context = {'stream': self._stream_name}
 
        # JSON serialize data
        json_data = json.dumps(record_data)
 
        # Encrypt data
        encrypted_data, _header = _client.encrypt(
            source=json_data,
            materials_manager=self._materials_manager,
            encryption_context=encryption_context
        )
 
        # Put records to Kinesis stream in all Regions
        for client in self._kinesis_clients:
            client.put_record(
                StreamName=self._stream_name,
                Data=encrypted_data,
                PartitionKey=partition_key
            )
```

------

## 消費者
<a name="caching-consumer"></a>

資料取用者是由 [Kinesis](https://aws.amazon.com/kinesis/) 事件觸發的 [AWS Lambda](https://aws.amazon.com/lambda/)函數。它會解密和還原序列化每個記錄，並將純文字記錄寫入相同區域中的 [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) 資料表。

如同生產者程式碼，取用者程式碼會在對解密方法的呼叫中使用快取密碼編譯資料管理員 （快取 CMM) 來啟用資料金鑰快取。

Java 程式碼會使用指定的 ，在*嚴格模式下*建置主金鑰提供者 AWS KMS key。解密時不需要嚴格模式，但這是[最佳實務](best-practices.md#strict-discovery-mode)。Python 程式碼使用*探索模式*，可讓 AWS Encryption SDK 使用任何加密資料金鑰的包裝金鑰來解密它。

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

下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.*x* 版。3.*x* 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。使用 3.*x* 版時，您也可以使用[AWS KMS 階層式 keyring](use-hierarchical-keyring.md)，這是替代的密碼編譯資料快取解決方案。

此程式碼會建立主金鑰提供者，以嚴格模式解密。只能使用 AWS KMS keys 您指定的 AWS Encryption SDK 來解密訊息。

```
/*
 * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
 * in compliance with the License. A copy of the License is located at
 *
 * http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.amazonaws.crypto.examples.kinesisdatakeycaching;

import com.amazonaws.encryptionsdk.AwsCrypto;
import com.amazonaws.encryptionsdk.CommitmentPolicy;
import com.amazonaws.encryptionsdk.CryptoResult;
import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager;
import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache;
import com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord;
import com.amazonaws.util.BinaryUtils;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;

/**
 * Decrypts all incoming Kinesis records and writes records to DynamoDB.
 */
public class LambdaDecryptAndWrite {

    private static final long MAX_ENTRY_AGE_MILLISECONDS = 600000;
    private static final int MAX_CACHE_ENTRIES = 100;
    private final CachingCryptoMaterialsManager cachingMaterialsManager_;
    private final AwsCrypto crypto_;
    private final DynamoDbTable<Item> table_;

    /**
     * Because the cache is used only for decryption, the code doesn't set the max bytes or max
     * message security thresholds that are enforced only on on data keys used for encryption.
     */
    public LambdaDecryptAndWrite() {
        String kmsKeyArn = System.getenv("CMK_ARN");
        cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder()
            .withMasterKeyProvider(KmsMasterKeyProvider.builder().buildStrict(kmsKeyArn))
            .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES))
            .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS)
            .build();

        crypto_ = AwsCrypto.builder()
            .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt)
            .build();

        String tableName = System.getenv("TABLE_NAME");
        DynamoDbEnhancedClient dynamodb = DynamoDbEnhancedClient.builder().build();
        table_ = dynamodb.table(tableName, TableSchema.fromClass(Item.class));
    }

    /**
     * @param event
     * @param context
     */
    public void handleRequest(KinesisEvent event, Context context)
        throws UnsupportedEncodingException {
        for (KinesisEventRecord record : event.getRecords()) {
            ByteBuffer ciphertextBuffer = record.getKinesis().getData();
            byte[] ciphertext = BinaryUtils.copyAllBytesFrom(ciphertextBuffer);

            // Decrypt and unpack record
            CryptoResult<byte[], ?> plaintextResult = crypto_.decryptData(cachingMaterialsManager_,
                ciphertext);

            // Verify the encryption context value
            String streamArn = record.getEventSourceARN();
            String streamName = streamArn.substring(streamArn.indexOf("/") + 1);
            if (!streamName.equals(plaintextResult.getEncryptionContext().get("stream"))) {
                throw new IllegalStateException("Wrong Encryption Context!");
            }

            // Write record to DynamoDB
            String jsonItem = new String(plaintextResult.getResult(), StandardCharsets.UTF_8);
            System.out.println(jsonItem);
            table_.putItem(Item.fromJSON(jsonItem));
        }
    }

    private static class Item {

        static Item fromJSON(String jsonText) {
            // Parse JSON and create new Item
            return new Item();
        }
    }
}
```

------
#### [ Python ]

此 Python 程式碼會使用探索模式中的主金鑰提供者進行解密。它可讓 AWS Encryption SDK 使用任何加密資料金鑰的包裝金鑰來解密它。嚴格模式是[最佳實務](best-practices.md#strict-discovery-mode)，您可以在其中指定可用於解密的包裝金鑰。

```
"""
Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
in compliance with the License. A copy of the License is located at
 
https://aws.amazon.com/apache-2-0/
 
or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
import base64
import json
import logging
import os
 
from aws_encryption_sdk import EncryptionSDKClient, DiscoveryAwsKmsMasterKeyProvider, CachingCryptoMaterialsManager, LocalCryptoMaterialsCache, CommitmentPolicy
import boto3
 
_LOGGER = logging.getLogger(__name__)
_is_setup = False
CACHE_CAPACITY = 100
MAX_ENTRY_AGE_SECONDS = 600.0
 
def setup():
    """Sets up clients that should persist across Lambda invocations."""
    global encryption_sdk_client
    encryption_sdk_client = EncryptionSDKClient(CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)
 
    global materials_manager
    key_provider = DiscoveryAwsKmsMasterKeyProvider()
    cache = LocalCryptoMaterialsCache(capacity=CACHE_CAPACITY)
           
    #  Because the cache is used only for decryption, the code doesn't set
    #   the max bytes or max message security thresholds that are enforced
    #   only on on data keys used for encryption.
    materials_manager = CachingCryptoMaterialsManager(
        master_key_provider=key_provider,
        cache=cache,
        max_age=MAX_ENTRY_AGE_SECONDS
    )
    global table
    table_name = os.environ.get('TABLE_NAME')
    table = boto3.resource('dynamodb').Table(table_name)
    global _is_setup
    _is_setup = True
 
 
def lambda_handler(event, context):
    """Decrypts all incoming Kinesis records and writes records to DynamoDB."""
    _LOGGER.debug('New event:')
    _LOGGER.debug(event)
    if not _is_setup:
        setup()
    with table.batch_writer() as batch:
        for record in event.get('Records', []):
            # Record data base64-encoded by Kinesis
            ciphertext = base64.b64decode(record['kinesis']['data'])
 
            # Decrypt and unpack record
            plaintext, header = encryption_sdk_client.decrypt(
                source=ciphertext,
                materials_manager=materials_manager
            )
            item = json.loads(plaintext)
 
            # Verify the encryption context value
            stream_name = record['eventSourceARN'].split('/', 1)[1]
            if stream_name != header.encryption_context['stream']:
                raise ValueError('Wrong Encryption Context!')
 
            # Write record to DynamoDB
            batch.put_item(Item=item)
```

------

# 資料金鑰快取範例： CloudFormation template
<a name="sample-cache-example-cloudformation"></a>

此 CloudFormation 範本會設定所有必要 AWS 的資源，以重現[資料金鑰快取範例](sample-cache-example.md)。

------
#### [ JSON ]

```
{
    "Parameters": {
        "SourceCodeBucket": {
            "Type": "String",
            "Description": "S3 bucket containing Lambda source code zip files"
        },
        "PythonLambdaS3Key": {
            "Type": "String",
            "Description": "S3 key containing Python Lambda source code zip file"
        },
        "PythonLambdaObjectVersionId": {
            "Type": "String",
            "Description": "S3 version id for S3 key containing Python Lambda source code zip file"
        },
        "JavaLambdaS3Key": {
            "Type": "String",
            "Description": "S3 key containing Python Lambda source code zip file"
        },
        "JavaLambdaObjectVersionId": {
            "Type": "String",
            "Description": "S3 version id for S3 key containing Python Lambda source code zip file"
        },
        "KeyAliasSuffix": {
            "Type": "String",
            "Description": "Suffix to use for KMS key Alias (ie: alias/KeyAliasSuffix)"
        },
        "StreamName": {
            "Type": "String",
            "Description": "Name to use for Kinesis Stream"
        }
    },
    "Resources": {
        "InputStream": {
            "Type": "AWS::Kinesis::Stream",
            "Properties": {
                "Name": {
                    "Ref": "StreamName"
                },
                "ShardCount": 2
            }
        },
        "PythonLambdaOutputTable": {
            "Type": "AWS::DynamoDB::Table",
            "Properties": {
                "AttributeDefinitions": [
                    {
                        "AttributeName": "id",
                        "AttributeType": "S"
                    }
                ],
                "KeySchema": [
                    {
                        "AttributeName": "id",
                        "KeyType": "HASH"
                    }
                ],
                "ProvisionedThroughput": {
                    "ReadCapacityUnits": 1,
                    "WriteCapacityUnits": 1
                }
            }
        },
        "PythonLambdaRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",		 	 	 
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "lambda.amazonaws.com"
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "ManagedPolicyArns": [
                    "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
                ],
                "Policies": [
                    {
                        "PolicyName": "PythonLambdaAccess",
                        "PolicyDocument": {
                            "Version": "2012-10-17",		 	 	 
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "dynamodb:DescribeTable",
                                        "dynamodb:BatchWriteItem"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${PythonLambdaOutputTable}"
                                    }
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "dynamodb:PutItem"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${PythonLambdaOutputTable}*"
                                    }
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "kinesis:GetRecords",
                                        "kinesis:GetShardIterator",
                                        "kinesis:DescribeStream",
                                        "kinesis:ListStreams"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}"
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        },
        "PythonLambdaFunction": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Description": "Python consumer",
                "Runtime": "python2.7",
                "MemorySize": 512,
                "Timeout": 90,
                "Role": {
                    "Fn::GetAtt": [
                        "PythonLambdaRole",
                        "Arn"
                    ]
                },
                "Handler": "aws_crypto_examples.kinesis_datakey_caching.consumer.lambda_handler",
                "Code": {
                    "S3Bucket": {
                        "Ref": "SourceCodeBucket"
                    },
                    "S3Key": {
                        "Ref": "PythonLambdaS3Key"
                    },
                    "S3ObjectVersion": {
                        "Ref": "PythonLambdaObjectVersionId"
                    }
                },
                "Environment": {
                    "Variables": {
                        "TABLE_NAME": {
                            "Ref": "PythonLambdaOutputTable"
                        }
                    }
                }
            }
        },
        "PythonLambdaSourceMapping": {
            "Type": "AWS::Lambda::EventSourceMapping",
            "Properties": {
                "BatchSize": 1,
                "Enabled": true,
                "EventSourceArn": {
                    "Fn::Sub": "arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}"
                },
                "FunctionName": {
                    "Ref": "PythonLambdaFunction"
                },
                "StartingPosition": "TRIM_HORIZON"
            }
        },
        "JavaLambdaOutputTable": {
            "Type": "AWS::DynamoDB::Table",
            "Properties": {
                "AttributeDefinitions": [
                    {
                        "AttributeName": "id",
                        "AttributeType": "S"
                    }
                ],
                "KeySchema": [
                    {
                        "AttributeName": "id",
                        "KeyType": "HASH"
                    }
                ],
                "ProvisionedThroughput": {
                    "ReadCapacityUnits": 1,
                    "WriteCapacityUnits": 1
                }
            }
        },
        "JavaLambdaRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",		 	 	 
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "lambda.amazonaws.com"
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "ManagedPolicyArns": [
                    "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
                ],
                "Policies": [
                    {
                        "PolicyName": "JavaLambdaAccess",
                        "PolicyDocument": {
                            "Version": "2012-10-17",		 	 	 
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "dynamodb:DescribeTable",
                                        "dynamodb:BatchWriteItem"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${JavaLambdaOutputTable}"
                                    }
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "dynamodb:PutItem"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${JavaLambdaOutputTable}*"
                                    }
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "kinesis:GetRecords",
                                        "kinesis:GetShardIterator",
                                        "kinesis:DescribeStream",
                                        "kinesis:ListStreams"
                                    ],
                                    "Resource": {
                                        "Fn::Sub": "arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}"
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        },
        "JavaLambdaFunction": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Description": "Java consumer",
                "Runtime": "java8",
                "MemorySize": 512,
                "Timeout": 90,
                "Role": {
                    "Fn::GetAtt": [
                        "JavaLambdaRole",
                        "Arn"
                    ]
                },
                "Handler": "com.amazonaws.crypto.examples.kinesisdatakeycaching.LambdaDecryptAndWrite::handleRequest",
                "Code": {
                    "S3Bucket": {
                        "Ref": "SourceCodeBucket"
                    },
                    "S3Key": {
                        "Ref": "JavaLambdaS3Key"
                    },
                    "S3ObjectVersion": {
                        "Ref": "JavaLambdaObjectVersionId"
                    }
                },
                "Environment": {
                    "Variables": {
                        "TABLE_NAME": {
                            "Ref": "JavaLambdaOutputTable"
                        },
                        "CMK_ARN": {
                            "Fn::GetAtt": [
                                "RegionKinesisCMK",
                                "Arn"
                            ]
                        }
                    }
                }
            }
        },
        "JavaLambdaSourceMapping": {
            "Type": "AWS::Lambda::EventSourceMapping",
            "Properties": {
                "BatchSize": 1,
                "Enabled": true,
                "EventSourceArn": {
                    "Fn::Sub": "arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}"
                },
                "FunctionName": {
                    "Ref": "JavaLambdaFunction"
                },
                "StartingPosition": "TRIM_HORIZON"
            }
        },
        "RegionKinesisCMK": {
            "Type": "AWS::KMS::Key",
            "Properties": {
                "Description": "Used to encrypt data passing through Kinesis Stream in this region",
                "Enabled": true,
                "KeyPolicy": {
                    "Version": "2012-10-17",		 	 	 
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": {
                                    "Fn::Sub": "arn:aws:iam::${AWS::AccountId}:root"
                                }
                            },
                            "Action": [
                                "kms:Encrypt",
                                "kms:GenerateDataKey",
                                "kms:CreateAlias",
                                "kms:DeleteAlias",
                                "kms:DescribeKey",
                                "kms:DisableKey",
                                "kms:EnableKey",
                                "kms:PutKeyPolicy",
                                "kms:ScheduleKeyDeletion",
                                "kms:UpdateAlias",
                                "kms:UpdateKeyDescription"
                            ],
                            "Resource": "*"
                        },
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": [
                                    {
                                        "Fn::GetAtt": [
                                            "PythonLambdaRole",
                                            "Arn"
                                        ]
                                    },
                                    {
                                        "Fn::GetAtt": [
                                            "JavaLambdaRole",
                                            "Arn"
                                        ]
                                    }
                                ]
                            },
                            "Action": "kms:Decrypt",
                            "Resource": "*"
                        }
                    ]
                }
            }
        },
        "RegionKinesisCMKAlias": {
            "Type": "AWS::KMS::Alias",
            "Properties": {
                "AliasName": {
                    "Fn::Sub": "alias/${KeyAliasSuffix}"
                },
                "TargetKeyId": {
                    "Ref": "RegionKinesisCMK"
                }
            }
        }
    }
}
```

------
#### [ YAML ]

```
Parameters:
    SourceCodeBucket:
        Type: String
        Description: S3 bucket containing Lambda source code zip files
    PythonLambdaS3Key:
        Type: String
        Description: S3 key containing Python Lambda source code zip file
    PythonLambdaObjectVersionId:
        Type: String
        Description: S3 version id for S3 key containing Python Lambda source code zip file
    JavaLambdaS3Key:
        Type: String
        Description: S3 key containing Python Lambda source code zip file
    JavaLambdaObjectVersionId:
        Type: String
        Description: S3 version id for S3 key containing Python Lambda source code zip file
    KeyAliasSuffix:
        Type: String
        Description: 'Suffix to use for KMS CMK Alias (ie: alias/<KeyAliasSuffix>)'
    StreamName:
        Type: String
        Description: Name to use for Kinesis Stream
Resources:
    InputStream:
        Type: AWS::Kinesis::Stream
        Properties:
            Name: !Ref StreamName
            ShardCount: 2
    PythonLambdaOutputTable:
        Type: AWS::DynamoDB::Table
        Properties:
            AttributeDefinitions:
                -
                    AttributeName: id
                    AttributeType: S
            KeySchema:
                -
                    AttributeName: id
                    KeyType: HASH
            ProvisionedThroughput:
                ReadCapacityUnits: 1
                WriteCapacityUnits: 1
    PythonLambdaRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                    -
                        Effect: Allow
                        Principal:
                            Service: lambda.amazonaws.com
                        Action: sts:AssumeRole
            ManagedPolicyArns:
                - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
            Policies:
                -
                    PolicyName: PythonLambdaAccess
                    PolicyDocument:
                        Version: 2012-10-17
                        Statement:
                            -
                                Effect: Allow
                                Action:
                                    - dynamodb:DescribeTable
                                    - dynamodb:BatchWriteItem
                                Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${PythonLambdaOutputTable}
                            -
                                Effect: Allow
                                Action:
                                    - dynamodb:PutItem
                                Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${PythonLambdaOutputTable}*
                            -
                                Effect: Allow
                                Action:
                                    - kinesis:GetRecords
                                    - kinesis:GetShardIterator
                                    - kinesis:DescribeStream
                                    - kinesis:ListStreams
                                Resource: !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}
    PythonLambdaFunction:
        Type: AWS::Lambda::Function
        Properties:
            Description: Python consumer
            Runtime: python2.7
            MemorySize: 512
            Timeout: 90
            Role: !GetAtt PythonLambdaRole.Arn
            Handler: aws_crypto_examples.kinesis_datakey_caching.consumer.lambda_handler
            Code:
                S3Bucket: !Ref SourceCodeBucket
                S3Key: !Ref PythonLambdaS3Key
                S3ObjectVersion: !Ref PythonLambdaObjectVersionId
            Environment:
                Variables:
                    TABLE_NAME: !Ref PythonLambdaOutputTable
    PythonLambdaSourceMapping:
        Type: AWS::Lambda::EventSourceMapping
        Properties:
            BatchSize: 1
            Enabled: true
            EventSourceArn: !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}
            FunctionName: !Ref PythonLambdaFunction
            StartingPosition: TRIM_HORIZON
    JavaLambdaOutputTable:
        Type: AWS::DynamoDB::Table
        Properties:
            AttributeDefinitions:
                -
                    AttributeName: id
                    AttributeType: S
            KeySchema:
                -
                    AttributeName: id
                    KeyType: HASH
            ProvisionedThroughput:
                ReadCapacityUnits: 1
                WriteCapacityUnits: 1
    JavaLambdaRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                    -
                        Effect: Allow
                        Principal:
                            Service: lambda.amazonaws.com
                        Action: sts:AssumeRole
            ManagedPolicyArns:
                - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
            Policies:
                -
                    PolicyName: JavaLambdaAccess
                    PolicyDocument:
                        Version: 2012-10-17
                        Statement:
                            -
                                Effect: Allow
                                Action:
                                    - dynamodb:DescribeTable
                                    - dynamodb:BatchWriteItem
                                Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${JavaLambdaOutputTable}
                            -
                                Effect: Allow
                                Action:
                                    - dynamodb:PutItem
                                Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${JavaLambdaOutputTable}*
                            -
                                Effect: Allow
                                Action:
                                    - kinesis:GetRecords
                                    - kinesis:GetShardIterator
                                    - kinesis:DescribeStream
                                    - kinesis:ListStreams
                                Resource: !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}
    JavaLambdaFunction:
        Type: AWS::Lambda::Function
        Properties:
            Description: Java consumer
            Runtime: java8
            MemorySize: 512
            Timeout: 90
            Role: !GetAtt JavaLambdaRole.Arn
            Handler: com.amazonaws.crypto.examples.kinesisdatakeycaching.LambdaDecryptAndWrite::handleRequest
            Code:
                S3Bucket: !Ref SourceCodeBucket
                S3Key: !Ref JavaLambdaS3Key
                S3ObjectVersion: !Ref JavaLambdaObjectVersionId
            Environment:
                Variables:
                    TABLE_NAME: !Ref JavaLambdaOutputTable
                    CMK_ARN: !GetAtt RegionKinesisCMK.Arn
    JavaLambdaSourceMapping:
        Type: AWS::Lambda::EventSourceMapping
        Properties:
            BatchSize: 1
            Enabled: true
            EventSourceArn: !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/${InputStream}
            FunctionName: !Ref JavaLambdaFunction
            StartingPosition: TRIM_HORIZON
    RegionKinesisCMK:
        Type: AWS::KMS::Key
        Properties:
            Description: Used to encrypt data passing through Kinesis Stream in this region
            Enabled: true
            KeyPolicy:
                Version: 2012-10-17
                Statement:
                    -
                        Effect: Allow
                        Principal:
                            AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
                        Action:
                            # Data plane actions
                            - kms:Encrypt
                            - kms:GenerateDataKey
                            # Control plane actions
                            - kms:CreateAlias
                            - kms:DeleteAlias
                            - kms:DescribeKey
                            - kms:DisableKey
                            - kms:EnableKey
                            - kms:PutKeyPolicy
                            - kms:ScheduleKeyDeletion
                            - kms:UpdateAlias
                            - kms:UpdateKeyDescription
                        Resource: '*'
                    -
                        Effect: Allow
                        Principal:
                            AWS:
                                - !GetAtt PythonLambdaRole.Arn
                                - !GetAtt JavaLambdaRole.Arn
                        Action: kms:Decrypt
                        Resource: '*'
    RegionKinesisCMKAlias:
        Type: AWS::KMS::Alias
        Properties:
            AliasName: !Sub alias/${KeyAliasSuffix}
            TargetKeyId: !Ref RegionKinesisCMK
```

------