搭配客戶提供的金鑰 (SSE-C) 使用伺服器端加密 - Amazon Simple Storage Service

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

搭配客戶提供的金鑰 (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) Salt 值,以驗證未來的請求。HMAC Salt 值不可用來衍生加密金鑰的值,或用來對加密物件的內容進行解密。換句話說,如果您遺失加密金鑰,則會遺失物件。

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-KMS 的 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" } } } ] }
重要

如果您使用儲存貯體政策要求開啟 SSE-Cs3:PutObject,則必須在所有分段上傳要求 (CreateMultipartUpload UploadPart、和) 中包含x-amz-server-side-encryption-customer-algorithm標頭。CompleteMultipartUpload

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

如需預先簽章的 URL 詳細資訊,請參閱使用預先簽章的 URL

使用客戶提供金鑰 (SSC-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 Digest 的加密金鑰。Amazon S3 使用此標頭來進行訊息完整性檢查,以確保加密金鑰傳輸無誤。

您可以使用 AWS 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 物件),您可以指定這些請求標頭。

  • HEAD 操作 – 若要使用 HEAD API 擷取物件中繼資料 (請參閱 HEAD 物件),您可以指定這些請求標頭。

  • PUT 操作 – 使用 PUT API 上傳資料時 (請參閱 PUT 物件),您可以指定這些請求標頭。

  • 分段上傳 – 使用分段上傳 API 上傳大型物件時,您可以指定這些標頭。您可以在啟動請求 (請參閱啟動分段上傳) 及每個後續片段上傳請求 (請參閱上傳片段上傳片段 - 複製) 中指定這些標頭。每個部分上傳要求的加密資訊,必須與您在啟動分段上傳要求中所提供的加密資訊相同。

  • POST 操作 – 使用 POST 操作上傳物件時 (請參閱 POST 物件),請提供與表單欄位中相同的資訊,而不是請求標頭。

  • 複製操作 – 當您複製物件時 (請參閱 PUT 物件 - 複製),您要同時有來源物件與目標物件。

    • 如果您想要使用具有 AWS 受管理金鑰的伺服器端加密來加密目標物件,則必須提供要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 上傳大型物件時,請您提供如此範例一樣的加密資訊。如需使用分段上傳的範例 AWS 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 大型物件的範例,請參閱 使用分段上傳來上傳物件使用 AWS 軟體開發套件 (低階 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 提供任何加密特定標頭。如需範例,請參閱 使用 AWS 軟體開發套件 (低階 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 (請參閱使用分段上傳來上傳和複製物件)。 AWS SDK for .NET 提供高階或低階 API 來上傳大型物件。這些 API 支援要求中的加密相關標頭。

  • 使用高階 Transfer-Utility API 時,您在 TransferUtilityUploadRequest 所提供的加密特定標頭,如下所示。如需程式碼範例,請參閱「使用分段上傳來上傳物件」。

    TransferUtilityUploadRequest request = new TransferUtilityUploadRequest() { FilePath = filePath, BucketName = existingBucketName, Key = keyName, // Provide encryption information. ServerSideEncryptionCustomerMethod = ServerSideEncryptionCustomerMethod.AES256, ServerSideEncryptionCustomerProvidedKey = base64Key, };
  • 使用低階 API 時,請提供您在啟動分段上傳要求中的加密相關資訊,後面接著後續分段上傳要求中的相同加密資訊。您不需要在完整的分段上傳要求中提供任何加密特定標頭。如需範例,請參閱 使用 AWS 軟體開發套件 (低階 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); } } } }