

# 指定使用客户提供的密钥的服务器端加密 (SSE-C)。
<a name="specifying-s3-c-encryption"></a>

要使用具有客户提供密钥的服务器端加密（SSE-C），请先确保在您的 Amazon S3 通用存储桶默认加密配置中，没有阻止 SSE-C 加密类型。如果此加密类型被阻止，您可以通过更新存储桶的默认加密配置来启用它。然后，您可以通过在上传请求中传递所需的标头来使用 SSE-C。请参阅[支持使用 SSE-C 写入数据的 Amazon S3 操作](#amazon-s3-actions-that-support-writing-data-with-sse-c)，并确保包含 [SSE-C 对象加密和解密请求所需的 S3 API 标头](#s3-api-headers-required-for-sse-c-object-encryption-and-decryption-requests)。

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

使用 SSE-C 之前，请确保您已查看[使用 SSE-C 之前的注意事项](ServerSideEncryptionCustomerKeys.md#considerations-before-using-sse-c)。

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

**Topics**
+ [SSE-C 操作和必需表头](#sse-c-actions-and-required-headers)
+ [强制 SSE-C 加密的存储桶策略示例](#example-bucket-policy-to-enforce-sse-c-encryption)
+ [预签名 URL 和 SSE-C](#ssec-and-presignedurl)
+ [使用 SSE-C 发出请求](#making-requests-with-sse-c)
+ [使用 REST API](#using-rest-api-sse-c)
+ [使用 Amazon SDK 为 PUT、GET、Head 和 Copy 操作指定 SSE-C](#sse-c-using-sdks)
+ [使用 Amazon SDK 为分段上传指定 SSE-C](#sse-c-using-sdks-multipart-uploads)

## SSE-C 操作和必需表头
<a name="sse-c-actions-and-required-headers"></a>

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

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

### 支持使用 SSE-C 写入数据的 Amazon S3 操作
<a name="amazon-s3-actions-that-support-writing-data-with-sse-c"></a>

您可以使用以下 API 操作，在向通用存储桶写入对象时，请求使用具有客户提供密钥的服务器端加密（SSE-C）：
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/API_CopyObject.html](https://docs.amazonaws.cn/AmazonS3/latest/API/API_CopyObject.html)
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/API_CreateMultipartUpload.html](https://docs.amazonaws.cn/AmazonS3/latest/API/API_CreateMultipartUpload.html)
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/API_CompleteMultipartUpload.html](https://docs.amazonaws.cn/AmazonS3/latest/API/API_CompleteMultipartUpload.html)
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectPOST.html](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectPOST.html)
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/API_PutObject.html](https://docs.amazonaws.cn/AmazonS3/latest/API/API_PutObject.html)
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/API_UploadPart.html](https://docs.amazonaws.cn/AmazonS3/latest/API/API_UploadPart.html)
+ [https://docs.amazonaws.cn/AmazonS3/latest/API/API_UploadPartCopy.html](https://docs.amazonaws.cn/AmazonS3/latest/API/API_UploadPartCopy.html)

**注意**  
S3 复制支持使用 SSE-C 加密的对象。有关复制加密对象的更多信息，请参阅[复制加密对象（SSE-S3、SSE-KMS、DSSE-KMS、SSE-C）](replication-config-for-kms-objects.md)。

### SSE-C 对象加密和解密请求所需的 S3 API 标头
<a name="s3-api-headers-required-for-sse-c-object-encryption-and-decryption-requests"></a>

要使用 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 标头
<a name="s3-api-headers-required-for-requests-to-copy-source-objects-encrypted-with-sse-c"></a>

要复制使用 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 加密的存储桶策略示例
<a name="example-bucket-policy-to-enforce-sse-c-encryption"></a>

如果要对写入 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
<a name="ssec-and-presignedurl"></a>

您可以生成可用于上传新对象、检索现有对象或检索对象元数据等操作的预签名 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 下载和上传对象](using-presigned-url.md)。

## 使用 SSE-C 发出请求
<a name="making-requests-with-sse-c"></a>

 在使用 REST API 创建对象时，您可以使用客户提供的密钥（SSE-C）指定服务器端加密。使用 SSE-C 时，必须使用[请求复制使用 SSE-C 加密的源对象时需要的 S3 API 标头](#s3-api-headers-required-for-requests-to-copy-source-objects-encrypted-with-sse-c)提供加密密钥信息。您可以使用 Amazon SDK 包装库将这些标头添加到您的请求中。如果需要，您可以直接在应用程序中调用 Amazon S3 REST API。

**重要**  
在指定具有客户提供密钥的服务器端加密（SSE-C）之前，请确保通用存储桶未阻止 SSE-C 加密。有关更多信息，请参阅 [对通用存储桶阻止或取消阻止 SSE-C](blocking-unblocking-s3-c-encryption-gpb.md)。

**注意**  
您不能使用 Amazon S3 控制台上传对象并请求 SSE-C，也不能使用控制台来更新使用 SSE-C 存储的现有对象（例如，更改存储类别或添加元数据）。有关更多信息，请参阅[SSE-C 对象加密和解密请求所需的 S3 API 标头](#s3-api-headers-required-for-sse-c-object-encryption-and-decryption-requests)。

## 使用 REST API
<a name="using-rest-api-sse-c"></a>

### 支持 SSE-C 的 Amazon S3 REST API
<a name="sse-c-supported-apis"></a>

以下 Amazon S3 API 支持使用客户提供的加密密钥进行服务器端加密 (SSE-C)。
+ **GET 操作** – 在使用 GET API 检索对象（请参阅 [GET Object](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectGET.html)）时，您可以指定请求标头。
+ **HEAD 操作** – 要使用 HEAD API 检索对象元数据（请参阅 [HEAD Object](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectHEAD.html)），可以指定这些请求标头。
+ **PUT 操作** – 使用 PUT Object API 上传数据（请参阅 [PUT Object](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectPUT.html)）时，可以指定这些请求标头。
+ **分段上传** – 在使用分段上传 API 上传大对象时，可以指定这些标头。您可以在以下请求中指定这些标头：启动请求（请参阅[启动分段上传](https://docs.amazonaws.cn/AmazonS3/latest/API/mpUploadInitiate.html)），以及每个后续分段上传请求（请参阅[上传分段](https://docs.amazonaws.cn/AmazonS3/latest/API/mpUploadUploadPart.html)或 [UploadPartCopy](https://docs.amazonaws.cn/AmazonS3/latest/API/mpUploadUploadPartCopy.html)）。对于每个分段上传请求，加密信息必须与您在启动分段上传请求中提供的信息相同。
+ **POST 操作** – 使用 POST 操作上传对象（请参阅 [POST 对象](https://docs.amazonaws.cn/AmazonS3/latest/API/RESTObjectPOST.html)）时，可在表单字段而不是请求标头中提供相同的信息。
+ **复制操作**：复制对象（请参阅 [CopyObject](https://docs.amazonaws.cn/AmazonS3/latest/API/API_CopyObject.html)）时，您同时具有源对象和目标对象：
  + 如果要指定目标对象的加密类型，您必须提供 `x-amz-server-side-encryption `请求标头。
  + 如果您希望使用 SSE-C 加密目标对象，则必须使用[SSE-C 对象加密和解密请求所需的 S3 API 标头](#s3-api-headers-required-for-sse-c-object-encryption-and-decryption-requests)提供加密信息。
  + 如果源对象使用 SSE-C 加密，则您必须使用[请求复制使用 SSE-C 加密的源对象时需要的 S3 API 标头](#s3-api-headers-required-for-requests-to-copy-source-objects-encrypted-with-sse-c)提供加密密钥信息。

## 使用 Amazon SDK 为 PUT、GET、Head 和 Copy 操作指定 SSE-C
<a name="sse-c-using-sdks"></a>

以下示例演示如何为对象请求使用客户提供的密钥的服务器端加密 (SSE-C)。这些示例执行以下操作。每个操作均演示了如何在请求中指定 SSE-C 相关标头：
+ **放置对象** – 上传对象，并请求使用客户提供的加密密钥的服务器端加密。
+ **获取对象** – 下载上一步中上传的对象。在请求中，应提供上传对象时提供的同一加密信息。Amazon S3 需要此信息来解密对象，以便将对象返回给您。
+ **获取对象元数据** – 检索对象的元数据。提供创建对象时使用的同一加密信息。
+ **复制对象** – 复制之前上传的对象的副本。因为源对象是使用 SSE-C 存储的，因此必须在复制请求中提供其加密信息。默认情况下，仅当您显式请求加密时，Amazon S3 才会为对象的副本加密。此示例指示 Amazon S3 存储对象的加密副本。

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

**注意**  
本示例显示如何在单个操作中上传对象。当使用分段上传 API 上传大型对象时，应按照此示例中所示的方式提供加密信息。有关使用 适用于 Java 的 Amazon SDK 的分段上传的示例，请参阅 [使用分段上传操作上传对象](mpu-upload-object.md)。

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

有关创建和测试有效示例的说明，请参阅《适用于 Java 的 Amazon SDK 开发人员指南》中的[入门](https://docs.amazonaws.cn/sdk-for-java/v1/developer-guide/getting-started.html)。

**Example**  

```
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 上传大型对象的示例，请参阅 [使用分段上传操作上传对象](mpu-upload-object.md) 和 [使用 Amazon SDK（低级别 API）](mpu-upload-object.md#mpu-upload-low-level)。

有关设置和运行代码示例的信息，请参阅《适用于 .NET 的 Amazon SDK 开发人员指南》**中的[适用于 .NET 的 Amazon SDK 入门](https://docs.amazonaws.cn/sdk-for-net/latest/developer-guide/net-dg-setup.html)。

**Example**  

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

------

## 使用 Amazon SDK 为分段上传指定 SSE-C
<a name="sse-c-using-sdks-multipart-uploads"></a>

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

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

要上传大型对象，您可以使用分段上传 API。有关更多信息，请参阅 [在 Amazon S3 中使用分段上传来上传和复制对象](mpuoverview.md)。可以使用高级或低级 API 上传大型对象。这些 API 支持在请求中使用与加密相关的标头。
+ 使用高级别 `TransferManager` API 时，应在 `PutObjectRequest` 中提供特定于加密的标头。有关更多信息，请参阅 [使用分段上传操作上传对象](mpu-upload-object.md)。
+ 当使用低级别 API 时，应提供 `InitiateMultipartUploadRequest` 中的加密相关信息，后跟每个 `UploadPartRequest` 中的相同加密信息。不需要在 `CompleteMultipartUploadRequest` 中提供任何特定于加密的标头。有关示例，请参阅 [使用 Amazon SDK（低级别 API）](mpu-upload-object.md#mpu-upload-low-level)。

以下示例使用 `TransferManager` 创建对象并显示如何提供 SSE-C 相关信息。本示例执行以下操作：
+ 使用 `TransferManager.upload()` 方法创建对象。在 `PutObjectRequest` 实例中，应在请求中提供加密密钥信息。Amazon S3 将使用客户提供的密钥对于对象进行加密。
+ 通过调用 `TransferManager.copy()` 方法创建对象的副本。该示例指示 Amazon S3 使用新的 `SSECustomerKey` 对对象副本进行加密。由于源对象使用 SSE-C 加密，因此 `CopyObjectRequest` 还提供了源对象的加密密钥，以便 Amazon S3 可以在复制对象之前解密对象。

**Example**  

```
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 中使用分段上传来上传和复制对象](mpuoverview.md)）。Amazon适用于 .NET 的 SDK 提供了高级或低级 API 来上传大型对象。这些 API 支持在请求中使用与加密相关的标头。
+ 当使用高级 `Transfer-Utility ` API 时，可以在 `TransferUtilityUploadRequest` 中提供特定于加密的标头，如下所示。有关代码示例，请参阅 [使用分段上传操作上传对象](mpu-upload-object.md)。

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

  下面是一个低级分段上传示例，该示例复制一个现有大型对象。在本示例中，要复制的对象使用 SSE-C 存储在 Amazon S3 中，并且您希望使用 SSE-C 保存目标对象。在本例中，您可以执行以下操作：
  + 通过提供加密密钥和相关信息启动分段上传请求。
  + 在 `CopyPartRequest` 中提供源和目标对象加密密钥及相关信息。
  + 通过检索对象元数据获取要复制的源对象的大小。
  + 以 5 MB 大小的分段上传对象。  
**Example**  

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

------