

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用客戶提供金鑰 (SSC-C) 指定伺服器端加密
<a name="specifying-s3-c-encryption"></a>

對於新的一般用途儲存貯體，預設會停用使用客戶提供金鑰 (SSE-C) 的伺服器端加密。您必須先在儲存貯體的預設加密組態`NONE`中`BlockedEncryptionTypes`將 設定為 ，才能使用 SSE-C 上傳物件。如需詳細資訊，請參閱[封鎖或取消封鎖一般用途儲存貯體的 SSE-C](blocking-unblocking-s3-c-encryption-gpb.md)。

為儲存貯體啟用 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) Salt 值，以驗證未來的請求。HMAC Salt 值不可用來衍生加密金鑰的值，或用來對加密物件的內容進行解密。換句話說，如果您遺失加密金鑰，則會遺失物件。

**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)
+ [使用 AWS SDKs為 PUT、GET、頭部和複製操作指定 SSE-C](#sse-c-using-sdks)
+ [使用 AWS SDKs為分段上傳指定 SSE-C](#sse-c-using-sdks-multipart-uploads)

## SSE-C 動作和必要標頭
<a name="sse-c-actions-and-required-headers"></a>

在支援的 S3 APIs 上指定 SSE-C 需要傳遞特定請求參數。

**注意**  
`PutBucketEncryption` API 不支援將 SSE-C 設定為儲存貯體的預設加密方法，物件不會自動使用 SSE-C。 SSE-C 是一種物件層級加密方法，可讓您在每次上傳或下載請求時提供加密金鑰。Amazon S3 使用此金鑰在請求期間加密或解密物件，然後捨棄金鑰。  
不過，您可以使用 `PutBucketEncryption` 來控制是否允許透過 `BlockedEncryptionTypes` 參數上傳儲存貯體的 SSE-C。根據預設，新的儲存貯體會封鎖 SSE-C，您必須`BlockedEncryptionTypes``NONE`將 設定為 允許 SSE-C 上傳。

### 支援使用 SSE-C 寫入資料的 Amazon S3 動作
<a name="amazon-s3-actions-that-support-writing-data-with-sse-c"></a>

您可以使用下列 API 操作或動作，在將物件寫入一般用途儲存貯體時，使用客戶提供的金鑰 (SSE-C) 請求伺服器端加密：
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html)
+ [https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html](https://docs.aws.amazon.com/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>

您必須提供下列三個 API 標頭，以使用 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 使用此標頭來進行訊息完整性檢查，以確保加密金鑰傳輸無誤。

### 請求複製使用 SSE-C 加密的來源物件所需的 S3 API 標頭
<a name="s3-api-headers-required-for-requests-to-copy-source-objects-encrypted-with-sse-c"></a>

您必須提供下列三個 API 標頭，才能複製使用 SSE-C 加密的來源物件：
+ `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，則必須在所有分段上傳請求中包含 `x-amz-server-side-encryption-customer-algorithm` 標頭 (CreateMultipartUpload、UploadPart 和 CompleteMultipartUpload)。

## 已預先簽章的 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 標頭。因此，您只能以程式設計方式針對 SSE-C 物件使用預先簽章的 URL。

如需預先簽章的 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)。您可以使用 AWS 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 APIs
<a name="sse-c-supported-apis"></a>

下列 Amazon S3 API 支援伺服器端加密搭配客戶提供的加密金鑰 (SSE-C)。
+ **GET 操作** – 使用 GET API 擷取物件時 (請參閱 [GET 物件](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html))，您可以指定這些請求標頭。
+ **HEAD 操作** – 若要使用 HEAD API 擷取物件中繼資料 (請參閱 [HEAD 物件](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html))，您可以指定這些請求標頭。
+ **PUT 操作** – 使用 PUT API 上傳資料時 (請參閱 [PUT 物件](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html))，您可以指定這些請求標頭。
+ **分段上傳** – 使用分段上傳 API 上傳大型物件時，您可以指定這些標頭。您可以在啟動請求 （請參閱[啟動分段上傳](https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadInitiate.html)) 和每個後續分段上傳請求 （請參閱[上傳組件](https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPart.html)或 [UploadPartCopy](https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html)) 中指定這些標頭。每個部分上傳要求的加密資訊，必須與您在啟動分段上傳要求中所提供的加密資訊相同。
+ **POST 操作** – 使用 POST 操作上傳物件時 (請參閱 [POST 物件](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html))，請提供與表單欄位中相同的資訊，而不是請求標頭。
+ **複製操作** – 當您複製物件時 （請參閱 [CopyObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html))，您會同時擁有來源物件和目標物件：
  + 如果您想要指定目標物件的加密類型，您必須提供`x-amz-server-side-encryption `請求標頭。
  + 如果您想要使用 SSE-C 加密目標物件，您必須使用 S3 API 提供加密資訊[SSE-C 物件加密和解密請求所需的 S3 API 標頭](#s3-api-headers-required-for-sse-c-object-encryption-and-decryption-requests)。
  + 如果來源物件是使用 SSE-C 加密，您必須使用 S3 API 標頭 提供加密金鑰資訊[請求複製使用 SSE-C 加密的來源物件所需的 S3 API 標頭](#s3-api-headers-required-for-requests-to-copy-source-objects-encrypted-with-sse-c)。

## 使用 AWS SDKs為 PUT、GET、頭部和複製操作指定 SSE-C
<a name="sse-c-using-sdks"></a>

下列範例說明如何用客戶提供金鑰 (SSE-C)，要求為物件進行伺服器端加密。這些範例會執行下列操作。每個操作示範如何在要求中指定 SSE-C 相關標頭：
+ **放置物件** – 使用客戶提供的加密金鑰上傳物件並請求伺服器端加密。
+ **取得物件** – 下載前一個步驟中所上傳的物件。在此請求中，請您提供在您上傳物件時所提供的相同加密資訊。Amazon S3 需要此資訊來解密物件，才能將物件傳回給您。
+ **取得物件中繼資料** – 擷取物件的中繼資料。請您提供物件建立時，使用的加密資訊。
+ **複製物件** – 建立先前上傳物件的複本。因為來源物件是使用 SSE-C 所存放，所以您必須在複製要求中提供其加密資訊。根據預設，只有在您明確請求加密時，Amazon S3 才會加密物件的複本。此範例指示 Amazon S3 存放加密的物件複本。

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

**注意**  
此範例示範如何以單一操作上傳物件。使用分段上傳 API 上傳大型物件時，請您提供如此範例一樣的加密資訊。如需使用 的分段上傳範例 適用於 Java 的 AWS SDK，請參閱 [使用分段上傳來上傳物件](mpu-upload-object.md)。

新增要求加密資訊，您可包含 `SSECustomerKey` 在您的要求中。如需有關 `SSECustomerKey` 類別的詳細資訊，請參閱 REST API 一節。

如需建立和測試工作範例的說明，請參閱《 適用於 Java 的 AWS SDK 開發人員指南》中的[入門](https://docs.aws.amazon.com/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) 和 [使用 AWS SDKs（低階 API)](mpu-upload-object.md#mpu-upload-low-level)。

如需有關設定和執行程式碼範例的資訊，請參閱《[適用於 .NET 的 AWS SDK 開發人員指南》中的適用於 .NET 的 SDK 入門](https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/net-dg-setup.html)。 *AWS *

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

------

## 使用 AWS SDKs為分段上傳指定 SSE-C
<a name="sse-c-using-sdks-multipart-uploads"></a>

上節中的範例示範如何以 PUT、GET、Head 和 Copy 操作要求經由客戶提供加密金鑰的伺服器端加密 (SSE-C)。本節說明其他支援 SSE-C 的 Amazon S3 API。

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

若要上傳大型物件，您可以使用分段上傳 APIs。如需詳細資訊，請參閱[在 Amazon S3 中使用分段上傳來上傳和複製物件](mpuoverview.md)。您可以使用高階或低階 API 來上傳大型物件。這些 API 支援要求中的加密相關標頭。
+ 使用高階 `TransferManager` API 時，您可以在 中提供加密特定的標頭`PutObjectRequest`。如需詳細資訊，請參閱[使用分段上傳來上傳物件](mpu-upload-object.md)。
+ 使用低階 API 時，請您在 `InitiateMultipartUploadRequest` 提供加密關聯資訊，遵照 `UploadPartRequest` 中每一個的相同加密資訊。您不需要在 `CompleteMultipartUploadRequest` 提供任何加密特定標頭。如需範例，請參閱 [使用 AWS SDKs（低階 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))。 AWS SDK for .NET 提供高階或低階 APIs 來上傳大型物件。這些 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 時，請提供您在啟動分段上傳要求中的加密相關資訊，後面接著後續分段上傳要求中的相同加密資訊。您不需要在完整的分段上傳要求中提供任何加密特定標頭。如需範例，請參閱 [使用 AWS SDKs（低階 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);
              }
          }
      }
  }
  ```

------