

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Almacenamiento en caché de claves de datos
<a name="data-key-caching"></a>

Con el *almacenamiento en caché de claves de datos*, se guardan [claves de datos](concepts.md#DEK) y sus [materiales criptográficos relacionados](data-caching-details.md#cache-entries) en una caché. Al cifrar o descifrar datos, AWS Encryption SDK busca una clave de datos coincidente en la memoria caché. Si encuentra una coincidencia, utiliza la clave de datos almacenada en caché en lugar de generar una nueva. El almacenamiento en caché de claves de datos puede mejorar el rendimiento, reducir los costos y ayudarle a mantenerse dentro de los límites de servicio, aunque cambie la escala de la aplicación. 

La aplicación puede beneficiarse del almacenamiento en caché de claves de datos si:
+ puede reutilizar claves de datos;
+ genera numerosas claves de datos; 
+ las operaciones criptográficas son inaceptablemente lentas, costosas o limitadas o realizan un uso intensivo de los recursos.

El almacenamiento en caché puede reducir el uso de servicios criptográficos, como (). AWS Key Management Service AWS KMS Si estás alcanzando tu [AWS KMS requests-per-secondlímite, el](https://docs.aws.amazon.com/kms/latest/developerguide/limits.html#requests-per-second) almacenamiento en caché puede ayudarte. Tu aplicación puede usar claves almacenadas en caché para atender algunas de tus solicitudes de claves de datos en lugar de realizar llamadas. AWS KMS(También puede crear un caso en el [AWS Centro de asistencia](https://console.aws.amazon.com/support/home#/) para aumentar el límite de su cuenta).

 AWS Encryption SDK Esto le ayuda a crear y administrar su caché de claves de datos. Proporciona una [caché local](data-caching-details.md#simplecache) y un [administrador de materiales criptográficos de almacenamiento](data-caching-details.md#caching-cmm) (CMM de almacenamiento en caché) que interactúa con la caché y aplica los [umbrales de seguridad](thresholds.md) establecidos. Juntos, estos componentes le ayudan a beneficiarse de la eficacia de reutilizar las claves de datos y, al mismo tiempo, mantiene la seguridad del sistema.

El almacenamiento en caché de las claves de datos es una función opcional AWS Encryption SDK que debe utilizar con precaución. De forma predeterminada, AWS Encryption SDK genera una nueva clave de datos para cada operación de cifrado. Esta técnica es compatible con las prácticas recomendadas criptográficas, que desaconsejan una reutilización excesiva de las claves de datos. En general, conviene utilizar el almacenamiento en caché de claves de datos exclusivamente cuando sea imprescindible para satisfacer sus objetivos de rendimiento. A continuación, utilice [umbrales de seguridad](thresholds.md) del almacenamiento en caché de claves de datos para asegurarse de usar la cantidad mínima de almacenamiento en caché requerida para satisfacer sus objetivos de costo y rendimiento. 

Versión 3. *x* SDK de cifrado de AWS para Java solo admite la CMM de almacenamiento en caché con la interfaz antigua de proveedores de claves maestras, no con la interfaz de anillo de claves. Sin embargo, la versión 4. *x* y versiones posteriores de la versión 3 AWS Encryption SDK para .NET. *x* de SDK de cifrado de AWS para Java, versión 4. *x* de la SDK de cifrado de AWS para Python, versión 1. *x* AWS Encryption SDK para Rust y la versión 0.1. *x* o versiones posteriores de AWS Encryption SDK for Go admiten el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos. El contenido cifrado con el anillo de claves AWS KMS jerárquico solo se puede descifrar con el anillo de claves jerárquico. AWS KMS 

Para consultar una descripción detallada de lo que esto conlleva para la seguridad, consulte [AWS Encryption SDK: cómo decidir si el almacenamiento en caché de las claves de datos es adecuado para su aplicación](https://aws.amazon.com/blogs/security/aws-encryption-sdk-how-to-decide-if-data-key-caching-is-right-for-your-application/) en el blog sobre seguridad de AWS .

**Topics**
+ [Cómo utilizar el almacenamiento en caché de claves de datos](implement-caching.md)
+ [Configuración de los umbrales de seguridad de la caché](thresholds.md)
+ [Información detallada sobre el almacenamiento en caché de claves de datos](data-caching-details.md)
+ [Ejemplo de almacenamiento en caché de claves de datos](sample-cache-example.md)

# Cómo utilizar el almacenamiento en caché de claves de datos
<a name="implement-caching"></a>

En este tema se muestra cómo usar el almacenamiento en caché de claves de datos en las aplicaciones. Recorremos el proceso paso a paso. A continuación, combinaremos todo el procedimiento en un ejemplo sencillo que utiliza el almacenamiento en caché de claves de datos en una operación para cifrar una cadena.

Los ejemplos en esta sección muestran cómo usar la [versión 2.0.*x*](about-versions.md) y posterior del AWS Encryption SDK. Para ver ejemplos que utilizan versiones anteriores, busca tu versión en la lista de [versiones](https://github.com/aws/aws-encryption-sdk-c/releases) del GitHub repositorio de tu [lenguaje de programación](programming-languages.md).

Para ver ejemplos completos y comprobados del uso del almacenamiento en caché de claves de datos en el AWS Encryption SDK, consulta:
+ C/C\$1\$1: [caching\$1cmm.cpp](https://github.com/aws/aws-encryption-sdk-c/blob/master/examples/caching_cmm.cpp)
+ [Java: SimpleDataKeyCachingExample .java](https://github.com/aws/aws-encryption-sdk-java/blob/master/src/examples/java/com/amazonaws/crypto/examples/v2/SimpleDataKeyCachingExample.java)
+ JavaScript [Navegador: 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)

La [AWS Encryption SDK para .NET](dot-net.md) no admite el almacenamiento en caché de claves de datos.

**Topics**
+ [Uso del almacenamiento en caché de claves de datos: Step-by-step](#implement-caching-steps)
+ [Ejemplo de almacenamiento en caché de claves de datos: cifrado de una cadena](#caching-example-encrypt-string)

## Uso del almacenamiento en caché de claves de datos: Step-by-step
<a name="implement-caching-steps"></a>

Estas step-by-step instrucciones le muestran cómo crear los componentes que necesita para implementar el almacenamiento en caché de claves de datos.
+ [Crear una caché de clave de datos](data-caching-details.md#simplecache). En estos ejemplos, utilizamos la caché local que AWS Encryption SDK proporciona. Limitamos la caché a 10 claves de datos.

   

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

  En el siguiente ejemplo se utiliza la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* del SDK de cifrado de AWS para Java CMM de almacenamiento en caché de claves de datos está en desuso. Con la versión 3. *x*, también puede utilizar el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

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

------

   
+ Cree un [proveedor de claves maestras](concepts.md#master-key-provider) (Java y Python) o un [anillo de claves](concepts.md#keyring) (C y JavaScript). En estos ejemplos se utiliza un proveedor de claves maestras AWS Key Management Service (AWS KMS) o un conjunto de [AWS KMS claves](use-kms-keyring.md) compatible.

   

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

  En el siguiente ejemplo se utiliza la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* del SDK de cifrado de AWS para Java CMM de almacenamiento en caché de claves de datos está en desuso. Con la versión 3. *x*, también puede utilizar el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

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

  En el navegador, debe insertar las credenciales de forma segura. En este ejemplo se definen las credenciales de un paquete web (kms.webpack.config) que resuelve las credenciales en tiempo de ejecución. Crea una instancia de proveedor de AWS KMS clientes a partir de un AWS KMS cliente y las credenciales. Luego, cuando crea el anillo de claves, pasa el proveedor del cliente al constructor junto con el 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])
  ```

------

   
+ [Crear un administrador de materiales criptográficos de almacenamiento](data-caching-details.md#caching-cmm) (CMM de almacenamiento en caché). 

   

  Asocie el CMM de almacenamiento en caché con la caché y con el proveedor de claves maestras o conjunto de claves. A continuación, [establezca los umbrales de seguridad de la caché](thresholds.md) en el CMM de almacenamiento en caché. 

   

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

  En el SDK de cifrado de AWS para C, puede crear una CMM de almacenamiento en caché a partir de una CMM subyacente, como la CMM predeterminada, o desde un anillo de claves. En este ejemplo se crea el CMM de almacenamiento en caché a partir de un conjunto de claves.

  Después de crear el CMM de almacenamiento en caché, puede liberar sus referencias al conjunto de claves y a la caché. Para obtener más información, consulte [Recuento de referencias](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 ]

  En el siguiente ejemplo, se utiliza la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* of the SDK de cifrado de AWS para Java no admite el almacenamiento en caché de claves de datos, pero sí es compatible con el [anillo de claves AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

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

------

Es todo lo que tiene que hacer. A continuación, deje que AWS Encryption SDK administren la memoria caché por usted o añada su propia lógica de administración de la memoria caché.

Cuando desee utilizar el almacenamiento de claves de datos en una llamada para cifrar o descifrar datos, especifique el CMM de almacenamiento en caché en lugar del proveedor de claves maestras u otro CMM.

**nota**  
Si va a cifrar flujos de datos o datos de tamaño desconocido, asegúrese de especificar el tamaño de los datos en la solicitud. AWS Encryption SDK No utiliza el almacenamiento en caché de claves de datos al cifrar datos de tamaño desconocido.

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

En el SDK de cifrado de AWS para C, se crea una sesión con la CMM de almacenamiento en caché y, a continuación, se procesa la sesión. 

De forma predeterminada, cuando el tamaño del mensaje es desconocido e ilimitado, no almacena en caché las claves de AWS Encryption SDK datos. Para permitir el almacenamiento en caché cuando no se conoce el tamaño exacto de los datos, utilice el método `aws_cryptosdk_session_set_message_bound` para establecer un tamaño máximo para el mensaje. Defina el límite en un tamaño mayor que el tamaño estimado del mensaje. Si el tamaño real del mensaje supera el límite, la operación de cifrado genera un error.

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

En el siguiente ejemplo, se utiliza la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* del SDK de cifrado de AWS para Java CMM de almacenamiento en caché de claves de datos está en desuso. Con la versión 3. *x*, también puede utilizar el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

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

Si utiliza el CMM de almacenamiento en caché SDK de cifrado de AWS para JavaScript para Node.js, el `encrypt` método requiere la longitud del texto sin formato. Si no la proporciona, la clave de datos no se almacena en caché. Si proporciona una longitud, pero los datos en texto no cifrado que proporciona superan esa longitud, se producirá un error en la operación de cifrado. Si no sabe la longitud exacta del texto no cifrado, como cuando transmite datos, proporcione el valor esperado más grande.

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

------

## Ejemplo de almacenamiento en caché de claves de datos: cifrado de una cadena
<a name="caching-example-encrypt-string"></a>

Este ejemplo de código sencillo utiliza el almacenamiento en caché de claves de datos al cifrar una cadena. Combina el código del [step-by-step procedimiento](#implement-caching-steps) en un código de prueba que se puede ejecutar.

El ejemplo crea una [caché local](data-caching-details.md#simplecache) y un [proveedor de claves maestras](concepts.md#master-key-provider) o un [conjunto de claves](concepts.md#keyring) para un AWS KMS key. A continuación, se utiliza la caché local y un proveedor de claves maestras o conjunto de claves para crear un CMM de almacenamiento en caché con los [umbrales de seguridad](thresholds.md) adecuados. En Java y Python, la solicitud de cifrado especifica el CMM de almacenamiento en caché, los datos en texto no cifrado que hay que cifrar y un [contexto de cifrado](data-caching-details.md#caching-encryption-context). En C, el CMM de almacenamiento en caché se especifica en la sesión y la sesión se proporciona a la solicitud de cifrado.

Para ejecutar estos ejemplos, debe proporcionar el [Nombre de recurso de Amazon (ARN) de un AWS KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys.html). Compruebe de tenga [permiso para utilizar la AWS KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html#key-policy-default-allow-users) para generar una clave de datos.

Para obtener ejemplos reales más detallados de cómo crear y usar una caché de claves de datos, consulte [Ejemplo de almacenamiento en caché de claves de datos](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 ]

En el siguiente ejemplo se utiliza la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* del SDK de cifrado de AWS para Java CMM de almacenamiento en caché de claves de datos está en desuso. Con la versión 3. *x*, también puede utilizar el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

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

------

# Configuración de los umbrales de seguridad de la caché
<a name="thresholds"></a>

Al implementar el almacenamiento en caché de claves de datos, es preciso configurar los umbrales de seguridad que el [CMM de almacenamiento en caché](data-caching-details.md#caching-cmm) debe aplicar. 

Los umbrales de seguridad ayudan a limitar el tiempo durante el cual se utiliza cada clave de datos almacenada en caché, así como la cantidad de datos que se protegen con cada una de ellas. El CMM de almacenamiento en caché devuelve claves de datos almacenadas en caché solo cuando la entrada se ajusta a todos los umbrales de seguridad. Si la entrada de la caché supera cualquier umbral, no se utilizará para la operación actual y se expulsará de la caché lo antes posible. El primer uso de cada clave de datos (antes de almacenarla en caché) no se cuenta al calcular estos umbrales. 

Por regla general, utilice la cantidad mínima de almacenamiento en caché que se requiera para satisfacer sus objetivos de costo y rendimiento. 

El AWS Encryption SDK único que almacena en caché las claves de datos que se cifran mediante una función de [derivación de claves](https://en.wikipedia.org/wiki/Key_derivation_function). Además, establece límites máximos para algunos de los valores de los umbrales. Estas restricciones garantizan que las claves de datos no se reutilicen más allá de sus límites criptográficos. Sin embargo, dado que las claves de datos en texto no cifrado se almacenan en caché (en memoria, de forma predeterminada), es importante intentar minimizar el tiempo durante el que se conservan. Además, es importante tratar de limitar los datos que podrían verse expuestas en caso de filtrarse una clave.

Para ver ejemplos de cómo configurar los umbrales de seguridad de la memoria caché, consulte [AWS Encryption SDK: Cómo decidir si el almacenamiento en caché de claves de datos es adecuado para su aplicación](https://aws.amazon.com/blogs/security/aws-encryption-sdk-how-to-decide-if-data-key-caching-is-right-for-your-application/) en el blog de seguridad. AWS 

**nota**  
El CMM de almacenamiento en caché aplica todos los umbrales siguientes. Si no se especifica un valor opcional, el CMM de almacenamiento en caché utiliza el predeterminado.  
Para deshabilitar el almacenamiento en caché de claves de datos temporalmente, las implementaciones de Java y Python del AWS Encryption SDK proporcionan una *caché de materiales criptográficos nula* (caché nula). La caché nula devuelve un error por cada solicitud `GET` y no responde a las solicitudes `PUT`. Le recomendamos que utilice la caché nula en lugar de establecer la [capacidad de caché](data-caching-details.md#simplecache) o los umbrales de seguridad en 0. Para obtener más información, consulte la caché nula en [Java](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/caching/NullCryptoMaterialsCache.html) y [Python](https://aws-encryption-sdk-python.readthedocs.io/en/latest/generated/aws_encryption_sdk.caches.null.html).

**Antigüedad máxima (obligatoria)**  
Determina cuánto tiempo se puede utilizar una entrada almacenada en la caché, a partir del momento en que se agregó. Este valor es obligatorio. Escriba un valor mayor que 0. Esto AWS Encryption SDK no limita el valor máximo de edad.  
Todas las implementaciones lingüísticas del AWS Encryption SDK definen la edad máxima en segundos, excepto el SDK de cifrado de AWS para JavaScript, que utiliza milisegundos.  
Utilice el intervalo más breve que permita que la aplicación continúe beneficiándose de la caché. Puede utilizar el umbral de antigüedad máxima como política de rotación de claves. Utilícelo para limitar la reutilización de claves de datos, minimizar la exposición de materiales criptográficas y expulsar las claves de datos cuyas políticas podrían haber cambiado mientras estaban almacenadas en la caché.

**Número máximo de mensajes cifrados (opcional)**  
Especifica el número máximo de mensajes que una clave de datos almacenada en caché puede cifrar. Este valor es opcional. Escriba un valor comprendido entre 1 y 2^32 mensajes. El valor predeterminado es 2^32 mensajes.  
Establezca el número de mensajes protegidos por cada clave almacenada en caché de modo que sea lo bastante grande para obtener un valor reutilizado pero lo bastante pequeño para limitar el número de mensajes que podrían verse expuestos en caso de filtrarse una clave.

**Número máximo de bytes cifrados (opcional)**  
Especifica el número máximo de bytes que una clave de datos almacenada en caché puede cifrar. Este valor es opcional. Escriba un valor comprendido entre 0 y 2^63 - 1. El valor predeterminado es 2^63 - 1. El valor 0 permite utilizar almacenamiento en caché de clave de datos solo al cifrar cadenas de mensaje vacías.  
Los bytes de la solicitud actual se incluyen al evaluar este umbral. Si la suma de bytes procesados más bytes actuales supera este umbral, la clave de datos almacenada en caché se expulsa de la caché, aunque se haya utilizado en una solicitud menor. 

# Información detallada sobre el almacenamiento en caché de claves de datos
<a name="data-caching-details"></a>

La mayoría de las aplicaciones pueden utilizar la implementación predeterminada de almacenamiento en caché de claves de datos sin necesidad de escribir código personalizado. En esta sección se describen la implementación predeterminada y algunos detalles sobre las opciones. 

**Topics**
+ [Funcionamiento del almacenamiento en caché de claves de datos](#how-caching-works)
+ [Creación de una caché de materiales criptográficos](#simplecache)
+ [Creación de un administrador de materiales criptográficos de almacenamiento en caché](#caching-cmm)
+ [¿Qué es una entrada en la caché de claves de datos?](#cache-entries)
+ [Contexto de cifrado: cómo seleccionar las entradas de la caché](#caching-encryption-context)
+ [¿Usa mi aplicación claves de datos almacenadas en caché?](#caching-effect)

## Funcionamiento del almacenamiento en caché de claves de datos
<a name="how-caching-works"></a>

Cuando se utiliza el almacenamiento en caché de claves de datos en una solicitud de cifrado o descifrado de datos, el AWS Encryption SDK busca primero en la caché si hay alguna clave de datos que coincida con la solicitud. Si encuentra una coincidencia válida, utiliza la clave de datos almacenada en caché para cifrar los datos. De lo contrario, genera una nueva clave de datos, tal y como lo haría sin la caché. 

El almacenamiento en caché de claves de datos no se utiliza para datos de tamaño desconocido, como, por ejemplo, los datos transmitidos en streaming. Esto permite que el CMM de almacenamiento en caché aplique correctamente el [umbral de bytes máximos](thresholds.md). Para evitar este comportamiento, agregue el tamaño del mensaje a la solicitud de cifrado. 

Además de una caché, el almacenamiento en caché de claves de datos utiliza un [administrador de materiales criptográficos de almacenamiento](#caching-cmm) (almacenamiento en caché CMM). El CMM de almacenamiento en caché es un [administrador de materiales criptográficos (CMM)](concepts.md#crypt-materials-manager) especializado que interactúa con una [caché](#simplecache) y con un [CMM](concepts.md#crypt-materials-manager) subyacente. (Cuando especifica un [proveedor de claves maestras](concepts.md#master-key-provider) o un conjunto de claves, el AWS Encryption SDK le crea un CMM predeterminado). El CMM almacenamiento almacena en caché las claves de datos que el CMM subyacente devuelve. El CMM de almacenamiento en caché también aplica los umbrales de seguridad de caché establecidos. 

Para evitar que se seleccione una clave de datos incorrecta de la caché, todo almacenamiento en caché compatible CMMs requiere que las siguientes propiedades de los materiales criptográficos almacenados en caché coincidan con la solicitud de materiales.
+ [Conjunto de algoritmos](concepts.md#crypto-algorithm)
+ [Contexto de cifrado](#caching-encryption-context) (incluso cuando está vacío)
+ Nombre de partición (una cadena que identifica el CMM de almacenamiento en caché)
+ (Descifrado solo) Claves de datos cifradas

**nota**  
[Almacena en AWS Encryption SDK caché las claves de datos solo cuando el [conjunto de algoritmos](concepts.md#crypto-algorithm) utiliza una función de derivación de claves.](https://en.wikipedia.org/wiki/Key_derivation_function)

Los siguientes flujos de trabajo muestran cómo se procesa una solicitud de cifrado de datos con y sin almacenamiento en caché de las claves de datos. Muestran cómo se utilizan en el proceso los componentes de almacenamiento en caché que crea, lo que incluye la caché y el CMM de almacenamiento en caché.

### Cifrado de datos sin almacenamiento en caché
<a name="workflow-wo-cache"></a>

Para obtener materiales de cifrado sin almacenamiento en caché:

1. Una aplicación les pide que cifren AWS Encryption SDK los datos. 

   La solicitud especifica un proveedor de claves maestras o un conjunto de claves. El AWS Encryption SDK crea un CMM predeterminado que interactúa con el proveedor de claves maestras o el conjunto de claves.

1.  AWS Encryption SDK Solicita a la CMM materiales de cifrado (obtenga materiales criptográficos).

1. La CMM solicita materiales criptográficos a su conjunto de [claves](concepts.md#keyring) (C and JavaScript) o a su [proveedor de claves maestras](concepts.md#master-key-provider) (Java y Python). Esto puede implicar una llamada a un servicio criptográfico, como (). AWS Key Management Service AWS KMS El CMM devuelve los materiales de cifrado al AWS Encryption SDK.

1.  AWS Encryption SDK Utiliza la clave de datos de texto simple para cifrar los datos. Almacena los datos cifrados y las claves de datos cifrados en un [mensaje cifrado](concepts.md#message), que devuelve al usuario.

![\[Cifrado de datos sin almacenamiento en caché\]](http://docs.aws.amazon.com/es_es/encryption-sdk/latest/developer-guide/images/encrypt-workflow-no-cache.png)


### Cifrado de datos con almacenamiento en caché
<a name="workflow-with-cache"></a>

Para obtener materiales de cifrado con almacenamiento en caché de clave de datos:

1. Una aplicación les pide que cifren AWS Encryption SDK los datos. 

   La solicitud especifica un [administrador de materiales criptográficos de almacenamiento (CMM de almacenamiento en caché)](#caching-cmm) que está asociado a un administrador de materiales criptográficos (CMM) subyacente. Cuando especifica un proveedor de claves maestras o un conjunto de claves, el AWS Encryption SDK le crea un CMM predeterminado.

1. El SDK solicita los materiales de cifrado al CMM de almacenamiento en caché especificado.

1. El CMM de almacenamiento en caché solicita materiales de cifrado de la caché.

   1. Si la caché encuentra una coincidencia, actualiza los valores de antigüedad y uso de la entrada de caché coincidente y devuelve los materiales de cifrado almacenados en caché al CMM de almacenamiento en caché. 

      Si la entrada de caché se ajusta a sus [umbrales de seguridad](thresholds.md), el CMM de almacenamiento en caché se la devuelve al SDK. De lo contrario, indica a la caché que expulse la entrada de la caché y continúa como si no hubiese encontrado ninguna coincidencia.

   1. Si la caché no encuentra ninguna coincidencia válida, el CMM de almacenamiento en caché pide al CMM subyacente que genere una nueva clave de datos. 

      La CMM subyacente obtiene los materiales criptográficos de su anillo de claves (C and JavaScript) o proveedor de claves maestras (Java y Python). Esto podría requerir una llamada a un servicio como AWS Key Management Service. El CMM subyacente devuelve las copias de texto no cifrado y cifrado de la clave de datos al CMM de almacenamiento en caché. 

      El CMM de almacenamiento en caché guarda los nuevos materiales de cifrado en la caché.

1. El CMM de almacenamiento en caché devuelve los materiales de cifrado al AWS Encryption SDK.

1.  AWS Encryption SDK Utiliza la clave de datos de texto plano para cifrar los datos. Almacena los datos cifrados y las claves de datos cifrados en un [mensaje cifrado](concepts.md#message), que devuelve al usuario.

![\[Cifrado de datos con almacenamiento en caché de claves de datos\]](http://docs.aws.amazon.com/es_es/encryption-sdk/latest/developer-guide/images/encrypt-workflow-with-cache.png)


## Creación de una caché de materiales criptográficos
<a name="simplecache"></a>

 AWS Encryption SDK Define los requisitos de una caché de materiales criptográficos utilizada en el almacenamiento en caché de claves de datos. También proporciona una caché local, que es una caché configurable, en memoria y [usada menos recientemente (LRU)](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29). Para crear una instancia de la caché local, usa el `LocalCryptoMaterialsCache` constructor en Java y Python, la getLocalCryptographic MaterialsCache función en JavaScript o el `aws_cryptosdk_materials_cache_local_new` constructor en C.

La caché local incluye la lógica para la administración básica de la caché, lo que incluye la adición, expulsión y coincidencia de entradas almacenadas en la caché, así como para realizar el mantenimiento de la caché. No es necesario escribir ninguna lógica personalizada de administración de la caché. Puede utilizar la caché local tal cual, personalizarla o sustituirla por cualquier otra caché compatible. 

Al crear una caché local, establece su *capacidad*, es decir, el número máximo de entradas que puede contener esa caché. Este ajuste ayuda a diseñar una caché eficiente con una reutilización limitada de las claves de datos.

Los SDK de cifrado de AWS para Java y los SDK de cifrado de AWS para Python también proporcionan una *caché de materiales criptográficos nula* (NullCryptoMaterialsCache). NullCryptoMaterialsCache Devuelve un error para todas `GET` las operaciones y no responde a `PUT` las operaciones. Puede usarlo para probar o deshabilitar temporalmente el almacenamiento NullCryptoMaterialsCache en caché en una aplicación que incluya el código de almacenamiento en caché. 

En el AWS Encryption SDK, cada caché de materiales criptográficos está asociado a un [administrador de materiales criptográficos de almacenamiento en caché (CMM](#caching-cmm)). El CMM de almacenamiento en caché obtiene las claves de datos de la memoria caché, coloca las claves de datos en la caché y aplica los [umbrales de seguridad](thresholds.md) establecidos. Cuando se crea un CMM de almacenamiento en caché, especifica la caché que este utiliza, así como el CMM subyacente o proveedor de claves maestras que genera las claves de datos almacenadas en esa caché.

## Creación de un administrador de materiales criptográficos de almacenamiento en caché
<a name="caching-cmm"></a>

Para habilitar el almacenamiento en caché de claves de datos, cree una [caché](#simplecache) y un *administrador de materiales criptográficos de almacenamiento en caché* (CMM de almacenamiento en caché). A continuación, en las solicitudes de cifrado o descifrado de datos, especifique un CMM de almacenamiento en caché, en lugar de un [administrador de materiales criptográficos (CMM)](concepts.md#crypt-materials-manager) estándar o un [proveedor de claves maestras](concepts.md#master-key-provider) o [conjunto de claves](concepts.md#keyring).

Hay dos tipos de. CMMs Ambos obtienen las claves de datos (y el material criptográfico relacionado) pero de maneras distintas, como se indica a continuación:
+ Una CMM está asociada a un conjunto de claves (C o JavaScript) o a un proveedor de claves maestras (Java y Python). Cuando el SDK solicita al CMM materiales de cifrado o descifrado, el CMM obtiene los materiales de su conjunto de claves o proveedor de claves maestras. En Java y Python, el CMM utiliza estas claves maestras para generar, cifrar o descifrar las claves de datos. En C y JavaScript, el anillo de claves genera, cifra y devuelve el material criptográfico.
+ Un CMM de almacenamiento en caché está asociado a una caché, como una [caché local](#simplecache) y un CMM subyacente. Cuando el SDK solicita materiales criptográficos al CMM, el CMM de almacenamiento en caché intenta obtenerlos en la caché. Si no encuentra ninguna coincidencia, el CMM de almacenamiento en caché pide los materiales al CMM subyacente. A continuación, almacena los nuevos materiales criptográficos antes de devolvérselos al intermediario. 

El CMM de almacenamiento en caché también aplica los [umbrales de seguridad](thresholds.md) que establece para cada entrada de la caché. Dado que el CMM de almacenamiento en caché establece y aplica los umbrales de seguridad, puede utilizar cualquier caché compatible, aunque no se haya diseñado para materiales confidenciales.

## ¿Qué es una entrada en la caché de claves de datos?
<a name="cache-entries"></a>

Con el almacenamiento en caché de claves de datos, estas y sus materiales criptográficos relacionados se almacenan en una caché. Cada entrada incluye los elementos que se indican a continuación. Esta información puede resultar útil al decidir si se va a utilizar la característica de almacenamiento en caché de claves de datos y al establecer los umbrales de seguridad en un administrador de materiales criptográficos de almacenamiento (CMM de almacenamiento en caché).

**Entradas almacenadas en caché para solicitudes de cifrado**  
Las entradas que se agregan a una caché de claves de datos a consecuencia de una operación de cifrado incluyen los siguientes elementos:
+ Clave de datos en texto no cifrado
+ Claves de datos cifrados (una o más)
+ [Contexto de cifrado](#caching-encryption-context) 
+ Clave de firma de mensaje (si procede)
+ [Conjunto de algoritmos](concepts.md#crypto-algorithm)
+ Metadatos, incluidos los contadores de uso para aplicar los umbrales de seguridad

**Entradas almacenadas en caché para solicitudes de descifrado**  
Las entradas que se agregan a una caché de claves de datos a consecuencia de una operación de descifrado incluyen los siguientes elementos:
+ Clave de datos en texto no cifrado
+ Clave de verificación de firma (si procede)
+ Metadatos, incluidos los contadores de uso para aplicar los umbrales de seguridad

## Contexto de cifrado: cómo seleccionar las entradas de la caché
<a name="caching-encryption-context"></a>

Puede especificar un contexto de cifrado en cualquier solicitud de cifrado de datos. Sin embargo, el contexto de cifrado desempeña una función especial en el almacenamiento en caché de claves de datos. Permite crear subgrupos de claves de datos en la caché, aunque las claves de datos se originen a partir del mismo CMM de almacenamiento en caché.

Un [contexto de cifrado](concepts.md#encryption-context) es un conjunto de pares de clave-valor que contienen datos no secretos arbitrarios. Durante el cifrado, el contexto de cifrado se vincula criptográficamente a los datos cifrados, de tal forma que se requiere el mismo contexto de cifrado para descifrar los datos. En el AWS Encryption SDK, el contexto de cifrado se almacena en el [mensaje cifrado con los datos cifrados](concepts.md#message) y las claves de datos. 

Cuando se utiliza una caché de claves de datos, también se puede utilizar el contexto de cifrado para seleccionar claves de datos concretas almacenadas en la caché para las operaciones de cifrado. El contexto de cifrado se guarda en la entrada de la caché con la clave de datos (forma parte del ID de la entrada de la caché). Las claves de datos almacenadas en caché se reutilizan únicamente si contextos de cifrado coinciden. Si desea reutilizar determinadas claves de datos para una solicitud de cifrado, especifique el mismo contexto de cifrado. Si desea evitar estas claves de datos, especifique otro contexto de cifrado. 

El contexto de cifrado siempre es opcional, pero se recomienda. Si no especifica un contexto de cifrado en la solicitud, se incluirá un contexto de cifrado vacío en el identificador de la entrada de la caché y se hará coincidir con cada solicitud.

## ¿Usa mi aplicación claves de datos almacenadas en caché?
<a name="caching-effect"></a>

El almacenamiento en caché de claves de datos es una estrategia de optimización que es muy eficaz en determinadas aplicaciones y cargas de trabajo. Sin embargo, debido a que conlleva cierto riesgo, es importante que determine primero hasta qué punto es probable que sea eficaz para su situación y luego decida si los beneficios compensan los riesgos.

Dado que el almacenamiento en caché de claves de datos reutiliza claves de datos, el efecto más evidente es que se reduce el número de llamadas para generar nuevas claves de datos. Cuando se implementa el almacenamiento en caché de claves de datos, solo se AWS Encryption SDK llama a la AWS KMS `GenerateDataKey` operación para crear la clave de datos inicial y cuando no se utiliza la caché. Sin embargo, el almacenamiento en caché mejora el rendimiento perceptiblemente solo en el caso de aplicaciones que generen numerosas claves de datos con las mismas características, incluido el mismo contexto de cifrado y conjunto de algoritmos.

Para determinar si su implementación AWS Encryption SDK utiliza realmente claves de datos de la caché, pruebe las siguientes técnicas.
+ En los registros de la infraestructura de clave maestra, consulte la frecuencia de las llamadas para crear nuevas claves de datos. Cuando existe un almacenamiento en caché de claves de datos efectivo, el número de llamadas para crear nuevas claves debe caer perceptiblemente. Por ejemplo, si utiliza un proveedor de claves AWS KMS maestras o un conjunto de claves, busque [GenerateDataKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateDataKey.html)llamadas en los CloudTrail registros. 
+ Compare los [mensajes cifrados](concepts.md#message) que el AWS Encryption SDK devuelve en respuesta a diferentes solicitudes de cifrado. Por ejemplo, si utiliza el SDK de cifrado de AWS para Java, compare el [ParsedCiphertext](https://aws.github.io/aws-encryption-sdk-java/com/amazonaws/encryptionsdk/ParsedCiphertext.html)objeto de diferentes llamadas de cifrado. En el SDK de cifrado de AWS para JavaScript, compare el contenido de la `encryptedDataKeys` propiedad del [MessageHeader](https://github.com/aws/aws-encryption-sdk-javascript/blob/master/modules/serialize/src/types.ts#L21). Cuando se reutilizan claves de datos, las claves de datos cifradas del mensaje cifrado son idénticas.

# Ejemplo de almacenamiento en caché de claves de datos
<a name="sample-cache-example"></a>

En este ejemplo se utiliza el [almacenamiento en caché de claves de datos](data-key-caching.md) con una [caché local](data-caching-details.md#simplecache) para acelerar una aplicación en la que los datos generados por varios dispositivos se cifren y almacenen en diferentes regiones.

En este caso, varios productores de datos generan datos, los cifran y escriben en una [secuencia de Kinesis](https://aws.amazon.com/kinesis/streams/) en cada región. Las funciones [AWS Lambda](https://aws.amazon.com/lambda/) (consumidores) descifran las transmisiones y escriben datos de texto no cifrado en una tabla de DynamoDB en la Región. Los productores y los consumidores de datos utilizan el AWS Encryption SDK y un [AWS KMS proveedor de claves maestras](concepts.md#master-key-provider). Para reducir las llamadas a KMS, cada productor y consumidor tiene su propio caché local.

Puede encontrar el código fuente de estos ejemplos en [Java y Python](sample-cache-example-code.md). El ejemplo también incluye una CloudFormation plantilla que define los recursos de las muestras.

![\[Este diagrama muestra cómo los productores y consumidores de datos utilizan Amazon Kinesis Data Streams y Amazon DynamoDB. AWS KMS\]](http://docs.aws.amazon.com/es_es/encryption-sdk/latest/developer-guide/images/simplecache-example.png)


## Resultados de la caché local
<a name="caching-example-impact"></a>

En la siguiente tabla se muestra que una caché local reduce las llamadas totales a KMS (por segundo y región) de este ejemplo al 1 % de su valor original.


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


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

# Ejemplo de almacenamiento en caché de claves de datos
<a name="sample-cache-example-code"></a>

Este ejemplo de código crea una implementación simple de almacenamiento en caché de claves de datos con una [caché local](data-caching-details.md#simplecache) en Java y Python. El código crea dos instancias de una caché local: una para [los productores de datos](#caching-producer) que cifran datos y otra para [los consumidores de datos](#caching-consumer) (AWS Lambda funciones) que descifran los datos. Para obtener más información sobre la implementación del almacenamiento en caché de claves de datos en cada lenguaje, consulte la documentación de [Javadoc](https://aws.github.io/aws-encryption-sdk-java/) y [Python](https://aws-encryption-sdk-python.readthedocs.io/en/latest/) para AWS Encryption SDK.

El almacenamiento en caché de claves de datos está disponible para todos los [lenguajes de programación compatibles](programming-languages.md). AWS Encryption SDK 

Para ver ejemplos completos y comprobados del uso del almacenamiento en caché de claves de datos en el AWS Encryption SDK, consulte:
+ C/C\$1\$1: [caching\$1cmm.cpp](https://github.com/aws/aws-encryption-sdk-c/blob/master/examples/caching_cmm.cpp) 
+ [Java: SimpleDataKeyCachingExample .java](https://github.com/aws/aws-encryption-sdk-java/blob/master/src/examples/java/com/amazonaws/crypto/examples/v2/SimpleDataKeyCachingExample.java)
+ JavaScript [Navegador: 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)

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

[El productor obtiene un mapa, lo convierte a JSON, lo utiliza AWS Encryption SDK para cifrarlo y envía el registro de texto cifrado a una transmisión de Kinesis en cada uno de ellos.](https://aws.amazon.com/kinesis/streams/) Región de AWS

El código define un [administrador de materiales criptográficos de almacenamiento en caché](data-caching-details.md#caching-cmm) (CMM de almacenamiento en caché) y lo asocia a una [caché local](data-caching-details.md#simplecache) y a un [proveedor de claves maestras de AWS KMS](concepts.md#master-key-provider) subyacente. El CMM de almacenamiento en caché almacena en caché las claves de datos (y los [materiales criptográficos relacionados](data-caching-details.md#cache-entries)) del proveedor de claves maestras. También interacciona con la caché en nombre del SDK y aplica los umbrales de seguridad establecidos. 

Como la llamada al método de cifrado especifica un CMM de almacenamiento en caché, en lugar de un [administrador de materiales criptográficos (CMM)](concepts.md#crypt-materials-manager) o un proveedor de clave maestra normal, el cifrado utilizará el almacenamiento en caché de claves de datos.

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

En el siguiente ejemplo, se utiliza la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* del SDK de cifrado de AWS para Java CMM de almacenamiento en caché de claves de datos está en desuso. Con la versión 3. *x*, también puede utilizar el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

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

------

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

El consumidor de datos es una función de [AWS Lambda](https://aws.amazon.com/lambda/) que se activa mediante eventos de [Kinesis](https://aws.amazon.com/kinesis/). Descifra y deserializa cada registro y lo escribe en texto no cifrado en una tabla de [Amazon DynamoDB](https://aws.amazon.com/dynamodb/) de la misma región.

Al igual que el código de productor, el código de consumidor permite el almacenamiento en caché de claves de datos mediante el uso de un administrador de materiales criptográficos de almacenamiento en caché (CMM) en llamadas al método de descifrado. 

El código Java crea un proveedor de claves maestras en *modo estricto* con una especificación. AWS KMS key El modo estricto no es obligatorio para descifrar, pero es una [práctica recomendada](best-practices.md#strict-discovery-mode). El código Python usa el *modo de descubrimiento*, que permite AWS Encryption SDK usar cualquier clave de empaquetado que cifre una clave de datos para descifrarla.

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

El siguiente ejemplo usa la versión 2. *x* del SDK de cifrado de AWS para Java. Versión 3. *x* del SDK de cifrado de AWS para Java CMM de almacenamiento en caché de claves de datos está en desuso. Con la versión 3. *x*, también puede utilizar el [llavero AWS KMS jerárquico](use-hierarchical-keyring.md), una solución alternativa de almacenamiento en caché de materiales criptográficos.

Este código crea un proveedor de claves maestras para descifrar en modo estricto. Solo AWS Encryption SDK puede usar AWS KMS keys lo que usted especifique para descifrar el mensaje.

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

Este código de Python se descifra con un proveedor de claves maestras en modo de detección. Permite AWS Encryption SDK utilizar cualquier clave de encapsulamiento que haya cifrado una clave de datos para descifrarla. La [práctica recomendada](best-practices.md#strict-discovery-mode) es el modo estricto, en el que se especifican las claves de encapsulamiento que se pueden utilizar para el descifrado.

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

------

# Ejemplo de almacenamiento en caché de claves de datos: plantilla CloudFormation
<a name="sample-cache-example-cloudformation"></a>

Esta CloudFormation plantilla configura todos los AWS recursos necesarios para reproducir el [ejemplo de almacenamiento en caché de claves de datos](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
```

------