고객 제공 키(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) 값을 저장하여 향후 요청을 검증합니다. 솔트 HMAC 값은 암호화 키의 값을 파생하거나 암호화된 객체의 콘텐츠를 해독하기 위해 사용할 수 없습니다. 즉, 암호화 키가 없어지면 객체도 없어집니다.

S3 복제는 SSE-C로 암호화된 객체를 지원합니다. 암호화된 객체 복제에 대한 자세한 내용은 서버 측 암호화(SSE-C, SSE-S3, SSE-KMS)를 사용하여 생성된 객체 복제 섹션을 참조하세요.

SSE-C에 대한 자세한 내용은 다음 주제를 참조하세요.

SSE-C 개요

이 섹션에서는 SSE-C의 개요를 다룹니다. SSE-C를 사용할 때는 다음 사항을 명심하세요.

  • HTTPS를 사용해야 합니다.

    중요

    Amazon S3는 SSE-C를 사용할 때 HTTP를 통해 전송된 모든 요청을 거부합니다. 보안상의 이유로 인해, 보안에 취약할 수 있는 HTTP를 통해 키를 보내면 오류가 발생할 수 있음을 유의하세요. 키를 취소하고 적절하게 교체합니다.

  • 응답의 엔터티 태그(ETag)는 객체 데이터의 MD5 해시가 아닙니다.

  • 암호화 키가 사용되는 매핑을 관리하여 객체를 암호화합니다. Amazon S3는 암호화 키를 저장하지 않습니다. 객체에 대해 제공한 암호화 키는 직접 추적해야 합니다.

    • 버전 관리가 활성화된 버킷의 경우, 이 기능을 사용하여 업로드 하는 각 객체의 버전에는 자체 암호화 키가 있습니다. 어떤 객체 버전에 어떤 암호화 키가 사용되었는지는 직접 추적해야 합니다.

    • 암호화 키는 클라이언트 측에서 관리하기 때문에 클라이언트 측에서 키 교체와 같은 추가적인 보안 조치를 관리합니다.

    주의

    암호화 키가 없어지면 암호화 키를 사용하지 않은 객체에 대한 모든 GET 요청이 실패하고 객체도 잃게 됩니다.

SSE-C 요구 및 제한

특정 Amazon S3 버킷의 모든 객체에 대해 SSE-C를 요구하려면 정책을 사용하면 됩니다.

예를 들어 다음 버킷 정책은 SSE-C를 요청하는 x-amz-server-side-encryption-customer-algorithm 헤더가 포함되지 않은 모든 요청에 대해 객체 업로드(s3:PutObject) 권한을 거부합니다.

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

특정 Amazon S3 버킷의 모든 객체에 대한 서버 측 암호화를 제한하려는 경우에도 정책을 사용할 수 있습니다. 예를 들어, 다음 버킷 정책은 요청에 SSE-C를 요청하는 x-amz-server-side-encryption-customer-algorithm 헤더가 포함되는 경우 모든 사용자에게 객체 업로드(s3:PutObject) 권한을 거부합니다.

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

미리 서명된 URL 및 SSE-C

새로운 객체 업로드, 기존 객체 또는 객체 메타데이터 검색과 같은 작업에 사용되는 미리 서명된 URL을 생성할 수 있습니다. 미리 서명된 URL은 다음과 같이 SSE-C를 지원합니다.

  • 미리 서명된 URL을 만들 때 서명 계산에 x-amz-server-side​-encryption​-customer-algorithm 헤더를 사용하여 알고리즘을 지정해야 합니다.

  • 새로운 객체 업로드, 기존 객체 또는 객체 메타데이터 전용 검색에 미리 서명된 URL을 사용하는 경우, 클라이언트 애플리케이션의 요청에서 모든 암호화 헤더를 제공해야 합니다.

    참고

    비 SSE-C 객체의 경우, 데이터에 액세스하기 위해 미리 서명된 URL을 생성하여 브라우저에 직접 붙여 넣을 수 있습니다.

    하지만 SSE-C 객체에는 이렇게 할 수 없습니다. 미리 서명된 URL 이외에 SSE-C 객체에 고유한 HTTP 헤더도 포함해야 하기 때문입니다. 따라서 프로그래밍 방식으로만 SSE-C 객체에 미리 서명된 URL을 생성할 수 있습니다.

미리 서명된 URL에 대한 자세한 내용은 미리 서명된 URL 사용 단원을 참조하십시오.

고객 제공 키를 사용한 서버 측 암호화 지정(SSE-C).

REST API를 사용하여 객체를 생성할 때 고객 제공 키(SSE-C)를 사용하여 서버 측 암호화를 지정할 수 있습니다. SSE-C를 사용하는 경우 다음 요청 헤더를 사용하여 암호화 키 정보를 제공해야 합니다.

이름 설명
x-amz-server-side​-encryption​-customer-algorithm

헤더를 사용하여 암호화 알고리즘을 지정합니다. 헤더 값은 AES256이어야 합니다.

x-amz-server-side​-encryption​-customer-key

이 헤더를 사용하여 256비트의 base64 인코딩 암호화 키를 Amazon S3에 제공하여 데이터를 암호화하거나 암호 해독합니다.

x-amz-server-side​-encryption​-customer-key-MD5

이 헤더를 사용하여 RFC 1321에 따라 암호화 키의 128비트 base64 인코딩 MD5 다이제스트를 제공합니다. 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(GetObject 참조)를 사용하여 객체를 검색할 때 요청 헤더를 지정할 수 있습니다.

  • HEAD 작업 - HEAD API(HeadObject 참조)를 사용하여 객체 메타데이터를 검색하려면 이러한 요청 헤더를 지정하면 됩니다.

  • PUT 작업 - PUT Object API(PutObject 참조)를 사용하여 데이터를 업로드할 때 이러한 요청 헤더를 지정할 수 있습니다.

  • 멀티파트 업로드 - 멀티파트 업로드 API를 사용하여 대형 객체를 업로드할 때 이 헤더를 지정할 수 있습니다. 시작 요청에서 이 헤더를 지정하고(멀티파트 업로드 시작에 관한 문서 참조) 각각의 후속 부분은 요청을 업로드합니다(UploadPart 또는 CopyObject 참조). 각 파트 업로드 요청에서 암호화 정보는 시작 멀티파트 업로드 요청의 시작에서 제공한 내용과 동일해야 합니다.

  • POST 작업 - POST 작업(객체 POST에 관한 문서 참조)을 사용하여 객체를 업로드할 경우에는 요청 헤더 대신 양식 필드에 동일한 정보를 제공합니다.

  • Copy 작업 - 객체를 복사(CopyObject 참조)하려면 원본 객체 및 대상 객체가 있어야 합니다.

    • AWS 관리형 키로 서버 측 암호를 사용하여 대상 객체를 암호화하려는 경우, x-amz-server-side​-encryption 요청 헤더를 제공해야 합니다.

    • SSE-C를 사용하여 대상 객체를 암호화하려는 경우, 앞의 표에서 설명한 3개의 헤더를 사용하여 암호화 정보를 제공해야 합니다.

    • 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

      이 헤더를 포함하여 Amazon S3가 원본 객체의 암호화를 해독하는 데 사용할 base64 인코딩 암호화 키를 제공합니다. 이 암호화 키는 원본 객체를 만들 때 Amazon S3에 제공한 것이어야 하며, 그렇지 않으면 Amazon S3가 객체의 암호를 해독할 수 없습니다.

      x-amz-copy-source-​server-side​-encryption​-customer-key-MD5

      이 헤더를 포함하여 RFC 1321에 따라 암호화 키의 128비트 base64 인코딩 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 SDK 사용(하위 수준 API) 단원을 참조하십시오.

SSE-C에 대한 자세한 내용은 고객 제공 키(SSE-C)로 서버 측 암호화 사용 단원을 참조하십시오. 실제 예제를 작성하여 테스트하는 방법에 대한 자세한 내용은Amazon S3 .NET 코드 예제 실행 단원을 참조하십시오.

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

앞 섹션의 예제에서는 PUT, GET, Head 및 Copy 작업에서 고객 제공 키(SSE-C)를 사용하는 서버 측 암호화를 요청하는 방법을 알아보았으며, 이 섹션에서는 SSE-C를 지원하는 기타 Amazon S3 API에 대해 소개합니다.

Java

멀티파트 업로드 API를 사용하여 대형 객체를 업로드할 수 있습니다(멀티파트 업로드를 사용한 객체 업로드 및 복사 참조). 상위 수준 또는 하위 수준의 API를 사용하여 대형 객체를 업로드할 수 있습니다. 이들 API는 요청의 암호화 관련 헤더를 지원합니다.

  • 고급 TransferManager API를 사용할 때는 PutObjectRequest에 암호화 관련 헤더를 제공합니다(멀티파트 업로드를 사용한 객체 업로드 단원 참조).

  • 하위 수준 API를 사용할 경우 InitiateMultipartUploadRequest에 암호화 관련 정보를 제공하고 각 UploadPartRequest에 동일한 암호화 정보를 제공합니다. CompleteMultipartUploadRequest에서는 암호화 관련 헤더를 제공할 필요가 없습니다. 예를 보려면 AWS SDK 사용(하위 수준 API) 섹션을 참조하세요.

다음 예제에서는 TransferManager를 사용하여 객체를 만들고 SSE-C 관련 정보를 제공하는 방법을 보여 줍니다. 이 예제는 다음을 수행합니다.

  • TransferManager.upload() 메서드를 사용하여 객체를 생성합니다. PutObjectRequest 인스턴스에서 요청할 암호화 키 정보를 제공합니다. Amazon S3에서는 고객이 제공하는 키를 사용하여 객체를 암호화합니다.

  • TransferManager.copy() 메서드를 호출하여 객체의 복사본을 생성합니다. 이 예제에서는 Amazon S3에 새 SSECustomerKey를 사용하여 객체 복사본을 암호화하도록 지시합니다. 원본 객체가 SSE-C를 사용하여 암호화되기 때문에 Amazon S3가 복사 전에 객체의 암호를 해독할 수 있도록 CopyObjectRequest 실행 시 원본 객체의 암호화 키도 제공됩니다.

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 SDK 사용(하위 수준 API) 섹션을 참조하세요.

    다음은 기존 대형 객체의 복사본을 만드는 하위 수준의 멀티파트 업로드 예제입니다. 이 예제에서 객체는 SSE-C를 사용하여 Amazon S3에 저장되고, 대상 객체 또한 SSE-C를 사용하여 저장됩니다. 예제에서는 다음을 수행합니다.

    • 암호화 키와 관련 정보를 제공하여 멀티파트 업로드 요청을 시작합니다.

    • CopyPartRequest에 원본 및 대상 객체 암호화 키와 관련 정보를 제공합니다.

    • 객체 메타데이터를 검색하여 복사할 원본 객체의 크기를 확인합니다.

    • 5MB 단위로 객체를 업로드합니다.

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