Exemplo de código de armazenamento em cache de chaves de dados - AWS Encryption SDK

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Exemplo de código de armazenamento em cache de chaves de dados

Este exemplo de código cria uma implementação básica do armazenamento em cache de chaves de dados com um cache local em Java e Python. O código cria duas instâncias de um cache local: uma para produtores de dados que estão criptografando dados e outra para consumidores de dados (AWS Lambda funções) que estão descriptografando dados. Para obter detalhes sobre a implementação do armazenamento em cache de chaves de dados em cada linguagem, consulte a documentação de Javadoc e Python para o AWS Encryption SDK.

O armazenamento em cache de chaves de dados está disponível para todas as linguagens de programação suportadas AWS Encryption SDK .

Para obter exemplos completos e testados do uso do armazenamento em cache de chaves de dados no AWS Encryption SDK, consulte:

Produtor

O produtor obtém um mapa, o converte emJSON, usa o AWS Encryption SDK para criptografá-lo e envia o registro de texto cifrado para um stream do Kinesis em cada um. Região da AWS

O código define um gerenciador de materiais criptográficos em cache (cacheCMM) e o associa a um cache local e a um provedor de chave mestra subjacente AWS KMS . O cache CMM armazena em cache as chaves de dados (e materiais criptográficos relacionados) do provedor da chave mestra. Ele também interage com o cache em nome do SDK e impõe os limites de segurança definidos por você.

Como a chamada para o método encrypt especifica um armazenamento em cacheCMM, em vez de um gerenciador de materiais criptográficos comum (CMM) ou provedor de chave mestra, a criptografia usará o cache de chave de dados.

Java

O exemplo a seguir usa a versão 2. x do AWS Encryption SDK for Java. Versão 3. x of the AWS Encryption SDK for Java descontinua o armazenamento em cache da chave de dados. CMM Com a versão 3. x, você também pode usar o AWS KMS chaveiro hierárquico, uma solução alternativa de cache de materiais 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

O consumidor de dados é uma função do AWS Lambda acionada por eventos do Kinesis. Ele descriptografa e desserializa cada registro e grava o registro de texto simples em uma tabela do Amazon DynamoDB na mesma região.

Assim como o código do produtor, o código do consumidor permite o armazenamento em cache da chave de dados usando um gerenciador de materiais criptográficos (cacheCMM) em chamadas para o método de descriptografia.

O código Java cria um provedor de chave mestra no modo estrito com um especificado AWS KMS key. O modo estrito não é necessário ao descriptografar, mas é uma prática recomendada. O código Python usa o modo de descoberta, que permite AWS Encryption SDK usar qualquer chave de empacotamento que criptografe uma chave de dados para descriptografá-la.

Java

O exemplo a seguir usa a versão 2. x do AWS Encryption SDK for Java. Versão 3. x of the AWS Encryption SDK for Java descontinua o armazenamento em cache da chave de dados. CMM Com a versão 3. x, você também pode usar o AWS KMS chaveiro hierárquico, uma solução alternativa de cache de materiais criptográficos.

Esse código cria um provedor de chave mestra para descriptografia no modo estrito. O AWS Encryption SDK pode usar somente o AWS KMS keys que você especificar para descriptografar sua mensagem.

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

Esse código Python é descriptografado com um provedor de chave mestra no modo de descoberta. Ele permite ao AWS Encryption SDK usar qualquer chave de encapsulamento que criptografe uma chave de dados para descriptografá-la. O modo estrito, no qual você especifica as chaves de encapsulamento que podem ser usadas para decodificação, é uma prática recomendada.

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