고객 제공 키를 사용한 서버 측 암호화(SSE-C) 지정하기
고객 제공 키를 사용한 서버 측 암호화(SSE-C) 지정하기 (Specifying server-side encryption with customer-provided keys (SSE-C))
SSE-C가 활성화된 버킷에서는 업로드 요청에 필요한 헤더를 전달해 SSE-C를 사용할 수 있어요. Amazon S3는 제공한 암호화 키로 AES-256 암호화를 적용해요.
출처: 문서
본문
고객 제공 키를 사용한 서버 측 암호화(SSE-C)는 새 범용 버킷에서 기본적으로 비활성화되어 있어요. SSE-C로 객체를 업로드하기 전에 버킷의 기본 암호화 구성에서 BlockedEncryptionTypes를 NONE으로 설정해 활성화해야 해요. 자세한 내용은 범용 버킷에 대한 SSE-C 차단 또는 차단 해제를 참고하세요.
버킷에 SSE-C가 활성화되면 필요한 헤더를 전달해 업로드 요청에서 SSE-C를 사용할 수 있어요. SSE-C로 데이터 쓰기를 지원하는 Amazon S3 작업을 참조하고 SSE-C 객체 암호화 및 복호화 요청에 필요한 S3 API 헤더를 포함해야 해요.
SSE-C를 지정해 객체를 업로드하면 Amazon S3는 제공한 암호화 키로 데이터에 AES-256 암호화를 적용해요. 그런 다음 Amazon S3는 암호화 키를 메모리에서 제거해요. 객체를 검색할 때는 요청의 일부로 동일한 암호화 키를 제공해야 해요. Amazon S3는 먼저 제공한 암호화 키가 일치하는지 확인한 다음 객체 데이터를 반환하기 전에 객체를 복호화해요.
SSE-C를 사용하기 전에 SSE-C 사용 전 고려 사항을 검토했는지 확인하세요.
참고 Amazon S3는 제공한 암호화 키를 저장하지 않아요. 대신 암호화 키의 무작위 솔트 해시 기반 메시지 인증 코드(HMAC) 값을 저장해 향후 요청을 검증해요. 솔트 처리된 HMAC 값으로는 암호화 키의 값을 유도하거나 암호화된 객체의 내용을 복호화할 수 없어요. 즉, 암호화 키를 잃으면 객체를 잃는 것이에요.
관련 주제
- SSE-C 작업 및 필수 헤더
- SSE-C 암호화를 강제하는 예시 버킷 정책
- 사전 서명된 URL과 SSE-C
- SSE-C로 요청하기
- REST API 사용
- AWS SDK를 사용해 PUT, GET, Head 및 Copy 작업에 SSE-C 지정하기
- AWS SDK를 사용해 멀티파트 업로드에 SSE-C 지정하기
SSE-C 작업 및 필수 헤더
지원되는 S3 API에 SSE-C를 지정하려면 특정 요청 매개 변수를 전달해야 해요.
참고
PutBucketEncryptionAPI는 버킷의 기본 암호화 방법으로 SSE-C를 설정하는 것을 지원하지 않으며, 객체가 자동으로 SSE-C를 사용하지 않아요. SSE-C는 각 업로드 또는 다운로드 요청에서 암호화 키를 제공하는 객체 수준 암호화 방법이에요. Amazon S3는 요청 중에 이 키로 객체를 암호화하거나 복호화한 다음 키를 폐기해요.
그러나 BlockedEncryptionTypes 매개 변수를 통해 버킷에 SSE-C 업로드가 허용되는지 여부를 제어하는 데는 PutBucketEncryption을 사용해요. 기본적으로 새 버킷은 SSE-C가 차단되어 있으며, SSE-C 업로드를 허용하려면 BlockedEncryptionTypes를 NONE으로 설정해야 해요.
SSE-C로 데이터 쓰기를 지원하는 Amazon S3 작업
다음 API 작업 또는 작업을 사용해 범용 버킷에 객체를 쓸 때 고객 제공 키(SSE-C)를 사용한 서버 측 암호화를 요청할 수 있어요.
- CopyObject
- CreateMultipartUpload
- CompleteMultipartUpload
- POST Object
- PutObject
- UploadPart
- UploadPartCopy
참고 S3 복제는 SSE-C로 암호화된 객체를 지원해요. 암호화된 객체 복제에 대한 자세한 내용은 암호화된 객체 복제하기(SSE-S3, SSE-KMS, DSSE-KMS, SSE-C)를 참고하세요.
SSE-C 객체 암호화 및 복호화 요청에 필요한 S3 API 헤더
SSE-C로 객체를 암호화하거나 복호화하려면 다음 세 가지 API 헤더를 제공해야 해요.
x-amz-server-side-encryption-customer-algorithm이 헤더로 암호화 알고리즘을 지정해요. 헤더 값은AES256이어야 해요.x-amz-server-side-encryption-customer-key이 헤더로 데이터를 암호화하거나 복호화하는 데 Amazon S3가 사용할 256비트, base64 인코딩 암호화 키를 제공해요.x-amz-server-side-encryption-customer-key-MD5이 헤더로 RFC 1321에 따른 암호화 키의 base64 인코딩 128비트 MD5 다이제스트를 제공해요. Amazon S3는 이 헤더를 메시지 무결성 검사에 사용해 암호화 키가 오류 없이 전송되었는지 확인해요.
SSE-C로 암호화된 원본 객체 복사 요청에 필요한 S3 API 헤더
SSE-C로 암호화된 원본 객체를 복사하려면 다음 세 가지 API 헤더를 제공해야 해요.
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에 따른 암호화 키의 base64 인코딩 128비트 MD5 다이제스트를 제공해요.
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:::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
새 객체 업로드, 기존 객체 검색 또는 객체 메타데이터 검색과 같은 작업에 사용할 수 있는 사전 서명된 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를 사용할 때는 SSE-C로 암호화된 원본 객체 복사 요청에 필요한 S3 API 헤더를 사용해 암호화 키 정보를 제공해야 해요. AWS SDK 래퍼 라이브러리를 사용해 이러한 헤더를 요청에 추가할 수 있어요. 필요하다면 애플리케이션에서 Amazon S3 REST API를 직접 호출할 수도 있어요.
중요 SSE-C는 모든 새 버킷에서 기본적으로 비활성화되어 있어요. SSE-C로 객체를 업로드하기 전에 버킷의 기본 암호화 구성을 업데이트해 활성화해야 해요. 자세한 내용은 범용 버킷에 대한 SSE-C 차단 또는 차단 해제를 참고하세요.
참고 Amazon S3 콘솔로 객체를 업로드하고 SSE-C를 요청할 수 없어요. 또한 콘솔로 SSE-C를 사용해 저장된 기존 객체를 업데이트(예: 스토리지 클래스 변경 또는 메타데이터 추가)할 수도 없어요. 자세한 내용은 SSE-C 객체 암호화 및 복호화 요청에 필요한 S3 API 헤더를 참고하세요.
REST API 사용
SSE-C를 지원하는 Amazon S3 REST API
다음 Amazon S3 API는 고객 제공 암호화 키(SSE-C)를 사용한 서버 측 암호화를 지원해요.
- GET 작업 – GET API(참조: GET Object)로 객체를 검색할 때 요청 헤더를 지정할 수 있어요.
- HEAD 작업 – HEAD API(참조: HEAD Object)로 객체 메타데이터를 검색할 때 이러한 요청 헤더를 지정할 수 있어요.
- PUT 작업 – PUT Object API(참조: PUT Object)로 데이터를 업로드할 때 이러한 요청 헤더를 지정할 수 있어요.
- 멀티파트 업로드 – 멀티파트 업로드 API로 대용량 객체를 업로드할 때 이러한 헤더를 지정할 수 있어요. initiate 요청(참조: Initiate Multipart Upload)과 이후 각 파트 업로드 요청(참조: Upload Part 또는 UploadPartCopy)에서 헤더를 지정해요. 각 파트 업로드 요청에서 암호화 정보는 initiate 멀티파트 업로드 요청에서 제공한 것과 동일해야 해요.
- POST 작업 – POST 작업으로 객체를 업로드할 때(참조: POST Object) 요청 헤더 대신 양식 필드에 같은 정보를 제공해요.
- 복사 작업 – 객체를 복사할 때(참조: CopyObject) 원본 객체와 대상 객체가 모두 있어요. 대상 객체의 암호화 유형을 지정하려면
x-amz-server-side-encryption요청 헤더를 제공해야 해요. 대상 객체를 SSE-C로 암호화하려면 SSE-C 객체 암호화 및 복호화 요청에 필요한 S3 API 헤더를 사용해 암호화 정보를 제공해야 해요. 원본 객체가 SSE-C로 암호화되어 있다면 SSE-C로 암호화된 원본 객체 복사 요청에 필요한 S3 API 헤더를 사용해 암호화 키 정보를 제공해야 해요.
AWS SDK를 사용해 PUT, GET, Head 및 Copy 작업에 SSE-C 지정하기
다음 예제는 객체에 대해 고객 제공 키(SSE-C)를 사용한 서버 측 암호화를 요청하는 방법을 보여줘요. 예제는 다음 작업을 수행해요. 각 작업은 요청에서 SSE-C 관련 헤더를 지정하는 방법을 보여줘요.
-
Put object – 객체를 업로드하고 고객 제공 암호화 키를 사용한 서버 측 암호화를 요청해요.
-
Get object – 이전 단계에서 업로드한 객체를 다운로드해요. 요청에서 객체를 업로드할 때 제공한 것과 동일한 암호화 정보를 제공해요. Amazon S3는 이 정보로 객체를 복호화해 사용자에게 반환해야 해요.
-
Get object metadata – 객체의 메타데이터를 검색해요. 객체를 만들 때 사용한 것과 동일한 암호화 정보를 제공해요.
-
Copy object – 이전에 업로드한 객체의 복사본을 만들어요. 원본 객체가 SSE-C로 저장되어 있으므로 복사 요청에서 원본 객체의 암호화 정보를 제공해야 해요. 기본적으로 Amazon S3는 명시적으로 요청할 때만 객체의 복사본을 암호화해요. 이 예제는 Amazon S3가 객체의 암호화된 복사본을 저장하도록 지시해요.
-
Java:
참고 이 예제는 단일 작업으로 객체를 업로드하는 방법을 보여줘요. 멀티파트 업로드 API로 대용량 객체를 업로드할 때는 이 예제와 동일한 방식으로 암호화 정보를 제공해요. AWS SDK for Java를 사용하는 멀티파트 업로드 예제는 멀티파트 업로드를 사용한 객체 업로드를 참고하세요. 요청에
SSECustomerKey를 포함해 필요한 암호화 정보를 추가해요.SSECustomerKey클래스에 대한 자세한 내용은 REST API 섹션을 참고하세요. 작동 샘플 생성 및 테스트 지침은 AWS SDK for 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)를 참고하세요. 코드 예제 설정 및 실행에 대한 정보는 AWS SDK for .NET 개발자 안내서의 AWS SDK for .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"); } } 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); } 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 SDK를 사용해 멀티파트 업로드에 SSE-C 지정하기
앞선 섹션의 예제는 PUT, GET, Head 및 Copy 작업에서 고객 제공 키(SSE-C)를 사용한 서버 측 암호화를 요청하는 방법을 보여줘요. 이 섹션은 SSE-C를 지원하는 다른 Amazon S3 API를 설명해요.
-
Java: 대용량 객체를 업로드하려면 멀티파트 업로드 API를 사용할 수 있어요. 자세한 내용은 Amazon S3에서 멀티파트 업로드를 사용한 객체 업로드 및 복사를 참고하세요. 대용량 객체를 업로드하는 데 상위 수준 또는 하위 수준 API를 사용할 수 있어요. 이러한 API는 요청에서 암호화 관련 헤더를 지원해요. 상위 수준
TransferManagerAPI를 사용할 때는PutObjectRequest에서 암호화 관련 헤더를 제공해요. 자세한 내용은 멀티파트 업로드를 사용한 객체 업로드를 참고하세요. 하위 수준 API를 사용할 때는InitiateMultipartUploadRequest에서 암호화 관련 정보를 제공한 다음 각UploadPartRequest에서 동일한 암호화 정보를 제공해요.CompleteMultipartUploadRequest에는 암호화 관련 헤더를 제공할 필요가 없어요. 예제는 AWS SDK 사용하기(하위 수준 API)를 참고하세요. 다음 예제는TransferManager를 사용해 객체를 만들고 SSE-C 관련 정보를 제공하는 방법을 보여줘요. 예제는 다음을 수행해요.TransferManager.upload()메서드로 객체를 만들어요.PutObjectRequest인스턴스에서 요청에 암호화 키 정보를 제공해요. Amazon S3는 고객 제공 키로 객체를 암호화해요.TransferManager.copy()메서드를 호출해 객체의 복사본을 만들어요. 예제는 새SSECustomerKey로 객체 복사본을 암호화하도록 Amazon S3에 지시해요. 원본 객체가 SSE-C로 암호화되어 있으므로CopyObjectRequest도 원본 객체의 암호화 키를 제공해 Amazon S3가 복사하기 전에 객체를 복호화할 수 있게 해요.import com.amazonaws.AmazonServiceException; import com.amazonaws.SdkClientException; import com.amazonaws.auth.profile.ProfileCredentialsProvider; import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.CopyObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.SSECustomerKey; import com.amazonaws.services.s3.transfer.Copy; import com.amazonaws.services.s3.transfer.TransferManager; import com.amazonaws.services.s3.transfer.TransferManagerBuilder; import com.amazonaws.services.s3.transfer.Upload; import javax.crypto.KeyGenerator; import java.io.File; import java.security.SecureRandom; public class ServerSideEncryptionCopyObjectUsingHLwithSSEC { public static void main(String[] args) throws Exception { Regions clientRegion = Regions.DEFAULT_REGION; String bucketName = "*** Bucket name ***"; String fileToUpload = "*** File path ***"; String keyName = "*** New object key name ***"; String targetKeyName = "*** Key name for object copy ***"; try { AmazonS3 s3Client = AmazonS3ClientBuilder.standard() .withRegion(clientRegion) .withCredentials(new ProfileCredentialsProvider()) .build(); TransferManager tm = TransferManagerBuilder.standard() .withS3Client(s3Client) .build(); // Create an object from a file. PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, keyName, new File(fileToUpload)); // Create an encryption key. KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(256, new SecureRandom()); SSECustomerKey sseCustomerEncryptionKey = new SSECustomerKey(keyGenerator.generateKey()); // Upload the object. TransferManager uploads asynchronously, so this call // returns immediately. putObjectRequest.setSSECustomerKey(sseCustomerEncryptionKey); Upload upload = tm.upload(putObjectRequest); // Optionally, wait for the upload to finish before continuing. upload.waitForCompletion(); System.out.println("Object created."); // Copy the object and store the copy using SSE-C with a new key. CopyObjectRequest copyObjectRequest = new CopyObjectRequest(bucketName, keyName, bucketName, targetKeyName); SSECustomerKey sseTargetObjectEncryptionKey = new SSECustomerKey(keyGenerator.generateKey()); copyObjectRequest.setSourceSSECustomerKey(sseCustomerEncryptionKey); copyObjectRequest.setDestinationSSECustomerKey(sseTargetObjectEncryptionKey); // Copy the object. TransferManager copies asynchronously, so this call returns // immediately. Copy copy = tm.copy(copyObjectRequest); // Optionally, wait for the upload to finish before continuing. copy.waitForCompletion(); System.out.println("Copy complete."); } catch (AmazonServiceException e) { // The call was transmitted successfully, but Amazon S3 couldn't process // it, so it returned an error response. e.printStackTrace(); } catch (SdkClientException e) { // Amazon S3 couldn't be contacted for a response, or the client // couldn't parse the response from Amazon S3. e.printStackTrace(); } } } -
.NET: 대용량 객체를 업로드하려면 멀티파트 업로드 API를 사용할 수 있어요(Amazon S3에서 멀티파트 업로드를 사용한 객체 업로드 및 복사 참고). 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를 사용할 때는 initiate 멀티파트 업로드 요청에서 암호화 관련 정보를 제공한 다음 이후 업로드 파트 요청에서 동일한 암호화 정보를 제공해요. 완료 멀티파트 업로드 요청에는 암호화 관련 헤더를 제공할 필요가 없어요. 예제는 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 }; 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, 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, }; 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); } } } }
더 알아보기 (Learn more)
- 범용 버킷에 대한 SSE-C 차단 또는 차단 해제
- SSE-C 작업 및 필수 헤더
- SSE-C 암호화를 강제하는 예시 버킷 정책
- 사전 서명된 URL과 SSE-C
- SSE-C로 요청하기
- REST API 사용
- AWS SDK를 사용해 PUT, GET, Head 및 Copy 작업에 SSE-C 지정하기
- AWS SDK를 사용해 멀티파트 업로드에 SSE-C 지정하기
- 암호화된 객체 복제하기(SSE-S3, SSE-KMS, DSSE-KMS, SSE-C)
- 사전 서명된 URL로 객체 다운로드 및 업로드
- 멀티파트 업로드를 사용한 객체 업로드
- Amazon S3에서 멀티파트 업로드를 사용한 객체 업로드 및 복사
- 문서 규칙