Support V4 Signed URLs

Fixes #160.  Fixes #164.  Fixes #204.
pull/207/head
Andrew Gaul 2017-02-21 16:31:11 -08:00
rodzic 8b2d056d40
commit 014f558c51
2 zmienionych plików z 62 dodań i 17 usunięć

Wyświetl plik

@ -181,7 +181,13 @@ public class S3ProxyHandler {
"response-expires", "response-expires",
"Signature", "Signature",
"uploadId", "uploadId",
"uploads" "uploads",
"X-Amz-Algorithm",
"X-Amz-Credential",
"X-Amz-Date",
"X-Amz-Expires",
"X-Amz-Signature",
"X-Amz-SignedHeaders"
); );
/** All supported x-amz- headers, except for x-amz-meta- user metadata. */ /** All supported x-amz- headers, except for x-amz-meta- user metadata. */
private static final Set<String> SUPPORTED_X_AMZ_HEADERS = ImmutableSet.of( private static final Set<String> SUPPORTED_X_AMZ_HEADERS = ImmutableSet.of(
@ -333,6 +339,7 @@ public class S3ProxyHandler {
(method.equals("GET") || method.equals("HEAD") || (method.equals("GET") || method.equals("HEAD") ||
method.equals("POST")) && method.equals("POST")) &&
request.getHeader(HttpHeaders.AUTHORIZATION) == null && request.getHeader(HttpHeaders.AUTHORIZATION) == null &&
request.getParameter("X-Amz-Algorithm") == null &&
request.getParameter("AWSAccessKeyId") == null && request.getParameter("AWSAccessKeyId") == null &&
defaultBlobStore != null) { defaultBlobStore != null) {
doHandleAnonymous(request, response, is, uri, defaultBlobStore); doHandleAnonymous(request, response, is, uri, defaultBlobStore);
@ -340,6 +347,7 @@ public class S3ProxyHandler {
} }
if (!anonymousIdentity && !hasDateHeader && !hasXAmzDateHeader && if (!anonymousIdentity && !hasDateHeader && !hasXAmzDateHeader &&
request.getParameter("X-Amz-Date") == null &&
request.getParameter("Expires") == null) { request.getParameter("Expires") == null) {
throw new S3Exception(S3ErrorCode.ACCESS_DENIED, throw new S3Exception(S3ErrorCode.ACCESS_DENIED,
"AWS authentication requires a valid Date or" + "AWS authentication requires a valid Date or" +
@ -373,13 +381,32 @@ public class S3ProxyHandler {
if (!anonymousIdentity) { if (!anonymousIdentity) {
if (headerAuthorization == null) { if (headerAuthorization == null) {
String identity = request.getParameter("AWSAccessKeyId"); String algorithm = request.getParameter("X-Amz-Algorithm");
String signature = request.getParameter("Signature"); if (algorithm == null) {
if (identity == null || signature == null) { String identity = request.getParameter("AWSAccessKeyId");
throw new S3Exception(S3ErrorCode.ACCESS_DENIED); String signature = request.getParameter("Signature");
if (identity == null || signature == null) {
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
}
headerAuthorization = "AWS " + identity + ":" + signature;
presignedUrl = true;
} else if (algorithm.equals("AWS4-HMAC-SHA256")) {
String credential = request.getParameter(
"X-Amz-Credential");
String signedHeaders = request.getParameter(
"X-Amz-SignedHeaders");
String signature = request.getParameter(
"X-Amz-Signature");
if (credential == null || signedHeaders == null ||
signature == null) {
throw new S3Exception(S3ErrorCode.ACCESS_DENIED);
}
headerAuthorization = "AWS4-HMAC-SHA256" +
" Credential=" + credential +
", requestSignedHeaders=" + signedHeaders +
", Signature=" + signature;
presignedUrl = true;
} }
headerAuthorization = "AWS " + identity + ":" + signature;
presignedUrl = true;
} }
try { try {
@ -464,7 +491,9 @@ public class S3ProxyHandler {
"x-amz-content-sha256"); "x-amz-content-sha256");
try { try {
byte[] payload; byte[] payload;
if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals( if (request.getParameter("X-Amz-Algorithm") != null) {
payload = new byte[0];
} else if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals(
contentSha256)) { contentSha256)) {
payload = new byte[0]; payload = new byte[0];
is = new ChunkedInputStream(is); is = new ChunkedInputStream(is);
@ -481,7 +510,8 @@ public class S3ProxyHandler {
is = new ByteArrayInputStream(payload); is = new ByteArrayInputStream(payload);
} }
expectedSignature = createAuthorizationSignatureV4( expectedSignature = createAuthorizationSignatureV4(
baseRequest, payload, uriForSigning, credential); baseRequest, authHeader, payload, uriForSigning,
credential);
} catch (InvalidKeyException | NoSuchAlgorithmException e) { } catch (InvalidKeyException | NoSuchAlgorithmException e) {
throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT); throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT);
} }
@ -2572,12 +2602,10 @@ public class S3ProxyHandler {
* http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html * http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
*/ */
private static String createAuthorizationSignatureV4( private static String createAuthorizationSignatureV4(
HttpServletRequest request, HttpServletRequest request, S3AuthorizationHeader authHeader,
byte[] payload, String uri, String credential) byte[] payload, String uri, String credential)
throws InvalidKeyException, IOException, NoSuchAlgorithmException, throws InvalidKeyException, IOException, NoSuchAlgorithmException,
S3Exception { S3Exception {
S3AuthorizationHeader authHeader = new S3AuthorizationHeader(
request.getHeader("Authorization"));
String canonicalRequest = createCanonicalRequest(request, uri, payload, String canonicalRequest = createCanonicalRequest(request, uri, payload,
authHeader.hashAlgorithm); authHeader.hashAlgorithm);
String algorithm = authHeader.hmacAlgorithm; String algorithm = authHeader.hmacAlgorithm;
@ -2594,8 +2622,12 @@ public class S3ProxyHandler {
byte[] signingKey = signMessage( byte[] signingKey = signMessage(
"aws4_request".getBytes(StandardCharsets.UTF_8), "aws4_request".getBytes(StandardCharsets.UTF_8),
dateRegionServiceKey, algorithm); dateRegionServiceKey, algorithm);
String date = request.getHeader("x-amz-date");
if (date == null) {
date = request.getParameter("X-Amz-Date");
}
String signatureString = "AWS4-HMAC-SHA256\n" + String signatureString = "AWS4-HMAC-SHA256\n" +
request.getHeader("x-amz-date") + "\n" + date + "\n" +
authHeader.date + "/" + authHeader.region + authHeader.date + "/" + authHeader.region +
"/s3/aws4_request\n" + "/s3/aws4_request\n" +
canonicalRequest; canonicalRequest;
@ -2617,15 +2649,27 @@ public class S3ProxyHandler {
throws IOException, NoSuchAlgorithmException { throws IOException, NoSuchAlgorithmException {
String authorizationHeader = request.getHeader("Authorization"); String authorizationHeader = request.getHeader("Authorization");
String xAmzContentSha256 = request.getHeader("x-amz-content-sha256"); String xAmzContentSha256 = request.getHeader("x-amz-content-sha256");
if (xAmzContentSha256 == null) {
xAmzContentSha256 = request.getParameter("X-Amz-SignedHeaders");
}
String digest; String digest;
if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals(xAmzContentSha256)) { if (authorizationHeader == null) {
digest = "UNSIGNED-PAYLOAD";
} else if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals(
xAmzContentSha256)) {
digest = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; digest = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
} else if ("UNSIGNED-PAYLOAD".equals(xAmzContentSha256)) { } else if ("UNSIGNED-PAYLOAD".equals(xAmzContentSha256)) {
digest = "UNSIGNED-PAYLOAD"; digest = "UNSIGNED-PAYLOAD";
} else { } else {
digest = getMessageDigest(payload, hashAlgorithm); digest = getMessageDigest(payload, hashAlgorithm);
} }
String[] signedHeaders = extractSignedHeaders(authorizationHeader); String[] signedHeaders;
if (authorizationHeader != null) {
signedHeaders = extractSignedHeaders(authorizationHeader);
} else {
signedHeaders = request.getParameter("X-Amz-SignedHeaders")
.split(";");
}
String canonicalRequest = Joiner.on("\n").join( String canonicalRequest = Joiner.on("\n").join(
request.getMethod(), request.getMethod(),
uri, uri,
@ -2693,6 +2737,9 @@ public class S3ProxyHandler {
List<String> queryParameters = new ArrayList<>(); List<String> queryParameters = new ArrayList<>();
for (String key : parameters) { for (String key : parameters) {
if (key.equals("X-Amz-Signature")) {
continue;
}
// re-encode keys and values in AWS normalized form // re-encode keys and values in AWS normalized form
String value = request.getParameter(key); String value = request.getParameter(key);
queryParameters.add(AWS_URL_PARAMETER_ESCAPER.escape(key) + queryParameters.add(AWS_URL_PARAMETER_ESCAPER.escape(key) +

Wyświetl plik

@ -390,8 +390,6 @@ public final class AwsSdkTest {
} }
} }
// TODO: implement V4 URL signing
@Ignore
@Test @Test
public void testAwsV4UrlSigning() throws Exception { public void testAwsV4UrlSigning() throws Exception {
String blobName = "foo"; String blobName = "foo";