

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Caching kunci data
<a name="data-key-caching"></a>

*Caching kunci data* menyimpan [kunci data](concepts.md#DEK) dan [materi kriptografi terkait](data-caching-details.md#cache-entries) dalam cache. Ketika Anda mengenkripsi atau mendekripsi data, AWS Encryption SDK mencari kunci data yang cocok dalam cache. Jika menemukan kecocokan, ia menggunakan kunci data yang di-cache daripada menghasilkan yang baru. Caching kunci data dapat meningkatkan kinerja, mengurangi biaya, dan membantu Anda tetap dalam batas layanan saat aplikasi Anda meningkat. 

Aplikasi Anda dapat memperoleh manfaat dari caching kunci data jika:
+ Itu dapat menggunakan kembali kunci data.
+ Ini menghasilkan banyak kunci data. 
+ Operasi kriptografi Anda sangat lambat, mahal, terbatas, atau intensif sumber daya.

Caching dapat mengurangi penggunaan layanan kriptografi Anda, seperti AWS Key Management Service ()AWS KMS. Jika Anda mencapai [AWS KMS requests-per-secondbatas](https://docs.aws.amazon.com/kms/latest/developerguide/limits.html#requests-per-second) Anda, caching dapat membantu. Aplikasi Anda dapat menggunakan kunci cache untuk melayani beberapa permintaan kunci data Anda alih-alih menelepon AWS KMS. (Anda juga dapat membuat kasus di [AWS Support Center](https://console.aws.amazon.com/support/home#/) untuk menaikkan batas akun Anda.)

 AWS Encryption SDK Ini membantu Anda membuat dan mengelola cache kunci data Anda. [Ini menyediakan [cache lokal](data-caching-details.md#simplecache) dan [manajer bahan kriptografi caching](data-caching-details.md#caching-cmm) (caching CMM) yang berinteraksi dengan cache dan memberlakukan ambang keamanan yang Anda tetapkan.](thresholds.md) Bekerja sama, komponen-komponen ini membantu Anda mendapatkan keuntungan dari efisiensi penggunaan kembali kunci data sambil menjaga keamanan sistem Anda.

Caching kunci data adalah fitur opsional AWS Encryption SDK yang harus Anda gunakan dengan hati-hati. Secara default, AWS Encryption SDK menghasilkan kunci data baru untuk setiap operasi enkripsi. Teknik ini mendukung praktik terbaik kriptografi, yang mencegah penggunaan kembali kunci data yang berlebihan. Secara umum, gunakan caching kunci data hanya jika diperlukan untuk memenuhi tujuan kinerja Anda. Kemudian, gunakan [ambang keamanan](thresholds.md) caching kunci data untuk memastikan bahwa Anda menggunakan jumlah minimum caching yang diperlukan untuk memenuhi tujuan biaya dan kinerja Anda. 

Versi 3. *x* dari AWS Encryption SDK for Java satu-satunya mendukung CMM caching dengan antarmuka penyedia kunci master lama, bukan antarmuka keyring. Namun, versi 4. *x* dan yang lebih baru AWS Encryption SDK untuk .NET, versi 3. *x* dari AWS Encryption SDK for Java, versi 4. *x* dari AWS Encryption SDK for Python, versi 1. *x* dari AWS Encryption SDK untuk Rust dan versi 0.1. *x* atau yang lebih baru dari AWS Encryption SDK for Go mendukung [keyring AWS KMS Hierarchical, solusi](use-hierarchical-keyring.md) caching bahan kriptografi alternatif. Konten yang dienkripsi dengan keyring AWS KMS Hierarkis hanya dapat didekripsi dengan keyring Hierarkis. AWS KMS 

Untuk diskusi rinci tentang pengorbanan keamanan ini, lihat [AWS Encryption SDK: Cara Memutuskan apakah Caching Kunci Data Tepat untuk Aplikasi Anda](https://aws.amazon.com/blogs/security/aws-encryption-sdk-how-to-decide-if-data-key-caching-is-right-for-your-application/) di Blog Keamanan. AWS 

**Topics**
+ [Cara menggunakan caching kunci data](implement-caching.md)
+ [Mengatur ambang keamanan cache](thresholds.md)
+ [Detail caching kunci data](data-caching-details.md)
+ [Contoh caching kunci data](sample-cache-example.md)

# Cara menggunakan caching kunci data
<a name="implement-caching"></a>

Topik ini menunjukkan cara menggunakan caching kunci data dalam aplikasi Anda. Ini membawa Anda melalui proses langkah demi langkah. Kemudian, ia menggabungkan langkah-langkah dalam contoh sederhana yang menggunakan caching kunci data dalam operasi untuk mengenkripsi string.

Contoh di bagian ini menunjukkan cara menggunakan [versi 2.0. ](about-versions.md)*x* dan yang lebih baru AWS Encryption SDK. Untuk contoh yang menggunakan versi sebelumnya, temukan rilis Anda di daftar [Rilis](https://github.com/aws/aws-encryption-sdk-c/releases) GitHub repositori untuk [bahasa pemrograman](programming-languages.md) Anda.

Untuk contoh lengkap dan teruji menggunakan caching kunci data di AWS Encryption SDK, lihat:
+ [C/C\$1\$1: caching\$1cmm.cpp](https://github.com/aws/aws-encryption-sdk-c/blob/master/examples/caching_cmm.cpp)
+ Jawa: [SimpleDataKeyCachingExample.java](https://github.com/aws/aws-encryption-sdk-java/blob/master/src/examples/java/com/amazonaws/crypto/examples/v2/SimpleDataKeyCachingExample.java)
+ JavaScript Peramban: [caching\$1cmm.ts](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-browser/src/caching_cmm.ts)
+ JavaScript Node.js: [caching\$1cmm.ts](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/example-node/src/caching_cmm.ts)
+ [Python: data\$1key\$1caching\$1basic.py](https://github.com/aws/aws-encryption-sdk-python/blob/master/examples/src/legacy/data_key_caching_basic.py)

[AWS Encryption SDK Untuk .NET](dot-net.md) tidak mendukung caching kunci data.

**Topics**
+ [Menggunakan caching kunci data: Step-by-step](#implement-caching-steps)
+ [Contoh caching kunci data: Enkripsi string](#caching-example-encrypt-string)

## Menggunakan caching kunci data: Step-by-step
<a name="implement-caching-steps"></a>

 step-by-stepPetunjuk ini menunjukkan cara membuat komponen yang Anda butuhkan untuk mengimplementasikan caching kunci data.
+ [Buat cache kunci data](data-caching-details.md#simplecache). Dalam contoh ini, kami menggunakan cache lokal yang AWS Encryption SDK disediakan. Kami membatasi cache hingga 10 kunci data.

   

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

  Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* dari CMM AWS Encryption SDK for Java caching kunci data tidak digunakan lagi. Dengan versi 3. *x*, Anda juga dapat menggunakan [keyring AWS KMS Hierarki, solusi caching](use-hierarchical-keyring.md) bahan kriptografi alternatif.

  ```
  // 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)
  ```

------

   
+ Buat [penyedia kunci master](concepts.md#master-key-provider) (Java dan Python) atau [keyring](concepts.md#keyring) (C dan). JavaScript Contoh-contoh ini menggunakan penyedia kunci master AWS Key Management Service (AWS KMS) atau [AWS KMS keyring](use-kms-keyring.md) yang kompatibel.

   

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

  Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* dari CMM AWS Encryption SDK for Java caching kunci data tidak digunakan lagi. Dengan versi 3. *x*, Anda juga dapat menggunakan [keyring AWS KMS Hierarki, solusi caching](use-hierarchical-keyring.md) bahan kriptografi alternatif.

  ```
  // 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 ]

  Di browser, Anda harus menyuntikkan kredensyal Anda dengan aman. Contoh ini mendefinisikan kredensyal dalam webpack (kms.webpack.config) yang menyelesaikan kredensyal saat runtime. Ini menciptakan instance penyedia AWS KMS klien dari AWS KMS klien dan kredensialnya. Kemudian, ketika membuat keyring, ia meneruskan penyedia klien ke konstruktor bersama dengan (. 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])
  ```

------

   
+ [Buat manajer materi kriptografi caching](data-caching-details.md#caching-cmm) (caching CMM). 

   

  Kaitkan CMM caching Anda dengan cache dan penyedia kunci utama atau keyring Anda. Kemudian, [atur ambang keamanan cache](thresholds.md) pada CMM caching. 

   

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

  Di dalam AWS Encryption SDK for C, Anda dapat membuat CMM caching dari CMM yang mendasarinya, seperti CMM default, atau dari keyring. Contoh ini membuat CMM caching dari keyring.

  Setelah Anda membuat CMM caching, Anda dapat melepaskan referensi Anda ke keyring dan cache. Lihat perinciannya di [Penghitungan referensi](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 ]

  Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* AWS Encryption SDK for Java tidak mendukung caching kunci data, tetapi mendukung [keyring AWS KMS Hierarkis](use-hierarchical-keyring.md), solusi caching bahan kriptografi alternatif.

  ```
  /*
   * 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
  )
  ```

------

Hanya itu yang perlu Anda lakukan. Kemudian, biarkan AWS Encryption SDK mengelola cache untuk Anda, atau tambahkan logika manajemen cache Anda sendiri.

Saat Anda ingin menggunakan caching kunci data dalam panggilan untuk mengenkripsi atau mendekripsi data, tentukan CMM caching Anda alih-alih penyedia kunci master atau CMM lainnya.

**catatan**  
Jika Anda mengenkripsi aliran data, atau data apa pun dengan ukuran yang tidak diketahui, pastikan untuk menentukan ukuran data dalam permintaan. AWS Encryption SDK Tidak menggunakan caching kunci data saat mengenkripsi data dengan ukuran yang tidak diketahui.

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

Di AWS Encryption SDK for C, Anda membuat sesi dengan CMM caching dan kemudian memproses sesi. 

Secara default, ketika ukuran pesan tidak diketahui dan tidak terbatas, kunci data AWS Encryption SDK tidak cache. Untuk mengizinkan caching saat Anda tidak mengetahui ukuran data yang tepat, gunakan `aws_cryptosdk_session_set_message_bound` metode ini untuk mengatur ukuran maksimum pesan. Atur batas lebih besar dari perkiraan ukuran pesan. Jika ukuran pesan sebenarnya melebihi batas, operasi enkripsi gagal.

```
/* 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 ]

Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* dari CMM AWS Encryption SDK for Java caching kunci data tidak digunakan lagi. Dengan versi 3. *x*, Anda juga dapat menggunakan [keyring AWS KMS Hierarki, solusi caching](use-hierarchical-keyring.md) bahan kriptografi alternatif.

```
// 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 ]

Saat Anda menggunakan CMM caching di AWS Encryption SDK for JavaScript for Node.js, `encrypt` metode ini membutuhkan panjang plaintext. Jika Anda tidak menyediakannya, kunci data tidak di-cache. Jika Anda memberikan panjang, tetapi data plaintext yang Anda berikan melebihi panjang itu, operasi enkripsi gagal. Jika Anda tidak tahu persis panjang plaintext, seperti saat Anda streaming data, berikan nilai yang diharapkan terbesar.

```
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
)
```

------

## Contoh caching kunci data: Enkripsi string
<a name="caching-example-encrypt-string"></a>

Contoh kode sederhana ini menggunakan caching kunci data saat mengenkripsi string. Ini menggabungkan kode dari [step-by-step prosedur](#implement-caching-steps) ke kode pengujian yang dapat Anda jalankan.

Contoh ini membuat [cache lokal](data-caching-details.md#simplecache) dan [penyedia kunci master](concepts.md#master-key-provider) atau [keyring](concepts.md#keyring) untuk file AWS KMS key. [Kemudian, ia menggunakan cache lokal dan penyedia kunci master atau keyring untuk membuat CMM caching dengan ambang keamanan yang sesuai.](thresholds.md) [Di Java dan Python, permintaan enkripsi menentukan CMM caching, data plaintext untuk mengenkripsi, dan konteks enkripsi.](data-caching-details.md#caching-encryption-context) Dalam C, CMM caching ditentukan dalam sesi, dan sesi disediakan untuk permintaan enkripsi.

Untuk menjalankan contoh ini, Anda perlu menyediakan [Amazon Resource Name (ARN) dari file. AWS KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html) Pastikan bahwa Anda memiliki [izin untuk menggunakan AWS KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default-allow-users) untuk menghasilkan kunci data.

Untuk contoh dunia nyata yang lebih rinci tentang membuat dan menggunakan cache kunci data, lihat[Kode contoh caching kunci data](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 ]

Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* dari CMM AWS Encryption SDK for Java caching kunci data tidak digunakan lagi. Dengan versi 3. *x*, Anda juga dapat menggunakan [keyring AWS KMS Hierarki, solusi caching](use-hierarchical-keyring.md) bahan kriptografi alternatif.

```
// 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
```

------

# Mengatur ambang keamanan cache
<a name="thresholds"></a>

Saat Anda menerapkan caching kunci data, Anda perlu mengonfigurasi ambang keamanan yang diberlakukan CMM [caching](data-caching-details.md#caching-cmm). 

Ambang batas keamanan membantu Anda membatasi berapa lama setiap kunci data yang di-cache digunakan dan berapa banyak data yang dilindungi di bawah setiap kunci data. CMM caching mengembalikan kunci data cache hanya ketika entri cache sesuai dengan semua ambang keamanan. Jika entri cache melebihi ambang batas apa pun, entri tidak digunakan untuk operasi saat ini dan dikeluarkan dari cache sesegera mungkin. Penggunaan pertama setiap kunci data (sebelum caching) dikecualikan dari ambang batas ini. 

Sebagai aturan, gunakan jumlah minimum caching yang diperlukan untuk memenuhi tujuan biaya dan kinerja Anda. 

 AWS Encryption SDK Satu-satunya cache kunci data yang dienkripsi dengan menggunakan fungsi derivasi [kunci](https://en.wikipedia.org/wiki/Key_derivation_function). Juga, ini menetapkan batas atas untuk beberapa nilai ambang batas. Pembatasan ini memastikan bahwa kunci data tidak digunakan kembali di luar batas kriptografinya. Namun, karena kunci data plaintext Anda di-cache (dalam memori, secara default), cobalah untuk meminimalkan waktu penyimpanan kunci. Juga, cobalah untuk membatasi data yang mungkin terekspos jika kunci dikompromikan.

Untuk contoh pengaturan ambang keamanan cache, lihat [AWS Encryption SDK: Cara Memutuskan apakah Caching Kunci Data Tepat untuk Aplikasi Anda di Blog](https://aws.amazon.com/blogs/security/aws-encryption-sdk-how-to-decide-if-data-key-caching-is-right-for-your-application/) Keamanan. AWS 

**catatan**  
CMM caching memberlakukan semua ambang batas berikut. Jika Anda tidak menentukan nilai opsional, CMM caching menggunakan nilai default.  
Untuk menonaktifkan caching kunci data sementara, implementasi Java dan Python menyediakan cache *materi kriptografi null (cache null*). AWS Encryption SDK Cache null mengembalikan miss untuk setiap `GET` permintaan dan tidak menanggapi `PUT` permintaan. Kami menyarankan Anda menggunakan cache null alih-alih mengatur [kapasitas cache](data-caching-details.md#simplecache) atau ambang keamanan ke 0. Untuk informasi selengkapnya, lihat cache null di [Java](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/caching/NullCryptoMaterialsCache.html) dan [Python](https://aws-encryption-sdk-python.readthedocs.io/en/latest/generated/aws_encryption_sdk.caches.null.html).

**Usia maksimal (wajib)**  
Menentukan berapa lama entri cache dapat digunakan, dimulai saat ditambahkan. Nilai ini diperlukan. Masukkan nilai yang lebih besar dari 0. AWS Encryption SDK Itu tidak membatasi nilai usia maksimum.  
Semua implementasi bahasa AWS Encryption SDK menentukan usia maksimum dalam hitungan detik, kecuali untuk AWS Encryption SDK for JavaScript, yang menggunakan milidetik.  
Gunakan interval terpendek yang masih memungkinkan aplikasi Anda mendapat manfaat dari cache. Anda dapat menggunakan ambang batas usia maksimum seperti kebijakan rotasi kunci. Gunakan untuk membatasi penggunaan kembali kunci data, meminimalkan paparan materi kriptografi, dan mengusir kunci data yang kebijakannya mungkin telah berubah saat di-cache.

**Pesan maksimum dienkripsi (opsional)**  
Menentukan jumlah maksimum pesan yang kunci data cache dapat mengenkripsi. Nilai ini bersifat opsional. Masukkan nilai antara 1 dan 2 ^ 32 pesan. Nilai default adalah 2 ^ 32 pesan.  
Setel jumlah pesan yang dilindungi oleh setiap kunci cache menjadi cukup besar untuk mendapatkan nilai dari penggunaan kembali, tetapi cukup kecil untuk membatasi jumlah pesan yang mungkin terpapar jika kunci dikompromikan.

**Byte maksimum dienkripsi (opsional)**  
Menentukan jumlah maksimum byte yang kunci data cache dapat mengenkripsi. Nilai ini bersifat opsional. Masukkan nilai antara 0 dan 2 ^ 63 - 1. Nilai default adalah 2 ^ 63 - 1. Nilai 0 memungkinkan Anda menggunakan caching kunci data hanya ketika Anda mengenkripsi string pesan kosong.  
Byte dalam permintaan saat ini disertakan saat mengevaluasi ambang batas ini. Jika byte yang diproses, ditambah byte saat ini, melebihi ambang batas, kunci data yang di-cache dikeluarkan dari cache, meskipun mungkin telah digunakan pada permintaan yang lebih kecil. 

# Detail caching kunci data
<a name="data-caching-details"></a>

Sebagian besar aplikasi dapat menggunakan implementasi default caching kunci data tanpa menulis kode kustom. Bagian ini menjelaskan implementasi default dan beberapa detail tentang opsi. 

**Topics**
+ [Cara kerja caching kunci data](#how-caching-works)
+ [Membuat cache bahan kriptografi](#simplecache)
+ [Membuat manajer materi kriptografi caching](#caching-cmm)
+ [Apa yang ada dalam entri cache kunci data?](#cache-entries)
+ [Konteks enkripsi: Cara memilih entri cache](#caching-encryption-context)
+ [Apakah aplikasi saya menggunakan kunci data cache?](#caching-effect)

## Cara kerja caching kunci data
<a name="how-caching-works"></a>

Saat Anda menggunakan caching kunci data dalam permintaan untuk mengenkripsi atau mendekripsi data, yang AWS Encryption SDK pertama akan mencari cache untuk kunci data yang cocok dengan permintaan. Jika menemukan kecocokan yang valid, ia menggunakan kunci data cache untuk mengenkripsi data. Jika tidak, itu menghasilkan kunci data baru, seperti halnya tanpa cache. 

Caching kunci data tidak digunakan untuk data dengan ukuran yang tidak diketahui, seperti data yang dialirkan. Hal ini memungkinkan CMM caching untuk menerapkan ambang batas byte [maksimum](thresholds.md) dengan benar. Untuk menghindari perilaku ini, tambahkan ukuran pesan ke permintaan enkripsi. 

Selain cache, caching kunci data menggunakan [pengelola bahan kriptografi caching](#caching-cmm) (caching CMM). [CMM caching adalah [manajer bahan kriptografi khusus (CMM)](concepts.md#crypt-materials-manager) yang berinteraksi dengan [cache](#simplecache) dan CMM yang mendasarinya.](concepts.md#crypt-materials-manager) (Saat Anda menentukan [penyedia kunci master](concepts.md#master-key-provider) atau keyring, CMM AWS Encryption SDK default akan dibuat untuk Anda.) CMM caching menyimpan kunci data yang dikembalikan oleh CMM yang mendasarinya. CMM caching juga memberlakukan ambang keamanan cache yang Anda tetapkan. 

Untuk mencegah kunci data yang salah dipilih dari cache, semua caching yang kompatibel CMMs mengharuskan properti berikut dari bahan kriptografi yang di-cache cocok dengan permintaan bahan.
+ [Suite algoritma](concepts.md#crypto-algorithm)
+ [Konteks enkripsi](#caching-encryption-context) (bahkan saat kosong)
+ Nama partisi (string yang mengidentifikasi CMM caching)
+ (Hanya dekripsi) Kunci data terenkripsi

**catatan**  
Kunci data AWS Encryption SDK cache hanya ketika [rangkaian algoritma menggunakan fungsi](concepts.md#crypto-algorithm) [derivasi kunci](https://en.wikipedia.org/wiki/Key_derivation_function).

Alur kerja berikut menunjukkan bagaimana permintaan untuk mengenkripsi data diproses dengan dan tanpa caching kunci data. Mereka menunjukkan bagaimana komponen caching yang Anda buat, termasuk cache dan CMM caching, digunakan dalam proses.

### Enkripsi data tanpa caching
<a name="workflow-wo-cache"></a>

Untuk mendapatkan materi enkripsi tanpa caching:

1. Aplikasi meminta AWS Encryption SDK untuk mengenkripsi data. 

   Permintaan menentukan penyedia kunci master atau keyring. AWS Encryption SDK Membuat CMM default yang berinteraksi dengan penyedia kunci master atau keyring Anda.

1. Mereka AWS Encryption SDK meminta CMM untuk bahan enkripsi (dapatkan bahan kriptografi).

1. CMM meminta [keyring](concepts.md#keyring) (C dan JavaScript) atau [penyedia kunci master](concepts.md#master-key-provider) (Java dan Python) untuk materi kriptografi. Ini mungkin melibatkan panggilan ke layanan kriptografi, seperti AWS Key Management Service (AWS KMS). CMM mengembalikan materi enkripsi ke file. AWS Encryption SDK

1.  AWS Encryption SDK Menggunakan kunci data plaintext untuk mengenkripsi data. Ini menyimpan data terenkripsi dan kunci data terenkripsi dalam [pesan terenkripsi](concepts.md#message), yang dikembalikan ke pengguna.

![\[Enkripsi data tanpa caching\]](http://docs.aws.amazon.com/id_id/encryption-sdk/latest/developer-guide/images/encrypt-workflow-no-cache.png)


### Enkripsi data dengan caching
<a name="workflow-with-cache"></a>

Untuk mendapatkan materi enkripsi dengan caching kunci data:

1. Aplikasi meminta AWS Encryption SDK untuk mengenkripsi data. 

   Permintaan tersebut menentukan [manajer bahan kriptografi caching (caching CMM) yang terkait dengan manajer bahan](#caching-cmm) kriptografi yang mendasarinya (CMM). Saat Anda menentukan penyedia kunci master atau keyring, CMM AWS Encryption SDK default akan dibuat untuk Anda.

1. SDK meminta CMM caching yang ditentukan untuk materi enkripsi.

1. CMM caching meminta materi enkripsi dari cache.

   1. Jika cache menemukan kecocokan, cache akan memperbarui usia dan menggunakan nilai entri cache yang cocok, dan mengembalikan materi enkripsi yang di-cache ke CMM caching. 

      Jika entri cache sesuai dengan [ambang keamanannya](thresholds.md), CMM caching mengembalikannya ke SDK. Jika tidak, ia memberitahu cache untuk mengusir entri dan melanjutkan seolah-olah tidak ada kecocokan.

   1. Jika cache tidak dapat menemukan kecocokan yang valid, CMM caching meminta CMM yang mendasarinya untuk menghasilkan kunci data baru. 

      CMM yang mendasarinya mendapatkan materi kriptografi dari keyring (C dan JavaScript) atau penyedia kunci master (Java dan Python). Ini mungkin melibatkan panggilan ke layanan, seperti AWS Key Management Service. CMM yang mendasari mengembalikan plaintext dan salinan terenkripsi dari kunci data ke CMM caching. 

      CMM caching menyimpan materi enkripsi baru dalam cache.

1. CMM caching mengembalikan materi enkripsi ke file. AWS Encryption SDK

1.  AWS Encryption SDK Menggunakan kunci data plaintext untuk mengenkripsi data. Ini menyimpan data terenkripsi dan kunci data terenkripsi dalam [pesan terenkripsi](concepts.md#message), yang dikembalikan ke pengguna.

![\[Enkripsi data dengan caching kunci data\]](http://docs.aws.amazon.com/id_id/encryption-sdk/latest/developer-guide/images/encrypt-workflow-with-cache.png)


## Membuat cache bahan kriptografi
<a name="simplecache"></a>

 AWS Encryption SDK Mendefinisikan persyaratan untuk cache bahan kriptografi yang digunakan dalam caching kunci data. Ini juga menyediakan cache lokal, yang merupakan cache yang dapat dikonfigurasi, dalam memori, yang [paling jarang digunakan (LRU](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29)). Untuk membuat instance cache lokal, gunakan `LocalCryptoMaterialsCache` konstruktor di Java dan Python, fungsi JavaScript di, getLocalCryptographic MaterialsCache atau `aws_cryptosdk_materials_cache_local_new` konstruktor di C.

Cache lokal mencakup logika untuk manajemen cache dasar, termasuk menambahkan, mengusir, dan mencocokkan entri cache, dan memelihara cache. Anda tidak perlu menulis logika manajemen cache khusus apa pun. Anda dapat menggunakan cache lokal apa adanya, menyesuaikannya, atau mengganti cache yang kompatibel. 

Saat Anda membuat cache lokal, Anda mengatur *kapasitasnya*, yaitu jumlah entri maksimum yang dapat disimpan cache. Pengaturan ini membantu Anda merancang cache yang efisien dengan penggunaan kembali kunci data terbatas.

The AWS Encryption SDK for Java and the AWS Encryption SDK for Python juga menyediakan *cache materi kriptografi null* ()NullCryptoMaterialsCache. NullCryptoMaterialsCache Pengembalian kehilangan untuk semua `GET` operasi dan tidak menanggapi `PUT` operasi. Anda dapat menggunakan NullCryptoMaterialsCache dalam pengujian atau untuk menonaktifkan sementara caching dalam aplikasi yang menyertakan kode caching. 

Dalam AWS Encryption SDK, setiap cache materi kriptografi dikaitkan dengan [manajer bahan kriptografi caching](#caching-cmm) (caching CMM). CMM caching mendapatkan kunci data dari cache, menempatkan kunci data dalam cache, dan memberlakukan [ambang keamanan](thresholds.md) yang Anda tetapkan. Saat Anda membuat CMM caching, Anda menentukan cache yang digunakannya dan CMM atau penyedia kunci master yang mendasarinya yang menghasilkan kunci data yang di-cache.

## Membuat manajer materi kriptografi caching
<a name="caching-cmm"></a>

Untuk mengaktifkan caching kunci data, Anda membuat [cache](#simplecache) dan *pengelola materi kriptografi caching* (caching CMM). [[Kemudian, dalam permintaan Anda untuk mengenkripsi atau mendekripsi data, Anda menentukan CMM caching, bukan [manajer bahan kriptografi standar (CMM)](concepts.md#crypt-materials-manager), atau penyedia kunci master atau keyring.](concepts.md#keyring)](concepts.md#master-key-provider)

Ada dua jenis CMMs. Keduanya mendapatkan kunci data (dan materi kriptografi terkait), tetapi dengan cara yang berbeda, sebagai berikut:
+ CMM dikaitkan dengan keyring (C atau JavaScript) atau penyedia kunci master (Java dan Python). Ketika SDK meminta CMM untuk bahan enkripsi atau dekripsi, CMM mendapatkan materi dari keyring atau penyedia kunci masternya. Di Java dan Python, CMM menggunakan kunci master untuk menghasilkan, mengenkripsi, atau mendekripsi kunci data. Dalam C dan JavaScript, keyring menghasilkan, mengenkripsi, dan mengembalikan materi kriptografi.
+ CMM caching dikaitkan dengan satu cache, seperti [cache lokal](#simplecache), dan CMM yang mendasarinya. Ketika SDK meminta CMM caching untuk materi kriptografi, CMM caching mencoba untuk mendapatkannya dari cache. Jika tidak dapat menemukan kecocokan, CMM caching menanyakan CMM yang mendasarinya untuk materi. Kemudian, ia menyimpan materi kriptografi baru sebelum mengembalikannya ke penelepon. 

CMM caching juga memberlakukan [ambang keamanan](thresholds.md) yang Anda tetapkan untuk setiap entri cache. Karena ambang keamanan diatur dan diberlakukan oleh CMM caching, Anda dapat menggunakan cache yang kompatibel, bahkan jika cache tidak dirancang untuk materi sensitif.

## Apa yang ada dalam entri cache kunci data?
<a name="cache-entries"></a>

Caching kunci data menyimpan kunci data dan materi kriptografi terkait dalam cache. Setiap entri mencakup elemen-elemen yang tercantum di bawah ini. Anda mungkin menemukan informasi ini berguna ketika Anda memutuskan apakah akan menggunakan fitur caching kunci data, dan ketika Anda menetapkan ambang keamanan pada pengelola bahan kriptografi caching (caching CMM).

**Entri Cache untuk Permintaan Enkripsi**  
Entri yang ditambahkan ke cache kunci data sebagai hasil dari operasi enkripsi mencakup elemen-elemen berikut:
+ Kunci data teks biasa
+ Kunci data terenkripsi (satu atau lebih)
+ [Konteks enkripsi](#caching-encryption-context) 
+ Kunci penandatanganan pesan (jika digunakan)
+ [Suite algoritma](concepts.md#crypto-algorithm)
+ Metadata, termasuk penghitung penggunaan untuk menegakkan ambang keamanan

**Entri Cache untuk Permintaan Dekripsi**  
Entri yang ditambahkan ke cache kunci data sebagai hasil dari operasi dekripsi mencakup elemen-elemen berikut:
+ Kunci data teks biasa
+ Kunci verifikasi tanda tangan (jika digunakan)
+ Metadata, termasuk penghitung penggunaan untuk menegakkan ambang keamanan

## Konteks enkripsi: Cara memilih entri cache
<a name="caching-encryption-context"></a>

Anda dapat menentukan konteks enkripsi dalam permintaan apa pun untuk mengenkripsi data. Namun, konteks enkripsi memainkan peran khusus dalam caching kunci data. Ini memungkinkan Anda membuat subkelompok kunci data di cache Anda, bahkan ketika kunci data berasal dari CMM caching yang sama.

[Konteks enkripsi](concepts.md#encryption-context) adalah seperangkat pasangan nilai kunci yang berisi data non-rahasia yang berubah-ubah. Selama enkripsi, konteks enkripsi terikat secara kriptografis ke data terenkripsi sehingga konteks enkripsi yang sama diperlukan untuk mendekripsi data. Dalam AWS Encryption SDK, konteks enkripsi disimpan dalam [pesan terenkripsi](concepts.md#message) dengan data terenkripsi dan kunci data. 

Bila Anda menggunakan cache kunci data, Anda juga dapat menggunakan konteks enkripsi untuk memilih kunci data cache tertentu untuk operasi enkripsi Anda. Konteks enkripsi disimpan dalam entri cache dengan kunci data (ini adalah bagian dari ID entri cache). Kunci data yang di-cache hanya digunakan kembali jika konteks enkripsi mereka cocok. Jika Anda ingin menggunakan kembali kunci data tertentu untuk permintaan enkripsi, tentukan konteks enkripsi yang sama. Jika Anda ingin menghindari kunci data tersebut, tentukan konteks enkripsi yang berbeda. 

Konteks enkripsi selalu opsional, tetapi disarankan. Jika Anda tidak menentukan konteks enkripsi dalam permintaan Anda, konteks enkripsi kosong disertakan dalam pengenal entri cache dan dicocokkan dengan setiap permintaan.

## Apakah aplikasi saya menggunakan kunci data cache?
<a name="caching-effect"></a>

Data key caching adalah strategi optimasi yang sangat efektif untuk aplikasi dan beban kerja tertentu. Namun, karena mengandung beberapa risiko, penting untuk menentukan seberapa efektif kemungkinan untuk situasi Anda, dan kemudian memutuskan apakah manfaatnya lebih besar daripada risikonya.

Karena caching kunci data menggunakan kembali kunci data, efek yang paling jelas adalah mengurangi jumlah panggilan untuk menghasilkan kunci data baru. Ketika caching kunci data diimplementasikan, AWS Encryption SDK panggilan AWS KMS `GenerateDataKey` operasi hanya untuk membuat kunci data awal dan ketika cache meleset. Namun, caching meningkatkan kinerja secara nyata hanya dalam aplikasi yang menghasilkan banyak kunci data dengan karakteristik yang sama, termasuk konteks enkripsi dan rangkaian algoritma yang sama.

Untuk menentukan apakah implementasi Anda AWS Encryption SDK benar-benar menggunakan kunci data dari cache, coba teknik berikut.
+ Di log infrastruktur kunci master Anda, periksa frekuensi panggilan untuk membuat kunci data baru. Ketika caching kunci data efektif, jumlah panggilan untuk membuat kunci baru akan turun dengan jelas. Misalnya, jika Anda menggunakan penyedia kunci AWS KMS master atau keyring, cari CloudTrail log untuk [GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html)panggilan. 
+ Bandingkan [pesan terenkripsi](concepts.md#message) yang AWS Encryption SDK dikembalikan sebagai respons terhadap permintaan enkripsi yang berbeda. Misalnya, jika Anda menggunakan AWS Encryption SDK for Java, bandingkan [ParsedCiphertext](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/ParsedCiphertext.html)objek dari panggilan enkripsi yang berbeda. Dalam AWS Encryption SDK for JavaScript, bandingkan isi `encryptedDataKeys` properti [MessageHeader](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/serialize/src/types.ts#L21). Ketika kunci data digunakan kembali, kunci data terenkripsi dalam pesan terenkripsi identik.

# Contoh caching kunci data
<a name="sample-cache-example"></a>

Contoh ini menggunakan [caching kunci data](data-key-caching.md) dengan [cache lokal](data-caching-details.md#simplecache) untuk mempercepat aplikasi di mana data yang dihasilkan oleh beberapa perangkat dienkripsi dan disimpan di Wilayah yang berbeda.

Dalam skenario ini, beberapa produsen data menghasilkan data, mengenkripsi, dan menulis ke aliran [Kinesis](https://aws.amazon.com/kinesis/streams/) di setiap Wilayah. [AWS Lambda](https://aws.amazon.com/lambda/)fungsi (konsumen) mendekripsi aliran dan menulis data teks biasa ke tabel DynamoDB di Wilayah. Produsen data dan konsumen menggunakan AWS Encryption SDK dan [penyedia kunci AWS KMS utama](concepts.md#master-key-provider). Untuk mengurangi panggilan ke KMS, setiap produsen dan konsumen memiliki cache lokal mereka sendiri.

Anda dapat menemukan kode sumber untuk contoh-contoh ini di [Java dan Python](sample-cache-example-code.md). Sampel juga menyertakan CloudFormation template yang mendefinisikan sumber daya untuk sampel.

![\[Diagram ini menunjukkan bagaimana produsen data dan konsumen menggunakan AWS KMS, Amazon Kinesis Data Streams, dan Amazon DynamoDB.\]](http://docs.aws.amazon.com/id_id/encryption-sdk/latest/developer-guide/images/simplecache-example.png)


## Hasil cache lokal
<a name="caching-example-impact"></a>

Tabel berikut menunjukkan bahwa cache lokal mengurangi total panggilan ke KMS (per detik per Wilayah) dalam contoh ini menjadi 1% dari nilai aslinya.


**Permintaan produsen**  
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/id_id/encryption-sdk/latest/developer-guide/sample-cache-example.html)


**Permintaan konsumen**  
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/id_id/encryption-sdk/latest/developer-guide/sample-cache-example.html)

# Kode contoh caching kunci data
<a name="sample-cache-example-code"></a>

Contoh kode ini menciptakan implementasi sederhana dari caching kunci data dengan [cache lokal](data-caching-details.md#simplecache) di Java dan Python. Kode ini menciptakan dua contoh cache lokal: satu untuk [produsen data yang mengenkripsi data](#caching-producer) dan satu lagi untuk [konsumen data](#caching-consumer) (AWS Lambda fungsi) yang mendekripsi data. Untuk detail tentang implementasi caching kunci data dalam setiap bahasa, lihat dokumentasi [Javadoc dan](https://aws.github.io/aws-encryption-sdk-java/) [Python](https://aws-encryption-sdk-python.readthedocs.io/en/latest/) untuk. AWS Encryption SDK

Data key caching tersedia untuk semua [bahasa pemrograman](programming-languages.md) yang AWS Encryption SDK mendukung. 

Untuk contoh lengkap dan teruji menggunakan caching kunci data di AWS Encryption SDK, lihat:
+ [C/C\$1\$1: caching\$1cmm.cpp](https://github.com/aws/aws-encryption-sdk-c/blob/master/examples/caching_cmm.cpp) 
+ Jawa: [SimpleDataKeyCachingExample.java](https://github.com/aws/aws-encryption-sdk-java/blob/master/src/examples/java/com/amazonaws/crypto/examples/v2/SimpleDataKeyCachingExample.java)
+ JavaScript Peramban: [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)

## Produser
<a name="caching-producer"></a>

[Produser mendapatkan peta, mengubahnya menjadi JSON, menggunakan AWS Encryption SDK untuk mengenkripsi, dan mendorong catatan ciphertext ke aliran Kinesis di masing-masing.](https://aws.amazon.com/kinesis/streams/) Wilayah AWS

[Kode mendefinisikan [manajer bahan kriptografi caching](data-caching-details.md#caching-cmm) (caching CMM) dan mengaitkannya dengan [cache lokal dan penyedia kunci master yang mendasarinya](data-caching-details.md#simplecache).AWS KMS](concepts.md#master-key-provider) CMM caching menyimpan kunci data (dan [materi kriptografi terkait](data-caching-details.md#cache-entries)) dari penyedia kunci utama. Ini juga berinteraksi dengan cache atas nama SDK dan memberlakukan ambang keamanan yang Anda tetapkan. 

Karena panggilan ke metode enkripsi menentukan CMM caching, bukan [manajer bahan kriptografi biasa (CMM) atau penyedia kunci utama, enkripsi akan menggunakan caching](concepts.md#crypt-materials-manager) kunci data.

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

Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* dari CMM AWS Encryption SDK for Java caching kunci data tidak digunakan lagi. Dengan versi 3. *x*, Anda juga dapat menggunakan [keyring AWS KMS Hierarkis, solusi](use-hierarchical-keyring.md) caching bahan kriptografi alternatif.

```
/*
 * 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
            )
```

------

## Konsumen
<a name="caching-consumer"></a>

Konsumen data adalah [AWS Lambda](https://aws.amazon.com/lambda/)fungsi yang dipicu oleh peristiwa [Kinesis](https://aws.amazon.com/kinesis/). [Ini mendekripsi dan deserialisasi setiap catatan, dan menulis catatan teks biasa ke tabel Amazon DynamoDB di Wilayah yang sama.](https://aws.amazon.com/dynamodb/)

Seperti kode produsen, kode konsumen memungkinkan caching kunci data dengan menggunakan manajer bahan kriptografi caching (caching CMM) dalam panggilan ke metode dekripsi. 

Kode Java membangun penyedia kunci master dalam *mode ketat* dengan yang ditentukan AWS KMS key. Mode ketat tidak diperlukan saat mendekripsi, tetapi ini adalah praktik [terbaik](best-practices.md#strict-discovery-mode). Kode Python menggunakan *mode penemuan*, yang memungkinkan AWS Encryption SDK penggunaan kunci pembungkus apa pun yang mengenkripsi kunci data untuk mendekripsi itu.

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

Contoh berikut menggunakan versi 2. *x* dari AWS Encryption SDK for Java. Versi 3. *x* dari CMM AWS Encryption SDK for Java caching kunci data tidak digunakan lagi. Dengan versi 3. *x*, Anda juga dapat menggunakan [keyring AWS KMS Hierarkis, solusi](use-hierarchical-keyring.md) caching bahan kriptografi alternatif.

Kode ini membuat penyedia kunci master untuk mendekripsi dalam mode ketat. Hanya AWS Encryption SDK dapat menggunakan yang AWS KMS keys Anda tentukan untuk mendekripsi pesan Anda.

```
/*
 * 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 ]

Kode Python ini mendekripsi dengan penyedia kunci master dalam mode penemuan. Ini memungkinkan AWS Encryption SDK penggunaan kunci pembungkus apa pun yang mengenkripsi kunci data untuk mendekripsi itu. [Mode ketat, di mana Anda menentukan kunci pembungkus yang dapat digunakan untuk dekripsi, adalah praktik terbaik.](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)
```

------

# Contoh caching kunci data: template CloudFormation
<a name="sample-cache-example-cloudformation"></a>

 CloudFormation Template ini mengatur semua AWS sumber daya yang diperlukan untuk mereproduksi [contoh caching kunci data](sample-cache-example.md).

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

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

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

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

------