本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
資料金鑰快取範例程式碼
此程式碼範例會在 Java 和 Python 中使用本機快取建立資料金鑰快取的簡單實作。此程式碼會建立本機快取的兩個執行個體:一個是用於加密資料的資料生產者,另一個是用於解密資料的資料取用者 (AWS Lambda 函數)。如需在每種語言中實作資料金鑰快取的詳細資訊,請參閱 的 Javadoc
資料金鑰快取適用於 AWS Encryption SDK 支援的所有程式設計語言。
如需在 中使用資料金鑰快取的完整且經過測試的範例 AWS Encryption SDK,請參閱:
-
C/C++:caching_cmm.cpp
-
JavaScript 瀏覽器:caching_cmm.ts
-
JavaScript Node.js:caching_cmm.ts
-
Python:data_key_caching_basic.py
生產者
生產者會取得地圖、將其轉換為 JSON、使用 AWS Encryption SDK 來加密它,並將密碼文字記錄推送到每個 中的 Kinesis 串流
此程式碼會定義快取密碼編譯材料管理員 (快取 CMM),並將其與本機快取和基礎AWS KMS 主金鑰提供者 建立關聯。快取會從主要金鑰提供者CMM快取資料金鑰 (和相關的密碼編譯材料)。它也會代表 與快取互動,SDK並強制執行您設定的安全閾值。
由於呼叫加密方法會指定快取 CMM,而不是一般密碼編譯材料管理器 (CMM) 或主金鑰提供者,因此加密會使用資料金鑰快取。
- Java
-
下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.x 版。3.x 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。對於 3.x 版,您也可以使用AWS KMS 階層式金鑰環 ,這是替代的密碼編譯材料快取解決方案。
/* * 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 )
消費者
資料取用者是由 Kinesis
與生產者程式碼一樣,取用者程式碼會在對解密方法的呼叫中使用快取密碼編譯材料管理員 (快取 CMM) 來啟用資料金鑰快取。
Java 程式碼會使用指定的 ,在嚴格模式下建置主要金鑰提供者 AWS KMS key。解密時不需要嚴格模式,但這是最佳實務 。Python 程式碼使用探索模式 ,可讓 AWS Encryption SDK 使用任何加密資料金鑰的包裝金鑰來解密它。
- Java
-
下列範例使用 的 2 適用於 JAVA 的 AWS Encryption SDK.x 版。3.x 版會 適用於 JAVA 的 AWS Encryption SDK 取代資料金鑰快取 CMM。對於 3.x 版,您也可以使用AWS KMS 階層式金鑰環 ,這是替代的密碼編譯材料快取解決方案。
此程式碼會建立主要金鑰提供者,以便在嚴格模式下解密。只能 AWS Encryption SDK 使用 AWS KMS keys 您指定的 來解密訊息。
/* * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except * in compliance with the License. A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package com.amazonaws.crypto.examples.kinesisdatakeycaching; import com.amazonaws.encryptionsdk.AwsCrypto; import com.amazonaws.encryptionsdk.CommitmentPolicy; import com.amazonaws.encryptionsdk.CryptoResult; import com.amazonaws.encryptionsdk.caching.CachingCryptoMaterialsManager; import com.amazonaws.encryptionsdk.caching.LocalCryptoMaterialsCache; import com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.KinesisEvent; import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord; import com.amazonaws.util.BinaryUtils; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; /** * Decrypts all incoming Kinesis records and writes records to DynamoDB. */ public class LambdaDecryptAndWrite { private static final long MAX_ENTRY_AGE_MILLISECONDS = 600000; private static final int MAX_CACHE_ENTRIES = 100; private final CachingCryptoMaterialsManager cachingMaterialsManager_; private final AwsCrypto crypto_; private final DynamoDbTable<Item> table_; /** * Because the cache is used only for decryption, the code doesn't set the max bytes or max * message security thresholds that are enforced only on on data keys used for encryption. */ public LambdaDecryptAndWrite() { String kmsKeyArn = System.getenv("CMK_ARN"); cachingMaterialsManager_ = CachingCryptoMaterialsManager.newBuilder() .withMasterKeyProvider(KmsMasterKeyProvider.builder().buildStrict(kmsKeyArn)) .withCache(new LocalCryptoMaterialsCache(MAX_CACHE_ENTRIES)) .withMaxAge(MAX_ENTRY_AGE_MILLISECONDS, TimeUnit.MILLISECONDS) .build(); crypto_ = AwsCrypto.builder() .withCommitmentPolicy(CommitmentPolicy.RequireEncryptRequireDecrypt) .build(); String tableName = System.getenv("TABLE_NAME"); DynamoDbEnhancedClient dynamodb = DynamoDbEnhancedClient.builder().build(); table_ = dynamodb.table(tableName, TableSchema.fromClass(Item.class)); } /** * @param event * @param context */ public void handleRequest(KinesisEvent event, Context context) throws UnsupportedEncodingException { for (KinesisEventRecord record : event.getRecords()) { ByteBuffer ciphertextBuffer = record.getKinesis().getData(); byte[] ciphertext = BinaryUtils.copyAllBytesFrom(ciphertextBuffer); // Decrypt and unpack record CryptoResult<byte[], ?> plaintextResult = crypto_.decryptData(cachingMaterialsManager_, ciphertext); // Verify the encryption context value String streamArn = record.getEventSourceARN(); String streamName = streamArn.substring(streamArn.indexOf("/") + 1); if (!streamName.equals(plaintextResult.getEncryptionContext().get("stream"))) { throw new IllegalStateException("Wrong Encryption Context!"); } // Write record to DynamoDB String jsonItem = new String(plaintextResult.getResult(), StandardCharsets.UTF_8); System.out.println(jsonItem); table_.putItem(Item.fromJSON(jsonItem)); } } private static class Item { static Item fromJSON(String jsonText) { // Parse JSON and create new Item return new Item(); } } }
- Python
-
此 Python 程式碼會在探索模式下使用主要金鑰提供者進行解密。它可讓 AWS Encryption SDK 使用任何已加密資料金鑰的包裝金鑰來解密它。嚴格模式是最佳實務,您可以在其中指定可用於解密的包裝金鑰。
""" Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 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)