Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
lumbermill.internal.aws.AWSV4SignerImpl Maven / Gradle / Ivy
/**
* The MIT License (MIT)
* Copyright (c) 2015 inreachventures
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* This file originates from https://github.com/inreachventures/aws-signing-request-interceptor
* with some minor changes.
*
* Originally Inspired By: http://pokusak.blogspot.co.uk/2015/10/aws-elasticsearch-request-signing.html
*
*/
package lumbermill.internal.aws;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSSessionCredentials;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import lumbermill.internal.elasticsearch.RequestSigner;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
public class AWSV4SignerImpl implements RequestSigner {
private final static char[] BASE16MAP = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static final String HMAC_SHA256 = "HmacSHA256";
private static final String SLASH = "/";
private static final String X_AMZ_DATE = "x-amz-date";
private static final String RETURN = "\n";
private static final String AWS4_HMAC_SHA256 = "AWS4-HMAC-SHA256\n";
private static final String AWS4_REQUEST = "/aws4_request";
private static final String AWS4_HMAC_SHA256_CREDENTIAL = "AWS4-HMAC-SHA256 Credential=";
private static final String SIGNED_HEADERS = ", SignedHeaders=";
private static final String SIGNATURE = ", Signature=";
private static final String SHA_256 = "SHA-256";
private static final String AWS4 = "AWS4";
private static final String AWS_4_REQUEST = "aws4_request";
private static final Joiner JOINER = Joiner.on(';');
private static final String CONNECTION = "connection";
private static final String CLOSE = ":close";
public static final DateTimeFormatter BASIC_TIME_FORMAT = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(ChronoField.YEAR, 4)
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.appendLiteral('Z')
.toFormatter();
private static final String EMPTY = "";
private static final String ZERO = "0";
private static final Joiner AMPERSAND_JOINER = Joiner.on('&');
private static final String CONTENT_LENGTH = "Content-Length";
private static final String AUTHORIZATION = "Authorization";
private static final String SESSION_TOKEN = "x-amz-security-token";
private static final String DATE = "date";
private final AWSCredentialsProvider credentialsProvider;
private final String region;
private final String service;
private final Supplier clock;
public AWSV4SignerImpl(AWSCredentialsProvider credentialsProvider,
String region,
String service,
Supplier clock) {
this.credentialsProvider = credentialsProvider;
this.region = region;
this.service = service;
this.clock = clock;
}
public AWSV4SignerImpl(AWSCredentialsProvider credentialsProvider,
String region,
String service) {
this.credentialsProvider = credentialsProvider;
this.region = region;
this.service = service;
clock = () ->
ZonedDateTime.ofInstant (ZonedDateTime.now().toInstant(),
ZoneId.of("UTC")).toLocalDateTime();
}
public Map getSignedHeaders(String uri, String method,
Map queryParams,
Map headers,
Optional payload) {
final LocalDateTime now = clock.get();
final AWSCredentials credentials = credentialsProvider.getCredentials();
final Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
result.putAll(headers);
if (!result.containsKey(DATE)) {
result.put(X_AMZ_DATE, now.format(BASIC_TIME_FORMAT));
}
if (AWSSessionCredentials.class.isAssignableFrom(credentials.getClass())) {
result.put(SESSION_TOKEN, ((AWSSessionCredentials) credentials).getSessionToken());
}
final StringBuilder headersString = new StringBuilder();
final ImmutableList.Builder signedHeaders = ImmutableList.builder();
for (Map.Entry entry : result.entrySet()) {
headersString.append(headerAsString(entry)).append(RETURN);
signedHeaders.add(entry.getKey().toLowerCase());
}
final String signedHeaderKeys = JOINER.join(signedHeaders.build());
final String canonicalRequest = method + RETURN +
uri + RETURN +
queryParamsString(queryParams) + RETURN +
headersString.toString() + RETURN +
signedHeaderKeys + RETURN +
toBase16(hash(payload.orElse(EMPTY.getBytes(Charsets.UTF_8))));
final String stringToSign = createStringToSign(canonicalRequest, now);
final String signature = sign(stringToSign, now, credentials);
final String autorizationHeader = AWS4_HMAC_SHA256_CREDENTIAL + credentials.getAWSAccessKeyId() + SLASH + getCredentialScope(now) +
SIGNED_HEADERS + signedHeaderKeys +
SIGNATURE + signature;
result.put(AUTHORIZATION, autorizationHeader);
return ImmutableMap.copyOf(result);
}
private String queryParamsString(Map queryParams) {
final ImmutableList.Builder result = ImmutableList.builder();
for (Map.Entry param : new TreeMap<>(queryParams).entrySet()) {
result.add(param.getKey() + '=' + param.getValue());
}
return AMPERSAND_JOINER.join(result.build());
}
private String headerAsString(Map.Entry header) {
if (header.getKey().equalsIgnoreCase(CONNECTION)) {
return CONNECTION + CLOSE;
}
if (header.getKey().equalsIgnoreCase(CONTENT_LENGTH) &&
header.getValue().equals(ZERO)) {
return header.getKey().toLowerCase() + ':';
}
return header.getKey().toLowerCase() + ':' + header.getValue();
}
private String sign(String stringToSign, LocalDateTime now, AWSCredentials credentials) {
return Hex.encodeHexString(hmacSHA256(stringToSign, getSignatureKey(now, credentials)));
}
private String createStringToSign(String canonicalRequest, LocalDateTime now) {
return AWS4_HMAC_SHA256 +
now.format(BASIC_TIME_FORMAT) + RETURN +
getCredentialScope(now) + RETURN +
toBase16(hash(canonicalRequest.getBytes(Charsets.UTF_8)));
}
private String getCredentialScope(LocalDateTime now) {
return now.format(DateTimeFormatter.BASIC_ISO_DATE) + SLASH + region + SLASH + service + AWS4_REQUEST;
}
private byte[] hash(byte[] payload) {
try {
final MessageDigest md = MessageDigest.getInstance(SHA_256);
md.update(payload);
return md.digest();
} catch (NoSuchAlgorithmException e) {
throw Throwables.propagate(e);
}
}
private String toBase16(byte[] data) {
final StringBuilder hexBuffer = new StringBuilder(data.length * 2);
for (byte aData : data) {
hexBuffer.append(BASE16MAP[(aData >> (4)) & 0xF]);
hexBuffer.append(BASE16MAP[(aData) & 0xF]);
}
return hexBuffer.toString();
}
private byte[] getSignatureKey(LocalDateTime now, AWSCredentials credentials) {
final byte[] kSecret = (AWS4 + credentials.getAWSSecretKey()).getBytes(Charsets.UTF_8);
final byte[] kDate = hmacSHA256(now.format(DateTimeFormatter.BASIC_ISO_DATE), kSecret);
final byte[] kRegion = hmacSHA256(region, kDate);
final byte[] kService = hmacSHA256(service, kRegion);
return hmacSHA256(AWS_4_REQUEST, kService);
}
private byte[] hmacSHA256(String data, byte[] key) {
try {
final Mac mac = Mac.getInstance(HMAC_SHA256);
mac.init(new SecretKeySpec(key, HMAC_SHA256));
return mac.doFinal(data.getBytes(Charsets.UTF_8));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw Throwables.propagate(e);
}
}
@Override
public void sign(RequestSigner.SignableRequest request) {
Map signedHeaders = getSignedHeaders( request.uri(),
request.method(), request.queryParams(), request.headers(), request.payload());
request.addSignedHeaders(signedHeaders);
}
}