

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# データキーキャッシュ
<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 の 1 秒あたりのリクエスト数](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 は、データキーキャッシュの作成と管理に役立ちます。キャッシュとやり取りがあり、設定した[セキュリティのしきい値](thresholds.md)を適用する[ローカルキャッシュ](data-caching-details.md#simplecache)および[キャッシュ暗号化マテリアルマネージャー](data-caching-details.md#caching-cmm) (キャッシュ CMM) を提供します。連携によって、これらのコンポーネントはシステムのセキュリティを維持しながら、データキーの再利用による効率の恩恵を受けるのに役立ちます。

データキーキャッシュは、 のオプション機能 AWS Encryption SDK であり、慎重に使用する必要があります。デフォルトでは、 は暗号化オペレーションごとに新しいデータキー AWS Encryption SDK を生成します。この手法では、暗号化のベストプラクティスをサポートしており、過剰なデータキーの再利用を防ぎます。一般的に、パフォーマンスの目標を満たすために必要な場合のみ、データキーキャッシュを使用します。次に、データキーキャッシュの[セキュリティしきい値](thresholds.md)を使用して、コストとパフォーマンスの目標を満たすために必要なキャッシュの最小量を使用していることを確認します。

のバージョン 3.*x* は、キーリングインターフェイスではなく、レガシーマスターキープロバイダーインターフェイスを使用したキャッシュ CMM AWS Encryption SDK for Java のみをサポートします。ただし、.NET AWS Encryption SDK 用 のバージョン 4.*x* 以降、 のバージョン 3.*x* AWS Encryption SDK for Java、 のバージョン 4.*x* AWS Encryption SDK for Python、Rust AWS Encryption SDK 用 のバージョン 1.*x*、Go AWS Encryption SDK 用 のバージョン 0.1.*x* 以降では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](use-hierarchical-keyring.md)がサポートされています。 AWS KMS 階層キーリングで暗号化されたコンテンツは、 AWS KMS 階層キーリングでのみ復号できます。

これらセキュリティトレードオフの詳細については、 AWS セキュリティブログの「[AWS Encryption SDK: How to Decide if Data Key Caching is Right for Your Application](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>

このトピックでは、アプリケーションのデータキーキャッシュを使用する方法について説明します。ここでは、プロセスを手順ごとに説明します。次に、文字列を暗号化するオペレーションでデータキーキャッシュを使用する簡単な例の中ですべての手順を結びつけます。

このセクションの例では、 AWS Encryption SDKの[バージョン 2.0.*x*](about-versions.md) 以降の使用方法について説明します。前バージョンを使用する例については、使用している[プログラミング言語](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 Browser: [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)

[.NET 用AWS Encryption SDK](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.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java では、データキーキャッシュ CMM は廃止されました。バージョン 3.*x* では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](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) または[キーリング](concepts.md#keyring) (C および JavaScript) を作成します。これらの例では、 AWS Key Management Service (AWS KMS) マスターキープロバイダーまたは互換性のある[AWS KMS キーリング](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.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java では、データキーキャッシュ CMM は廃止されました。バージョン 3.*x* では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](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 クライアントプロバイダーインスタンスを作成します。次に、キーリングを作成すると、 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 をキャッシュとマスターキープロバイダーまたはキーリングに関連付けます。次に、キャッシュ CMM の[キャッシュセキュリティのしきい値を設定します。](thresholds.md)

   

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

  では AWS Encryption SDK for C、デフォルトの CMM などの基盤となる CMM またはキーリングからキャッシュ CMM を作成できます。この例では、キーリングからキャッシュ CMM を作成します。

  キャッシュ CMM を作成したら、キーリングおよびキャッシュへのリファレンスを解放できます。詳細については、「[参照カウント](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.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java はデータキーキャッシュをサポートしていませんが、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](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 ]

で AWS Encryption SDK for C、キャッシュ 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.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java では、データキーキャッシュ CMM は廃止されました。バージョン 3.*x* では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](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 ]

 AWS Encryption SDK for JavaScript 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)または[キーリング](concepts.md#keyring)を AWS KMS keyのために作成します。次に、ローカルキャッシュとマスターキープロバイダーまたはキーリングを使用して、適切な[セキュリティしきい値](thresholds.md)でキャッシュ CMM を作成します。Java および Python では、暗号化リクエストは、キャッシュ CMM、暗号化する平文のデータ、および[暗号化コンテキスト](data-caching-details.md#caching-encryption-context)を指定します。Cでは、キャッシュ CMMがセッションで指定され、セッションが暗号化要求に提供されます。

これらの例を実行するには、[AWS KMS keyの Amazon リソースネーム (ARN)](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.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java では、データキーキャッシュ CMM は廃止されました。バージョン 3.*x* では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](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](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 はデフォルト値を使用します。  
データキーキャッシュを一時的に無効にするために、 AWS Encryption SDK の Java/Python 実装では、*null の暗号化マテリアルキャッシュ* (null キャッシュ) が用意されています。null キャッシュは、すべての `GET` リクエストのミスを返し、`PUT` リクエストに応答しません。[キャッシュ容量](data-caching-details.md#simplecache)またはセキュリティのしきい値を 0 に設定するのではなく、null キャッシュを使用することをお勧めします。詳細については、「[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 for 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 は、[キャッシュ](#simplecache)および基盤となる [CMM](concepts.md#crypt-materials-manager) とのやり取りに特化した[暗号化マテリアルマネージャー (CMM)](concepts.md#crypt-materials-manager) です。([マスターキープロバイダー](concepts.md#master-key-provider)またはキーリングを指定すると、 AWS Encryption SDK はデフォルトの CMM を自動で作成します。) キャッシュ CMM は、基盤となる CMM が返すデータキーをキャッシュします。また、キャッシュ CMM は、ユーザーが設定したキャッシュセキュリティしきい値を適用します。

キャッシュで誤ったデータキーが選択されないように、互換性のあるすべてのキャッシュ CMM では、キャッシュされた暗号化マテリアルの次のプロパティがマテリアルリクエストと一致している必要があります。
+ [アルゴリズムスイート](concepts.md#crypto-algorithm)
+ [暗号化コンテキスト](#caching-encryption-context) (空の場合も含む)
+ パーティション名 (キャッシュ CMM を識別する文字列)
+ (説明のみ) 暗号化されたデータキー

**注記**  
は、[アルゴリズムスイート](concepts.md#crypto-algorithm)がキー[取得関数を使用する場合にのみデータキー](https://en.wikipedia.org/wiki/Key_derivation_function)を AWS Encryption SDK キャッシュします。

次のワークフローでは、データを暗号化するリクエストがデータキーキャッシュがある場合とない場合にどのように処理されるかを示します。キャッシュおよびキャッシュ CMM を含む、ユーザーが作成したキャッシュコンポーネントがプロセスの中でどのように使用されるかが示されます。

### キャッシュを使用しないでデータを暗号化する
<a name="workflow-wo-cache"></a>

キャッシュせずに暗号化マテリアルを取得するには:

1. アプリケーションは、データの暗号化 AWS Encryption SDK を に要求します。

   リクエストは、マスターキープロバイダーまたはキーリングを指定します。 AWS Encryption SDK は、マスターキープロバイダーまたはキーリングとやり取りするデフォルト CMM を作成します。

1. は、暗号化マテリアル (暗号化マテリアルの取得) を CMM に AWS Encryption SDK 要求します。

1. CMM によって、暗号化マテリアルの[キーリング](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/ja_jp/encryption-sdk/latest/developer-guide/images/encrypt-workflow-no-cache.png)


### キャッシュを使用してデータを暗号化する
<a name="workflow-with-cache"></a>

データキーキャッシュを使用して暗号化マテリアルを取得するには:

1. アプリケーションは、データの暗号化 AWS Encryption SDK を に要求します。

   このリクエストは、基盤となる暗号化マテリアルマネージャー (CMM) と関連付けられている[キャッシュ暗号化マテリアルマネージャー (キャッシュCMM)](#caching-cmm) を指定します。マスターキープロバイダーまたはキーリングを指定すると、 AWS Encryption SDK はデフォルトの CMM を自動で作成します。

1. SDK によって、指定したキャッシュ CMM に対して暗号化マテリアルが求められます。

1. キャッシュ CMM は、キャッシュの暗号化マテリアルをリクエストします。

   1. キャッシュで一致が見つかった場合、期間が更新され、一致したキャッシュエントリの値を使用して、キャッシュされた暗号化マテリアルをキャッシュ CMM に返します。

      キャッシュエントリが[セキュリティしきい値](thresholds.md)に準拠している場合、キャッシュ CMM はそれを SDK に返します。それ以外の場合は、一致がなかったものとしてキャッシュがエントリを削除し続行するように指示します。

   1. キャッシュで有効な一致が見つからない場合、キャッシュ CMM は基盤となる CMM に新しいデータキーを生成するよう要求します。

      基盤となる CMM は、そのキーリング (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/ja_jp/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 および Python の `LocalCryptoMaterialsCache` コンストラクタ、JavaScript の getLocalCryptographicMaterialsCache 関数、または C の `aws_cryptosdk_materials_cache_local_new` コンストラクタを使用します。

ローカルキャッシュには、キャッシュされたエントリの追加、削除、一致とキャッシュの保持を含む基本的なキャッシュ管理のロジックが含まれます。カスタムキャッシュ管理ロジックを作成する必要はありません。ローカルキャッシュはそのまま使用したり、カスタマイズしたり、任意の互換性のあるキャッシュを置き換えたりすることができます。

ローカルキャッシュの作成時に、その*容量* (キャッシュが保持できるエントリの最大数) を設定します。この設定は、データキーの再利用が制限されている際に効率的なキャッシュを設計するのに役立ちます。

 AWS Encryption SDK for Java と は、*null 暗号化マテリアルキャッシュ* (NullCryptoMaterialsCache) AWS Encryption SDK for Python も提供します。NullCryptoMaterialsCache は、すべての `GET` オペレーションのミスを返し、`PUT` オペレーションには応答しません。NullCryptoMaterialsCache はテストで使用したり、キャッシュコードを含むアプリケーションで一時的にキャッシュを無効にしたりするために使用できます。

では AWS Encryption SDK、各暗号化マテリアルキャッシュは[、キャッシュ暗号化マテリアルマネージャー](#caching-cmm) (キャッシュ CMM) に関連付けられます。キャッシュ CMM は、キャッシュからデータキーを取得し、キャッシュにデータキーを格納して、設定した[セキュリティしきい値](thresholds.md)を適用します。キャッシュ CMM 作成時に、使用するキャッシュ、基盤となる CMM またはキャッシュするデータキーを生成するマスターキープロバイダーを指定します。

## キャッシュ暗号化マテリアルマネージャーの作成
<a name="caching-cmm"></a>

データキーキャッシュを有効にするには、[キャッシュ](#simplecache)と*キャッシュ暗号化マテリアルマネージャー* (キャッシュ CMM) を作成します。次に、データを暗号化または復号するリクエストで、標準的な[暗号化マテリアルマネージャー (CMM)](concepts.md#crypt-materials-manager) の代わりにキャッシュ CMM、または[マスターキープロバイダー](concepts.md#master-key-provider)か[キーリング](concepts.md#keyring)を指定します。

CMM には 2 つのタイプがあります。いずれもデータキー (および関連する暗号化マテリアル) を取得しますが、以下のようにさまざまな方法があります。
+ CMM は、キーリング (C または JavaScript) またはマスターキープロバイダー (Java および Python) に関連付けられています。SDK より CMM に暗号化マテリアルまたは復号マテリアルが求められると、CMM はそのキーリングまたはマスターキープロバイダーからそのマテリアルを取得します。Java および Python では、CMM はマスターキーを使用して、データキーを生成、暗号化、または復号します。C および JavaScript では、キーリングは暗号化マテリアルを生成し、暗号化して返します。
+ キャッシュ CMM は、[ローカルキャッシュ](#simplecache)などの 1 つのキャッシュ、および基盤となる CMM に関連付けられています。SDK がキャッシュ CMM に暗号化マテリアルを要求すると、キャッシュ CMM はキャッシュからそれらを取得しようとします。一致が見つからない場合、キャッシュ CMM は、基盤となる CMM にマテリアルを要求します。次に、発信者に返す前に、新しい暗号化マテリアルをキャッシュします。

また、キャッシュ CMM は、ユーザーが各キャッシュエントリに設定した[セキュリティしきい値](thresholds.md)を適用します。セキュリティしきい値はキャッシュ CMM で設定され適用されるため、キャッシュが機密性情報向けに設計されていなくても、互換性があるすべてのキャッシュが使用できます。

## データキーキャッシュエントリとは
<a name="cache-entries"></a>

データキーキャッシュにより、キャッシュにデータキーおよび関連する暗号化マテリアルが保存されます。各エントリには、以下に示す要素が含まれます。この情報は、データキーキャッシュ機能を使用するかどうかを決定するときや、キャッシュ暗号化マテリアルマネージャー (キャッシュ CMM) でセキュリティしきい値を設定するときに役立ちます。

**暗号化リクエストのキャッシュされたエントリ**  
暗号化オペレーションの結果としてデータキーキャッシュに追加されたエントリには、次の要素が含まれます。
+ プレーンテキストのデータキー
+ 暗号化されたデータキー (1 つ以上)
+ [暗号化コンテキスト](#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 の一部) を使用してキャッシュエントリに保存されます。キャッシュされたデータキーは、その暗号化がコンテキストと一致する場合にのみ再利用されます。暗号化リクエストに特定のデータキーを再利用する場合、同じ暗号化コンテキストを指定します。これらのデータキーを回避するには、別の暗号化コンテキストを指定します。

暗号化コンテキストは常にオプションですが、推奨されています。リクエストで暗号化コンテキストを指定しない場合、空の暗号化コンテキストがキャッシュエントリ ID に含められ、各リクエストに照合されます。

## アプリケーションはキャッシュされたデータキーを使用していますか？
<a name="caching-effect"></a>

データキーキャッシュは、特定のアプリケーションやワークロードに対して非常に効果的な最適化戦略です。ただし、リスクが伴うため、状況にどれほど効果があるかを判断し、その利点がリスクを上回るかどうかを判断することが重要です。

データキーキャッシュはデータキーを再利用するため、最も明白な効果は、新しいデータキーを生成するための呼び出し回数を減らすことです。データキーキャッシュが実装されている場合、 は オペレーションを AWS Encryption SDK AWS KMS `GenerateDataKey`呼び出して、最初のデータキーとキャッシュが欠落したときのみを作成します。しかし、同じ暗号化コンテキストとアルゴリズムスイートを含む、同じ特性を持つ多数のデータキーを生成するアプリケーションでのみキャッシュのパフォーマンスが知覚的に向上します。

の実装 AWS Encryption SDK でキャッシュのデータキーが実際に使用されているかどうかを判断するには、次の方法を試してください。
+ マスターキーインフラストラクチャのログで、新しいデータキーを作成する呼び出しの頻度を確認します。データキーキャッシュが有効な場合、新しいキーを作成するための呼び出しの数は目に見えて低下します。たとえば、 AWS KMS マスターキープロバイダーまたはキーリングを使用している場合は、[GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html) 呼び出しの CloudTrail ログを検索します。
+ さまざまな暗号化リクエストに応じて AWS Encryption SDK が返す[暗号化されたメッセージ](concepts.md#message)を比較します。たとえば、 を使用している場合は AWS Encryption SDK for Java、異なる暗号化呼び出しの [ParsedCiphertext](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/ParsedCiphertext.html) オブジェクトを比較します。 AWS Encryption SDK for JavaScriptでは、[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-caching-details.md#simplecache)で[データキーキャッシュ](data-key-caching.md)を使用して、複数のデバイスによって生成されたデータが暗号化されて異なるリージョンに保存されるアプリケーションを高速化します。

この例では、複数のデータのプロデューサーがデータを作成して暗号化し、各リージョンの [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/ja_jp/encryption-sdk/latest/developer-guide/images/simplecache-example.png)


## ローカルキャッシュの結果
<a name="caching-example-impact"></a>

以下の表は、ローカルキャッシュによって、この例の KMS への合計呼び出し回数 (1 秒あたり、リージョンあたり) が元の値の 1% まで減少していることを示しています。


**プロデューサーリクエスト**  
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/encryption-sdk/latest/developer-guide/sample-cache-example.html)


**コンシューマーリクエスト**  
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/encryption-sdk/latest/developer-guide/sample-cache-example.html)

# データキーキャッシュのコード例
<a name="sample-cache-example-code"></a>

このコード例は、Java と Python で[ローカルキャッシュ](data-caching-details.md#simplecache)を使用するデータキーキャッシュの単純な実装を作成します。このコードは、ローカルキャッシュの 2 つのインスタンスを作成します。1 つはデータを暗号化する[データプロデューサー](#caching-producer)用で、もう 1 つはデータを復号する[データコンシューマー](#caching-consumer) (AWS Lambda 関数) 用です。言語別のデータキーキャッシュの実装の詳細については、 AWS Encryption SDKの [Javadoc](https://aws.github.io/aws-encryption-sdk-java/) および [Python ドキュメント](https://aws-encryption-sdk-python.readthedocs.io/en/latest/)を参照してください。

データキーキャッシュは、 が 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 Browser: [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)) をキャッシュします。また、SDK に代わってキャッシュとのやり取りを行い、設定したセキュリティしきい値を適用します。

暗号化メソッドの呼び出しでは、通常の[暗号化マテリアルマネージャー (CMM)](concepts.md#crypt-materials-manager) やマスターキープロバイダーではなく、キャッシュ CMM が指定されるため、暗号化ではデータキーキャッシュが使用されます。

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

次の例では、 のバージョン 2.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java では、データキーキャッシュ CMM は廃止されました。バージョン 3.*x* では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](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 コードは、指定された を使用して *strict モードで*マスターキープロバイダーを構築します AWS KMS key。Strict モードは復号時に必須ではありませんが、[ベストプラクティス](best-practices.md#strict-discovery-mode)です。Python コードは*検出モード*を使用します。これにより、 はデータキーを暗号化したラッピングキー AWS Encryption SDK を使用して復号化できます。

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

次の例では、 のバージョン 2.*x* を使用しています AWS Encryption SDK for Java。のバージョン 3.*x* AWS Encryption SDK for Java では、データキーキャッシュ CMM は廃止されました。バージョン 3.*x* では、代替の暗号化マテリアルキャッシュソリューションである[AWS KMS 階層キーリング](use-hierarchical-keyring.md)を使用することもできます。

このコードは、Strict モードで復号するためのマスターキープロバイダーを作成します。 AWS Encryption SDK は、 AWS KMS keys 指定した のみを使用してメッセージを復号できます。

```
/*
 * 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 コードでは、Discovery モードでマスターキープロバイダーを使用して復号します。 AWS Encryption SDK では、データキーを暗号化したラッピングキーを使用して復号できます。復号に使用できるラッピングキーを指定する Strict モードが[ベストプラクティス](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 テンプレート
<a name="sample-cache-example-cloudformation"></a>

この CloudFormation テンプレートは、[データキーキャッシュの例](sample-cache-example.md)を再現するために必要なすべての AWS リソースを設定します。

------
#### [ 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
```

------