使用具有客户提供的密钥的服务器端加密(SSE-C) - Amazon Simple Storage Service
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

使用具有客户提供的密钥的服务器端加密(SSE-C)

服务器端加密是为了保护静态数据。服务器端加密仅加密对象数据而非加密对象元数据。使用具有客户提供的密钥的服务器端加密(SSE-C),您可以设置自己的加密密钥。使用您作为请求的一部分提供的加密密钥,Amazon S3 在其写入磁盘时管理数据加密,并在您访问对象时管理数据解密。因此,您不需要维护任何代码来执行数据加密和解密。您只需管理您提供的加密密钥。

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

使用 SSE-C 没有额外费用。但是,配置和使用 SSE-C 的请求会产生标准的 Amazon S3 请求费用。有关定价的信息,请参阅 Amazon S3 定价

注意

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

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

有关 SSE-C 的更多信息,请参阅以下主题。

SSE-C 概览

本部分提供 SSE-C 的概述。使用 SSE-C 时,请记住以下注意事项。

  • 您必须使用 HTTPS。

    重要

    在使用 SSE-C 时,Amazon S3 会拒绝通过 HTTP 发出的所有请求。出于安全原因,我们建议您考虑您错误地通过 HTTP 发送的任何密钥都会遭泄露。丢弃该密钥,并根据需要轮换密钥。

  • 响应中的实体标签(ETag)不是对象数据的 MD5 哈希。

  • 您管理哪个加密密钥用于加密哪个对象的映射。Amazon S3 不存储加密密钥。您负责跟踪为哪个对象提供了哪个加密密钥。

    • 如果您的存储桶启用了版本控制,则您使用此特征上载的每个对象版本可能都具有自己的加密密钥。您负责跟踪哪个加密密钥用于哪个对象版本。

    • 因为您在客户端管理加密密钥,所以也要在客户端管理所有额外的保护措施,例如密钥轮换。

    警告

    如果您丢失加密密钥,则在没有加密密钥的情况下对于对象的任何 GET 请求都会失败,并且您将丢失该对象。

要求和限制 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:::DOC-EXAMPLE-BUCKET/*", "Condition": { "Null": { "s3:x-amz-server-side-encryption-customer-algorithm": "true" } } } ] }

也可以使用策略,以限制对特定 Amazon S3 存储桶中的所有对象进行服务器端加密。例如,如果请求包含用于请求 SSE-C 的 x-amz-server-side-encryption-customer-algorithm 标头,则下面的存储桶策略将对所有人拒绝上载对象(s3:PutObject)权限。

{ "Version": "2012-10-17", "Id": "PutObjectPolicy", "Statement": [ { "Sid": "RestrictSSECObjectUploads", "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*", "Condition": { "Null": { "s3:x-amz-server-side-encryption-customer-algorithm": "false" } } } ] }
重要

如果您使用存储桶策略在 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 时,必须使用以下请求标头提供加密密钥信息。

名称 描述
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 使用此标头进行消息完整性检查以确保加密密钥的传输无误。

您可以使用 Amazon SDK 包装库将这些标头添加到您的请求中。如果需要,您可以直接在应用程序中调用 Amazon S3 REST API。

注意

您不能使用 Amazon S3 控制台上载对象并请求 SSE-C。也不能使用控制台来更新使用 SSE-C 存储的现有对象(例如,更改存储类或添加元数据)。

支持 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 上载大对象时,可以指定这些标头。您可以在以下请求中指定这些标头:启动请求(请参阅启动分段上传)和每个后续分段上传请求(请参阅上传分段上传分段 - 复制)。对于每个分段上传请求,加密信息必须与您在启动分段上传请求中提供的信息相同。

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

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

    • 如果您希望使用具有 Amazon 托管式密钥的服务器端加密对目标对象加密,则必须提供 x-amz-server-side​-encryption 请求标头。

    • 如果您希望使用 SSE-C 对目标对象加密,则必须使用上表中描述的三个标头提供加密信息。

    • 如果源对象是使用 SSE-C 加密的,则您必须使用以下标头提供加密密钥信息,以便 Amazon S3 可以解密对象以进行复制。

      名称 描述
      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)。这些示例执行以下操作。每个操作均演示了如何在请求中指定 SSE-C 相关标头:

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

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

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

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

Java
注意

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

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

有关 SSE-C 的信息,请参阅 使用具有客户提供的密钥的服务器端加密(SSE-C)。有关创建和测试有效示例的说明,请参阅 测试 Amazon S3 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 上传大型对象的示例,请参阅 使用分段上传上传对象使用 Amazon SDK(低级别 API)

有关 SSE-C 的信息,请参阅 使用具有客户提供的密钥的服务器端加密(SSE-C)。有关创建和测试有效示例的信息,请参阅 运行 Amazon S3 .NET 代码示例

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); } } }

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

Java

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

  • 当使用高级别 TransferManager API 时,应在 PutObjectRequest 中提供特定于加密的标头 (请参阅 使用分段上传上传对象)。

  • 当使用低级别 API 时,应提供 InitiateMultipartUploadRequest 中的加密相关信息,后跟每个 UploadPartRequest 中的相同加密信息。不需要在 CompleteMultipartUploadRequest 中提供任何特定于加密的标头。有关示例,请参阅使用 Amazon 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适用于 .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 时,可以在启动分段上传请求中提供加密相关的信息,并在后续的分段上传请求中提供相同加密信息。不需要在完成的分段上传请求中提供任何特定于加密的标头。有关示例,请参阅使用 Amazon 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); } } } }