Skip to content

Commit 84bb7fd

Browse files
committed
Turning off trailing checksums for SSE
1 parent 3bd2865 commit 84bb7fd

File tree

6 files changed

+388
-41
lines changed

6 files changed

+388
-41
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package software.amazon.awssdk.services.s3;
16+
17+
import static org.assertj.core.api.Fail.fail;
18+
import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AES256;
19+
import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName;
20+
21+
import java.io.File;
22+
import java.io.IOException;
23+
import java.security.SecureRandom;
24+
import java.util.Base64;
25+
import java.util.UUID;
26+
import javax.crypto.KeyGenerator;
27+
import org.junit.AfterClass;
28+
import org.junit.BeforeClass;
29+
import org.junit.Test;
30+
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
31+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
32+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
33+
import software.amazon.awssdk.services.s3.model.ServerSideEncryption;
34+
import software.amazon.awssdk.testutils.RandomTempFile;
35+
import software.amazon.awssdk.utils.Md5Utils;
36+
37+
public class AsyncServerSideEncryptionIntegrationTest extends S3IntegrationTestBase {
38+
39+
private static final String BUCKET = temporaryBucketName(GetObjectIntegrationTest.class);
40+
41+
private static File file;
42+
43+
@BeforeClass
44+
public static void setupFixture() throws IOException {
45+
createBucket(BUCKET);
46+
file = new RandomTempFile(10_000);
47+
}
48+
49+
@AfterClass
50+
public static void tearDownFixture() {
51+
deleteBucketAndAllContents(BUCKET);
52+
file.delete();
53+
}
54+
55+
@Test
56+
public void sse_AES256_succeeds() {
57+
String key = UUID.randomUUID().toString();
58+
PutObjectRequest request = PutObjectRequest.builder()
59+
.key(key)
60+
.bucket(BUCKET)
61+
.serverSideEncryption(AES256)
62+
.build();
63+
64+
s3Async.putObject(request, file.toPath()).join();
65+
66+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
67+
.key(key)
68+
.bucket(BUCKET)
69+
.build();
70+
71+
s3Async.getObject(getObjectRequest, AsyncResponseTransformer.toBytes()).join();
72+
}
73+
74+
@Test
75+
public void sse_AWSKMS_succeeds() {
76+
String key = UUID.randomUUID().toString();
77+
PutObjectRequest request = PutObjectRequest.builder()
78+
.key(key)
79+
.bucket(BUCKET)
80+
.serverSideEncryption(ServerSideEncryption.AWS_KMS)
81+
.build();
82+
83+
s3Async.putObject(request, file.toPath()).join();
84+
85+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
86+
.key(key)
87+
.bucket(BUCKET)
88+
.build();
89+
90+
s3Async.getObject(getObjectRequest, AsyncResponseTransformer.toBytes()).join();
91+
}
92+
93+
@Test
94+
public void sse_customerManaged_succeeds() {
95+
String key = UUID.randomUUID().toString();
96+
byte[] secretKey = generateSecretKey();
97+
String b64Key = Base64.getEncoder().encodeToString(secretKey);
98+
String b64KeyMd5 = Md5Utils.md5AsBase64(secretKey);
99+
100+
PutObjectRequest request = PutObjectRequest.builder()
101+
.key(key)
102+
.bucket(BUCKET)
103+
.sseCustomerKey(b64Key)
104+
.sseCustomerAlgorithm(AES256.name())
105+
.sseCustomerKeyMD5(b64KeyMd5)
106+
.build();
107+
108+
s3Async.putObject(request, file.toPath()).join();
109+
110+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
111+
.key(key)
112+
.bucket(BUCKET)
113+
.sseCustomerKey(b64Key)
114+
.sseCustomerAlgorithm(AES256.name())
115+
.sseCustomerKeyMD5(b64KeyMd5)
116+
.build();
117+
118+
s3Async.getObject(getObjectRequest, AsyncResponseTransformer.toBytes()).join();
119+
}
120+
121+
private static byte[] generateSecretKey() {
122+
KeyGenerator generator;
123+
try {
124+
generator = KeyGenerator.getInstance("AES");
125+
generator.init(256, new SecureRandom());
126+
return generator.generateKey().getEncoded();
127+
} catch (Exception e) {
128+
fail("Unable to generate symmetric key: " + e.getMessage());
129+
return null;
130+
}
131+
}
132+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package software.amazon.awssdk.services.s3;
16+
17+
import static org.assertj.core.api.Fail.fail;
18+
import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AES256;
19+
import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName;
20+
21+
import java.io.File;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.security.SecureRandom;
25+
import java.util.Base64;
26+
import java.util.UUID;
27+
import javax.crypto.KeyGenerator;
28+
import org.junit.AfterClass;
29+
import org.junit.BeforeClass;
30+
import org.junit.Test;
31+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
32+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
33+
import software.amazon.awssdk.services.s3.model.ServerSideEncryption;
34+
import software.amazon.awssdk.testutils.RandomTempFile;
35+
import software.amazon.awssdk.utils.IoUtils;
36+
import software.amazon.awssdk.utils.Md5Utils;
37+
38+
public class SyncServerSideEncryptionIntegrationTest extends S3IntegrationTestBase {
39+
40+
private static final String BUCKET = temporaryBucketName(GetObjectIntegrationTest.class);
41+
42+
private static File file;
43+
44+
@BeforeClass
45+
public static void setupFixture() throws IOException {
46+
createBucket(BUCKET);
47+
file = new RandomTempFile(10_000);
48+
}
49+
50+
@AfterClass
51+
public static void tearDownFixture() {
52+
deleteBucketAndAllContents(BUCKET);
53+
file.delete();
54+
}
55+
56+
@Test
57+
public void sse_AES256_succeeds() throws Exception {
58+
String key = UUID.randomUUID().toString();
59+
PutObjectRequest request = PutObjectRequest.builder()
60+
.key(key)
61+
.bucket(BUCKET)
62+
.serverSideEncryption(AES256)
63+
.build();
64+
65+
s3.putObject(request, file.toPath());
66+
67+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
68+
.key(key)
69+
.bucket(BUCKET)
70+
.build();
71+
72+
InputStream response = s3.getObject(getObjectRequest);
73+
IoUtils.drainInputStream(response);
74+
response.close();
75+
}
76+
77+
@Test
78+
public void sse_AWSKMS_succeeds() throws Exception {
79+
String key = UUID.randomUUID().toString();
80+
PutObjectRequest request = PutObjectRequest.builder()
81+
.key(key)
82+
.bucket(BUCKET)
83+
.serverSideEncryption(ServerSideEncryption.AWS_KMS)
84+
.build();
85+
86+
s3.putObject(request, file.toPath());
87+
88+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
89+
.key(key)
90+
.bucket(BUCKET)
91+
.build();
92+
93+
InputStream response = s3.getObject(getObjectRequest);
94+
IoUtils.drainInputStream(response);
95+
response.close();
96+
}
97+
98+
@Test
99+
public void sse_customerManaged_succeeds() throws Exception {
100+
String key = UUID.randomUUID().toString();
101+
byte[] secretKey = generateSecretKey();
102+
String b64Key = Base64.getEncoder().encodeToString(secretKey);
103+
String b64KeyMd5 = Md5Utils.md5AsBase64(secretKey);
104+
105+
PutObjectRequest request = PutObjectRequest.builder()
106+
.key(key)
107+
.bucket(BUCKET)
108+
.sseCustomerKey(b64Key)
109+
.sseCustomerAlgorithm(AES256.name())
110+
.sseCustomerKeyMD5(b64KeyMd5)
111+
.build();
112+
113+
s3.putObject(request, file.toPath());
114+
115+
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
116+
.key(key)
117+
.bucket(BUCKET)
118+
.sseCustomerKey(b64Key)
119+
.sseCustomerAlgorithm(AES256.name())
120+
.sseCustomerKeyMD5(b64KeyMd5)
121+
.build();
122+
123+
InputStream response = s3.getObject(getObjectRequest);
124+
IoUtils.drainInputStream(response);
125+
response.close();
126+
}
127+
128+
private static byte[] generateSecretKey() {
129+
KeyGenerator generator;
130+
try {
131+
generator = KeyGenerator.getInstance("AES");
132+
generator.init(256, new SecureRandom());
133+
return generator.generateKey().getEncoded();
134+
} catch (Exception e) {
135+
fail("Unable to generate symmetric key: " + e.getMessage());
136+
return null;
137+
}
138+
}
139+
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumConstant.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ public final class ChecksumConstant {
4040
*/
4141
public static final String ENABLE_MD5_CHECKSUM_HEADER_VALUE = "append-md5";
4242

43+
/**
44+
* Header value for specifying server side encryption.
45+
*/
46+
public static final String SERVER_SIDE_ENCRYPTION_HEADER = "x-amz-server-side-encryption";
47+
48+
/**
49+
* Header value for specifying server side encryption with a customer managed key.
50+
*/
51+
public static final String SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER = "x-amz-server-side-encryption-customer-algorithm";
52+
4353
/**
4454
* Length of an MD5 checksum in bytes.
4555
*/
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.checksums;
17+
18+
import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER;
19+
import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.SERVER_SIDE_ENCRYPTION_HEADER;
20+
import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AWS_KMS;
21+
22+
import java.util.Collections;
23+
import java.util.List;
24+
import java.util.Map;
25+
import software.amazon.awssdk.annotations.SdkInternalApi;
26+
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
27+
import software.amazon.awssdk.core.ClientType;
28+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
29+
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
30+
import software.amazon.awssdk.services.s3.S3Configuration;
31+
import software.amazon.awssdk.services.s3.internal.handlers.AsyncChecksumValidationInterceptor;
32+
import software.amazon.awssdk.services.s3.internal.handlers.SyncChecksumValidationInterceptor;
33+
34+
/**
35+
* Class used by {@link SyncChecksumValidationInterceptor} and
36+
* {@link AsyncChecksumValidationInterceptor} to determine if trailing checksums
37+
* should be enabled for a given request.
38+
*/
39+
@SdkInternalApi
40+
public final class ChecksumsEnabledValidator {
41+
42+
private ChecksumsEnabledValidator() {}
43+
44+
/**
45+
* Validates that trailing checksums should be enabled based on {@link ClientType} and the presence
46+
* or S3 specific headers.
47+
*
48+
* @param expectedClientType - The expected client type for enabling checksums
49+
* @param executionAttributes - {@link ExecutionAttributes} to determine the actual client type
50+
* @param headers A map of headers for a given request
51+
* @return If trailing checksums should be enabled for this request.
52+
*/
53+
public static boolean trailingChecksumsEnabled(ClientType expectedClientType,
54+
ExecutionAttributes executionAttributes,
55+
Map<String, List<String>> headers) {
56+
57+
// S3 doesn't support trailing checksums for customer encryption
58+
if (headers.containsKey(SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER)) {
59+
return false;
60+
}
61+
62+
// S3 doesn't support trailing checksums for KMS encrypted objects
63+
if (headers.getOrDefault(SERVER_SIDE_ENCRYPTION_HEADER, Collections.emptyList()).contains(AWS_KMS.toString())) {
64+
return false;
65+
}
66+
67+
ClientType actualClientType = executionAttributes.getAttribute(SdkExecutionAttribute.CLIENT_TYPE);
68+
69+
// Only validate checksums if the actual client type is the expected client type
70+
if (expectedClientType.equals(actualClientType)) {
71+
S3Configuration serviceConfiguration =
72+
(S3Configuration) executionAttributes.getAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG);
73+
74+
return serviceConfiguration == null || serviceConfiguration.checksumValidationEnabled();
75+
}
76+
77+
return false;
78+
}
79+
}

0 commit comments

Comments
 (0)