指定使用客户提供的密钥的服务器端加密 (SSE-C)。 - Amazon Simple Storage Service

指定使用客户提供的密钥的服务器端加密 (SSE-C)。

要使用具有客户提供密钥的服务器端加密(SSE-C),请先确保在您的 Amazon S3 通用存储桶默认加密配置中,没有阻止 SSE-C 加密类型。如果此加密类型被阻止,您可以通过更新存储桶的默认加密配置来启用它。然后,您可以通过在上传请求中传递所需的标头来使用 SSE-C。请参阅支持使用 SSE-C 写入数据的 Amazon S3 操作,并确保包含 SSE-C 对象加密和解密请求所需的 S3 API 标头

如果您在上传对象时指定了 SSE-C,Amazon S3 将使用您提供的加密密钥对数据应用 AES-256 加密。然后,Amazon S3 从内存中删除此加密密钥。在检索对象时,必须提供相同的加密密钥作为您请求的一部分。Amazon S3 在将对象数据返回给您之前,会首先验证您提供的加密密钥是否匹配,然后再解密对象。

使用 SSE-C 之前,请确保您已查看使用 SSE-C 之前的注意事项

注意

Amazon S3 不存储您提供的加密密钥,而是存储加密密钥的添加了随机数据的 HMAC 散列消息认证码(HMAC)值,以验证将来的请求。无法使用添加了随机数据的 HMAC 值来推导出加密密钥的值或解密加密对象的内容。这意味着,如果您丢失加密密钥,则会失去该对象。

SSE-C 操作和必需表头

在支持的 S3 API 上指定 SSE-C 需要传递特定请求参数。

注意

Amazon S3 中的 PutBucketEncryption API 用于为存储桶配置默认的服务器端加密。但是,PutBucketEncryption 不支持启用 SSE-C 作为存储桶的默认加密方法。SSE-C 是对象级加密方法,您可以随对象上传或下载的每个请求向 Amazon S3 提供加密密钥。Amazon S3 在请求处理期间使用此密钥加密或解密对象,然后丢弃密钥。这意味着 SSE-C 是按对象启用的,而不是作为默认的存储桶设置。

支持使用 SSE-C 写入数据的 Amazon S3 操作

您可以使用以下 API 操作,在向通用存储桶写入对象时,请求使用具有客户提供密钥的服务器端加密(SSE-C):

注意

S3 复制支持使用 SSE-C 加密的对象。有关复制加密对象的更多信息,请参阅复制加密对象(SSE-S3、SSE-KMS、DSSE-KMS、SSE-C)

SSE-C 对象加密和解密请求所需的 S3 API 标头

要使用 SSE-C 加密或解密对象,您必须提供以下三个 API 标头:

  • x-amz-server-side-encryption-customer-algorithm:使用此标头来指定加密算法。标头值必须为 AES256。

  • x-amz-server-side-encryption-customer-key:使用此标头来提供 256 位的 base64 编码的加密密钥,供 Amazon S3 用于加密或解密您的数据。

  • x-amz-server-side-encryption-customer-key-MD5:使用此标头来根据 RFC 1321,提供加密密钥的 base64 编码的 128 位 MD5 摘要。Amazon S3 使用此标头进行消息完整性检查以确保加密密钥的传输无误。

请求复制使用 SSE-C 加密的源对象时需要的 S3 API 标头

要复制使用 SSE-C 加密的源对象,您必须提供以下三个 API 标头:

  • x-amz-copy-source-server-side-encryption-customer-algorithm:包括此标头以指定 Amazon S3 用于解密源对象的算法。此值必须为 AES256。

  • x-amz-copy-source-server-side-encryption-customer-key:包括此标头以提供 base64 编码的加密密钥,供 Amazon S3 用于解密源对象。此加密密钥必须是您在创建源对象时为 Amazon S3 提供的加密密钥。否则,Amazon S3 无法解密对象。

  • x-amz-copy-source-server-side-encryption-customer-key-MD5:包括此标头来根据 RFC 1321,提供加密密钥的 base64 编码的 128 位 MD5 摘要。

强制 SSE-C 加密的存储桶策略示例

如果要对写入 Amazon S3 存储桶中的所有对象要求使用 SSE-C 加密,您可以使用存储桶策略。例如,如果请求不包括用于请求 SSE-C 的 x-amz-server-side-encryption-customer-algorithm 标头,以下存储桶策略将拒绝针对所有此类请求的上传对象(s3:PutObject)权限。

{ "Version":"2012-10-17", "Id": "PutObjectPolicy", "Statement": [ { "Sid": "RequireSSECObjectUploads", "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*", "Condition": { "Null": { "s3:x-amz-server-side-encryption-customer-algorithm": "true" } } } ] }
重要

如果您使用存储桶策略在 s3:PutObject 上请求 SSE-C,则必须在所有分段上传请求中(CreateMultipartUpload、UploadPart 和 CompleteMultipartUpload)包括 x-amz-server-side-encryption-customer-algorithm 标头。

预签名 URL 和 SSE-C

您可以生成可用于上传新对象、检索现有对象或检索对象元数据等操作的预签名 URL。预签名 URL 支持 SSE-C,如下所示:

  • 在创建预签名 URL 时,您必须在签名计算中使用 x-amz-server-side-encryption-customer-algorithm 标头指定算法。

  • 在使用预签名 URL 上传新对象、检索现有对象或仅检索对象元数据时,您必须在您的客户端应用程序的请求中提供所有加密标头。

    注意

    对于非 SSE-C 对象,您可以生成预签名 URL,并将该 URL 直接复制到浏览器中以访问数据。

    但是,不能对 SSE-C 对象执行此操作,因为除了预签名 URL 外,还必须包含 SSE-C 对象特定的 HTTP 标头。因此,您只能以编程方式将预签名 URL 用于 SSE-C 对象。

有关预签名 URL 的更多信息,请参阅 使用预签名 URL 下载和上传对象

使用 SSE-C 发出请求

在使用 REST API 创建对象时,您可以使用客户提供的密钥(SSE-C)指定服务器端加密。使用 SSE-C 时,必须使用请求复制使用 SSE-C 加密的源对象时需要的 S3 API 标头提供加密密钥信息。您可以使用 AWS SDK 包装库将这些标头添加到您的请求中。如果需要,您可以直接在应用程序中调用 Amazon S3 REST API。

重要

在指定具有客户提供密钥的服务器端加密(SSE-C)之前,请确保通用存储桶未阻止 SSE-C 加密。有关更多信息,请参阅 对通用存储桶阻止或取消阻止 SSE-C

注意

您不能使用 Amazon S3 控制台上传对象并请求 SSE-C,也不能使用控制台来更新使用 SSE-C 存储的现有对象(例如,更改存储类别或添加元数据)。有关更多信息,请参阅SSE-C 对象加密和解密请求所需的 S3 API 标头

使用 REST API

支持 SSE-C 的 Amazon S3 REST API

以下 Amazon S3 API 支持使用客户提供的加密密钥进行服务器端加密 (SSE-C)。

  • GET 操作 – 在使用 GET API 检索对象(请参阅 GET Object)时,您可以指定请求标头。

  • HEAD 操作 – 要使用 HEAD API 检索对象元数据(请参阅 HEAD Object),可以指定这些请求标头。

  • PUT 操作 – 使用 PUT Object API 上传数据(请参阅 PUT Object)时,可以指定这些请求标头。

  • 分段上传 – 在使用分段上传 API 上传大对象时,可以指定这些标头。您可以在以下请求中指定这些标头:启动请求(请参阅启动分段上传),以及每个后续分段上传请求(请参阅上传分段UploadPartCopy)。对于每个分段上传请求,加密信息必须与您在启动分段上传请求中提供的信息相同。

  • POST 操作 – 使用 POST 操作上传对象(请参阅 POST 对象)时,可在表单字段而不是请求标头中提供相同的信息。

  • 复制操作:复制对象(请参阅 CopyObject)时,您同时具有源对象和目标对象:

使用 AWS SDK 为 PUT、GET、Head 和 Copy 操作指定 SSE-C

以下示例演示如何为对象请求使用客户提供的密钥的服务器端加密 (SSE-C)。这些示例执行以下操作。每个操作均演示了如何在请求中指定 SSE-C 相关标头:

  • 放置对象 – 上传对象,并请求使用客户提供的加密密钥的服务器端加密。

  • 获取对象 – 下载上一步中上传的对象。在请求中,应提供上传对象时提供的同一加密信息。Amazon S3 需要此信息来解密对象,以便将对象返回给您。

  • 获取对象元数据 – 检索对象的元数据。提供创建对象时使用的同一加密信息。

  • 复制对象 – 复制之前上传的对象的副本。因为源对象是使用 SSE-C 存储的,因此必须在复制请求中提供其加密信息。默认情况下,仅当您显式请求加密时,Amazon S3 才会为对象的副本加密。此示例指示 Amazon S3 存储对象的加密副本。

Java
注意

本示例显示如何在单个操作中上传对象。当使用分段上传 API 上传大型对象时,应按照此示例中所示的方式提供加密信息。有关使用 AWS SDK for Java 的分段上传的示例,请参阅 使用分段上传操作上传对象

要添加必需的加密信息,请在请求中包含 SSECustomerKey。有关该 SSECustomerKey 课程的更多信息,请参阅 REST API 部分。

有关创建和测试有效示例的说明,请参阅《AWS SDK for Java 开发人员指南》中的入门

import com.amazonaws.AmazonServiceException; import com.amazonaws.SdkClientException; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.*; import javax.crypto.KeyGenerator; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; public class ServerSideEncryptionUsingClientSideEncryptionKey { private static SSECustomerKey SSE_KEY; private static AmazonS3 S3_CLIENT; private static KeyGenerator KEY_GENERATOR; public static void main(String[] args) throws IOException, NoSuchAlgorithmException { Regions clientRegion = Regions.DEFAULT_REGION; String bucketName = "*** Bucket name ***"; String keyName = "*** Key name ***"; String uploadFileName = "*** File path ***"; String targetKeyName = "*** Target key name ***"; // Create an encryption key. KEY_GENERATOR = KeyGenerator.getInstance("AES"); KEY_GENERATOR.init(256, new SecureRandom()); SSE_KEY = new SSECustomerKey(KEY_GENERATOR.generateKey()); try { S3_CLIENT = AmazonS3ClientBuilder.standard() .withCredentials(new ProfileCredentialsProvider()) .withRegion(clientRegion) .build(); // Upload an object. uploadObject(bucketName, keyName, new File(uploadFileName)); // Download the object. downloadObject(bucketName, keyName); // Verify that the object is properly encrypted by attempting to retrieve it // using the encryption key. retrieveObjectMetadata(bucketName, keyName); // Copy the object into a new object that also uses SSE-C. copyObject(bucketName, keyName, targetKeyName); } catch (AmazonServiceException e) { // The call was transmitted successfully, but Amazon S3 couldn't process // it, so it returned an error response. e.printStackTrace(); } catch (SdkClientException e) { // Amazon S3 couldn't be contacted for a response, or the client // couldn't parse the response from Amazon S3. e.printStackTrace(); } } private static void uploadObject(String bucketName, String keyName, File file) { PutObjectRequest putRequest = new PutObjectRequest(bucketName, keyName, file).withSSECustomerKey(SSE_KEY); S3_CLIENT.putObject(putRequest); System.out.println("Object uploaded"); } private static void downloadObject(String bucketName, String keyName) throws IOException { GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, keyName).withSSECustomerKey(SSE_KEY); S3Object object = S3_CLIENT.getObject(getObjectRequest); System.out.println("Object content: "); displayTextInputStream(object.getObjectContent()); } private static void retrieveObjectMetadata(String bucketName, String keyName) { GetObjectMetadataRequest getMetadataRequest = new GetObjectMetadataRequest(bucketName, keyName) .withSSECustomerKey(SSE_KEY); ObjectMetadata objectMetadata = S3_CLIENT.getObjectMetadata(getMetadataRequest); System.out.println("Metadata retrieved. Object size: " + objectMetadata.getContentLength()); } private static void copyObject(String bucketName, String keyName, String targetKeyName) throws NoSuchAlgorithmException { // Create a new encryption key for target so that the target is saved using // SSE-C. SSECustomerKey newSSEKey = new SSECustomerKey(KEY_GENERATOR.generateKey()); CopyObjectRequest copyRequest = new CopyObjectRequest(bucketName, keyName, bucketName, targetKeyName) .withSourceSSECustomerKey(SSE_KEY) .withDestinationSSECustomerKey(newSSEKey); S3_CLIENT.copyObject(copyRequest); System.out.println("Object copied"); } private static void displayTextInputStream(S3ObjectInputStream input) throws IOException { // Read one line at a time from the input stream and display each line. BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } System.out.println(); } }
.NET
注意

有关使用分段上传 API 上传大型对象的示例,请参阅 使用分段上传操作上传对象使用 AWS SDK(低级别 API)

有关设置和运行代码示例的信息,请参阅《适用于 .NET 的 AWS SDK 开发人员指南》中的适用于 .NET 的 AWS SDK 入门

using Amazon; using Amazon.S3; using Amazon.S3.Model; using System; using System.IO; using System.Security.Cryptography; using System.Threading.Tasks; namespace Amazon.DocSamples.S3 { class SSEClientEncryptionKeyObjectOperationsTest { private const string bucketName = "*** bucket name ***"; private const string keyName = "*** key name for new object created ***"; private const string copyTargetKeyName = "*** key name for object copy ***"; // Specify your bucket region (an example region is shown). private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2; private static IAmazonS3 client; public static void Main() { client = new AmazonS3Client(bucketRegion); ObjectOpsUsingClientEncryptionKeyAsync().Wait(); } private static async Task ObjectOpsUsingClientEncryptionKeyAsync() { try { // Create an encryption key. Aes aesEncryption = Aes.Create(); aesEncryption.KeySize = 256; aesEncryption.GenerateKey(); string base64Key = Convert.ToBase64String(aesEncryption.Key); // 1. Upload the object. PutObjectRequest putObjectRequest = await UploadObjectAsync(base64Key); // 2. Download the object and verify that its contents matches what you uploaded. await DownloadObjectAsync(base64Key, putObjectRequest); // 3. Get object metadata and verify that the object uses AES-256 encryption. await GetObjectMetadataAsync(base64Key); // 4. Copy both the source and target objects using server-side encryption with // a customer-provided encryption key. await CopyObjectAsync(aesEncryption, base64Key); } catch (AmazonS3Exception e) { Console.WriteLine("Error encountered ***. Message:'{0}' when writing an object", e.Message); } catch (Exception e) { Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message); } } private static async Task<PutObjectRequest> UploadObjectAsync(string base64Key) { PutObjectRequest putObjectRequest = new PutObjectRequest { BucketName = bucketName, Key = keyName, ContentBody = "sample text", ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key }; PutObjectResponse putObjectResponse = await client.PutObjectAsync(putObjectRequest); return putObjectRequest; } private static async Task DownloadObjectAsync(string base64Key, PutObjectRequest putObjectRequest) { GetObjectRequest getObjectRequest = new GetObjectRequest { BucketName = bucketName, Key = keyName, // Provide encryption information for the object stored in Amazon S3. ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key }; using (GetObjectResponse getResponse = await client.GetObjectAsync(getObjectRequest)) using (StreamReader reader = new StreamReader(getResponse.ResponseStream)) { string content = reader.ReadToEnd(); if (String.Compare(putObjectRequest.ContentBody, content) == 0) Console.WriteLine("Object content is same as we uploaded"); else Console.WriteLine("Error...Object content is not same."); if (getResponse.ServerSideEncryptionCustomerMethod == ServerSideEncryptionCustomerMethod.AES256) Console.WriteLine("Object encryption method is AES256, same as we set"); else Console.WriteLine("Error...Object encryption method is not the same as AES256 we set"); // Assert.AreEqual(putObjectRequest.ContentBody, content); // Assert.AreEqual(ServerSideEncryptionCustomerMethod.AES256, getResponse.ServerSideEncryptionCustomerMethod); } } private static async Task GetObjectMetadataAsync(string base64Key) { GetObjectMetadataRequest getObjectMetadataRequest = new GetObjectMetadataRequest { BucketName = bucketName, Key = keyName, // The object stored in Amazon S3 is encrypted, so provide the necessary encryption information. ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key }; GetObjectMetadataResponse getObjectMetadataResponse = await client.GetObjectMetadataAsync(getObjectMetadataRequest); Console.WriteLine("The object metadata show encryption method used is: {0}", getObjectMetadataResponse.ServerSideEncryptionCustomerMethod); // Assert.AreEqual(ServerSideEncryptionCustomerMethod.AES256, getObjectMetadataResponse.ServerSideEncryptionCustomerMethod); } private static async Task CopyObjectAsync(Aes aesEncryption, string base64Key) { aesEncryption.GenerateKey(); string copyBase64Key = Convert.ToBase64String(aesEncryption.Key); CopyObjectRequest copyRequest = new CopyObjectRequest { SourceBucket = bucketName, SourceKey = keyName, DestinationBucket = bucketName, DestinationKey = copyTargetKeyName, // Information about the source object's encryption. CopySourceServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, CopySourceServerSideEncryptionCustomerProvidedKey = base64Key, // Information about the target object's encryption. ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = copyBase64Key }; await client.CopyObjectAsync(copyRequest); } } }

使用 AWS SDK 为分段上传指定 SSE-C

上一部分中的示例显示了如何在 PUT、GET、Head 和 Copy 操作中请求使用客户提供的密钥的服务器端加密 (SSE-C)。本节介绍支持 SSE-C 的其他 Amazon S3 API。

Java

要上传大型对象,您可以使用分段上传 API。有关更多信息,请参阅 在 Amazon S3 中使用分段上传来上传和复制对象。可以使用高级或低级 API 上传大型对象。这些 API 支持在请求中使用与加密相关的标头。

  • 使用高级别 TransferManager API 时,应在 PutObjectRequest 中提供特定于加密的标头。有关更多信息,请参阅 使用分段上传操作上传对象

  • 当使用低级别 API 时,应提供 InitiateMultipartUploadRequest 中的加密相关信息,后跟每个 UploadPartRequest 中的相同加密信息。不需要在 CompleteMultipartUploadRequest 中提供任何特定于加密的标头。有关示例,请参阅 使用 AWS SDK(低级别 API)

以下示例使用 TransferManager 创建对象并显示如何提供 SSE-C 相关信息。本示例执行以下操作:

  • 使用 TransferManager.upload() 方法创建对象。在 PutObjectRequest 实例中,应在请求中提供加密密钥信息。Amazon S3 将使用客户提供的密钥对于对象进行加密。

  • 通过调用 TransferManager.copy() 方法创建对象的副本。该示例指示 Amazon S3 使用新的 SSECustomerKey 对对象副本进行加密。由于源对象使用 SSE-C 加密,因此 CopyObjectRequest 还提供了源对象的加密密钥,以便 Amazon S3 可以在复制对象之前解密对象。

import com.amazonaws.AmazonServiceException; import com.amazonaws.SdkClientException; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.CopyObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.SSECustomerKey; import com.amazonaws.services.s3.transfer.Copy; import com.amazonaws.services.s3.transfer.TransferManager; import com.amazonaws.services.s3.transfer.TransferManagerBuilder; import com.amazonaws.services.s3.transfer.Upload; import javax.crypto.KeyGenerator; import java.io.File; import java.security.SecureRandom; public class ServerSideEncryptionCopyObjectUsingHLwithSSEC { public static void main(String[] args) throws Exception { Regions clientRegion = Regions.DEFAULT_REGION; String bucketName = "*** Bucket name ***"; String fileToUpload = "*** File path ***"; String keyName = "*** New object key name ***"; String targetKeyName = "*** Key name for object copy ***"; try { AmazonS3 s3Client = AmazonS3ClientBuilder.standard() .withRegion(clientRegion) .withCredentials(new ProfileCredentialsProvider()) .build(); TransferManager tm = TransferManagerBuilder.standard() .withS3Client(s3Client) .build(); // Create an object from a file. PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, keyName, new File(fileToUpload)); // Create an encryption key. KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(256, new SecureRandom()); SSECustomerKey sseCustomerEncryptionKey = new SSECustomerKey(keyGenerator.generateKey()); // Upload the object. TransferManager uploads asynchronously, so this call // returns immediately. putObjectRequest.setSSECustomerKey(sseCustomerEncryptionKey); Upload upload = tm.upload(putObjectRequest); // Optionally, wait for the upload to finish before continuing. upload.waitForCompletion(); System.out.println("Object created."); // Copy the object and store the copy using SSE-C with a new key. CopyObjectRequest copyObjectRequest = new CopyObjectRequest(bucketName, keyName, bucketName, targetKeyName); SSECustomerKey sseTargetObjectEncryptionKey = new SSECustomerKey(keyGenerator.generateKey()); copyObjectRequest.setSourceSSECustomerKey(sseCustomerEncryptionKey); copyObjectRequest.setDestinationSSECustomerKey(sseTargetObjectEncryptionKey); // Copy the object. TransferManager copies asynchronously, so this call returns // immediately. Copy copy = tm.copy(copyObjectRequest); // Optionally, wait for the upload to finish before continuing. copy.waitForCompletion(); System.out.println("Copy complete."); } catch (AmazonServiceException e) { // The call was transmitted successfully, but Amazon S3 couldn't process // it, so it returned an error response. e.printStackTrace(); } catch (SdkClientException e) { // Amazon S3 couldn't be contacted for a response, or the client // couldn't parse the response from Amazon S3. e.printStackTrace(); } } }
.NET

要上传大型对象,您可以使用分段上传 API(请参阅在 Amazon S3 中使用分段上传来上传和复制对象)。AWS适用于 .NET 的 SDK 提供了高级或低级 API 来上传大型对象。这些 API 支持在请求中使用与加密相关的标头。

  • 当使用高级 Transfer-Utility API 时,可以在 TransferUtilityUploadRequest 中提供特定于加密的标头,如下所示。有关代码示例,请参阅 使用分段上传操作上传对象

    TransferUtilityUploadRequest request = new TransferUtilityUploadRequest() { FilePath = filePath, BucketName = existingBucketName, Key = keyName, // Provide encryption information. ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key, };
  • 当使用低级 API 时,可以在启动分段上传请求中提供加密相关的信息,并在后续的分段上传请求中提供相同加密信息。不需要在完成的分段上传请求中提供任何特定于加密的标头。有关示例,请参阅 使用 AWS SDK(低级别 API)

    下面是一个低级分段上传示例,该示例复制一个现有大型对象。在本示例中,要复制的对象使用 SSE-C 存储在 Amazon S3 中,并且您希望使用 SSE-C 保存目标对象。在本例中,您可以执行以下操作:

    • 通过提供加密密钥和相关信息启动分段上传请求。

    • CopyPartRequest 中提供源和目标对象加密密钥及相关信息。

    • 通过检索对象元数据获取要复制的源对象的大小。

    • 以 5 MB 大小的分段上传对象。

    using Amazon; using Amazon.S3; using Amazon.S3.Model; using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Threading.Tasks; namespace Amazon.DocSamples.S3 { class SSECLowLevelMPUcopyObjectTest { private const string existingBucketName = "*** bucket name ***"; private const string sourceKeyName = "*** source object key name ***"; private const string targetKeyName = "*** key name for the target object ***"; private const string filePath = @"*** file path ***"; // Specify your bucket region (an example region is shown). private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USWest2; private static IAmazonS3 s3Client; static void Main() { s3Client = new AmazonS3Client(bucketRegion); CopyObjClientEncryptionKeyAsync().Wait(); } private static async Task CopyObjClientEncryptionKeyAsync() { Aes aesEncryption = Aes.Create(); aesEncryption.KeySize = 256; aesEncryption.GenerateKey(); string base64Key = Convert.ToBase64String(aesEncryption.Key); await CreateSampleObjUsingClientEncryptionKeyAsync(base64Key, s3Client); await CopyObjectAsync(s3Client, base64Key); } private static async Task CopyObjectAsync(IAmazonS3 s3Client, string base64Key) { List<CopyPartResponse> uploadResponses = new List<CopyPartResponse>(); // 1. Initialize. InitiateMultipartUploadRequest initiateRequest = new InitiateMultipartUploadRequest { BucketName = existingBucketName, Key = targetKeyName, ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key, }; InitiateMultipartUploadResponse initResponse = await s3Client.InitiateMultipartUploadAsync(initiateRequest); // 2. Upload Parts. long partSize = 5 * (long)Math.Pow(2, 20); // 5 MB long firstByte = 0; long lastByte = partSize; try { // First find source object size. Because object is stored encrypted with // customer provided key you need to provide encryption information in your request. GetObjectMetadataRequest getObjectMetadataRequest = new GetObjectMetadataRequest() { BucketName = existingBucketName, Key = sourceKeyName, ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key // " * **source object encryption key ***" }; GetObjectMetadataResponse getObjectMetadataResponse = await s3Client.GetObjectMetadataAsync(getObjectMetadataRequest); long filePosition = 0; for (int i = 1; filePosition < getObjectMetadataResponse.ContentLength; i++) { CopyPartRequest copyPartRequest = new CopyPartRequest { UploadId = initResponse.UploadId, // Source. SourceBucket = existingBucketName, SourceKey = sourceKeyName, // Source object is stored using SSE-C. Provide encryption information. CopySourceServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, CopySourceServerSideEncryptionCustomerProvidedKey = base64Key, //"***source object encryption key ***", FirstByte = firstByte, // If the last part is smaller then our normal part size then use the remaining size. LastByte = lastByte > getObjectMetadataResponse.ContentLength ? getObjectMetadataResponse.ContentLength - 1 : lastByte, // Target. DestinationBucket = existingBucketName, DestinationKey = targetKeyName, PartNumber = i, // Encryption information for the target object. ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key }; uploadResponses.Add(await s3Client.CopyPartAsync(copyPartRequest)); filePosition += partSize; firstByte += partSize; lastByte += partSize; } // Step 3: complete. CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest { BucketName = existingBucketName, Key = targetKeyName, UploadId = initResponse.UploadId, }; completeRequest.AddPartETags(uploadResponses); CompleteMultipartUploadResponse completeUploadResponse = await s3Client.CompleteMultipartUploadAsync(completeRequest); } catch (Exception exception) { Console.WriteLine("Exception occurred: {0}", exception.Message); AbortMultipartUploadRequest abortMPURequest = new AbortMultipartUploadRequest { BucketName = existingBucketName, Key = targetKeyName, UploadId = initResponse.UploadId }; s3Client.AbortMultipartUpload(abortMPURequest); } } private static async Task CreateSampleObjUsingClientEncryptionKeyAsync(string base64Key, IAmazonS3 s3Client) { // List to store upload part responses. List<UploadPartResponse> uploadResponses = new List<UploadPartResponse>(); // 1. Initialize. InitiateMultipartUploadRequest initiateRequest = new InitiateMultipartUploadRequest { BucketName = existingBucketName, Key = sourceKeyName, ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key }; InitiateMultipartUploadResponse initResponse = await s3Client.InitiateMultipartUploadAsync(initiateRequest); // 2. Upload Parts. long contentLength = new FileInfo(filePath).Length; long partSize = 5 * (long)Math.Pow(2, 20); // 5 MB try { long filePosition = 0; for (int i = 1; filePosition < contentLength; i++) { UploadPartRequest uploadRequest = new UploadPartRequest { BucketName = existingBucketName, Key = sourceKeyName, UploadId = initResponse.UploadId, PartNumber = i, PartSize = partSize, FilePosition = filePosition, FilePath = filePath, ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key }; // Upload part and add response to our list. uploadResponses.Add(await s3Client.UploadPartAsync(uploadRequest)); filePosition += partSize; } // Step 3: complete. CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest { BucketName = existingBucketName, Key = sourceKeyName, UploadId = initResponse.UploadId, //PartETags = new List<PartETag>(uploadResponses) }; completeRequest.AddPartETags(uploadResponses); CompleteMultipartUploadResponse completeUploadResponse = await s3Client.CompleteMultipartUploadAsync(completeRequest); } catch (Exception exception) { Console.WriteLine("Exception occurred: {0}", exception.Message); AbortMultipartUploadRequest abortMPURequest = new AbortMultipartUploadRequest { BucketName = existingBucketName, Key = sourceKeyName, UploadId = initResponse.UploadId }; await s3Client.AbortMultipartUploadAsync(abortMPURequest); } } } }