Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2013-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License").
   * You may not use this file except in compliance with the License.
   * A copy of the License is located at
   *
   *  http://aws.amazon.com/apache2.0
   *
  * or in the "license" file accompanying this file. This file is distributed
  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  * express or implied. See the License for the specific language governing
  * permissions and limitations under the License.
  */
 package com.amazonaws.auth;
 
 import static com.amazonaws.auth.internal.SignerConstants.AUTHORIZATION;
 import static com.amazonaws.auth.internal.SignerConstants.AWS4_SIGNING_ALGORITHM;
 import static com.amazonaws.auth.internal.SignerConstants.AWS4_TERMINATOR;
 import static com.amazonaws.auth.internal.SignerConstants.HOST;
 import static com.amazonaws.auth.internal.SignerConstants.LINE_SEPARATOR;
 import static com.amazonaws.auth.internal.SignerConstants.PRESIGN_URL_MAX_EXPIRATION_SECONDS;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_ALGORITHM;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_CONTENT_SHA256;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_CREDENTIAL;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_DATE;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_EXPIRES;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SECURITY_TOKEN;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SIGNATURE;
 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SIGNED_HEADER;
 
 import java.net.URI;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
Signer implementation that signs requests with the AWS4 signing protocol.
 
 public class AWS4Signer extends AbstractAWSSigner implements
 
     protected static final InternalLogApi log = InternalLogFactory.getLog(AWS4Signer.class);
     private static final int SIGNER_CACHE_MAX_SIZE = 300;
     private static final FIFOCache<SignerKeysignerCache = new FIFOCache<SignerKey>();

    
Service name override for use when the endpoint can't be used to determine the service name.
 
     protected String serviceName;

    
Region name override for use when the endpoint can't be used to determine the region name.
 
     protected String regionName;

    
Date override for testing only
 
     protected Date overriddenDate;

    
Whether double url-encode the resource path when constructing the canonical request. By default, we enable double url-encoding. TODO: Different sigv4 services seem to be inconsistent on this. So for services that want to suppress this, they should use new AWS4Signer(false).
 
     protected boolean doubleUrlEncode;

    
Construct a new AWS4 signer instance. By default, enable double url-encoding.
 
     public AWS4Signer() {
         this(true);
     }

    
Construct a new AWS4 signer instance.

Parameters:
doubleUrlEncoding Whether double url-encode the resource path when constructing the canonical request.
    public AWS4Signer(boolean doubleUrlEncoding) {
        this. = doubleUrlEncoding;
    }

    
Sets the service name that this signer should use when calculating request signatures. This can almost always be determined directly from the request's end point, so you shouldn't need this method, but it's provided for the edge case where the information is not in the endpoint.

Parameters:
serviceName The service name to use when calculating signatures in this signer.
    @Override
    public void setServiceName(String serviceName) {
        this. = serviceName;
    }

    
Sets the region name that this signer should use when calculating request signatures. This can almost always be determined directly from the request's end point, so you shouldn't need this method, but it's provided for the edge case where the information is not in the endpoint.

Parameters:
regionName The region name to use when calculating signatures in this signer.
    @Override
    public void setRegionName(String regionName) {
        this. = regionName;
    }

    
Sets the date that overrides the signing date in the request. This method is internal and should be used only for testing purposes.
    void setOverrideDate(Date overriddenDate) {
        this. = overriddenDate;
    }

    
Returns the region name that is used when calculating the signature.
    public String getRegionName() {
        return ;
    }

    
Returns the service name that is used when calculating the signature.
    public String getServiceName() {
        return ;
    }

    
Returns a copy of date that overrides the signing date in the request. Return null by default.
    public Date getOverriddenDate() {
        return  == null ? null : new Date(
                .getTime());
    }
    @Override
    public void sign(SignableRequest<?> requestAWSCredentials credentials) {
        // anonymous credentials, don't sign
        if (isAnonymous(credentials)) {
            return;
        }
        AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials);
        if (sanitizedCredentials instanceof AWSSessionCredentials) {
            addSessionCredentials(request,
                    (AWSSessionCredentialssanitizedCredentials);
        }
        final AWS4SignerRequestParams signerParams = new AWS4SignerRequestParams(
                request,
                );
        addHostHeader(request);
        request.addHeader(,
                signerParams.getFormattedSigningDateTime());
        String contentSha256 = calculateContentHash(request);
        if ("required".equals(request.getHeaders().get())) {
            request.addHeader(contentSha256);
        }
        final String canonicalRequest = createCanonicalRequest(request,
                contentSha256);
        final String stringToSign = createStringToSign(canonicalRequest,
                signerParams);
        final byte[] signingKey = deriveSigningKey(sanitizedCredentials,
                signerParams);
        final byte[] signature = computeSignature(stringToSignsigningKey,
                signerParams);
        request.addHeader(
                ,
                buildAuthorizationHeader(requestsignature,
                        sanitizedCredentialssignerParams));
        processRequestPayload(requestsignaturesigningKey,
                signerParams);
    }
    @Override
    public void presignRequest(SignableRequest<?> requestAWSCredentials credentials,
            Date userSpecifiedExpirationDate) {
        // anonymous credentials, don't sign
        if (isAnonymous(credentials)) {
            return;
        }
        long expirationInSeconds = generateExpirationDate(userSpecifiedExpirationDate);
        addHostHeader(request);
        AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials);
        if (sanitizedCredentials instanceof AWSSessionCredentials) {
            // For SigV4 pre-signing URL, we need to add "X-Amz-Security-Token"
            // as a query string parameter, before constructing the canonical
            // request.
            request.addParameter(,
                    ((AWSSessionCredentialssanitizedCredentials)
                            .getSessionToken());
        }
        final AWS4SignerRequestParams signerRequestParams = new AWS4SignerRequestParams(
                request,
                );
        // Add the important parameters for v4 signing
        final String timeStamp = AWS4SignerUtils.formatTimestamp(System
                .currentTimeMillis());
        addPreSignInformationToRequest(requestsanitizedCredentials,
                signerRequestParamstimeStampexpirationInSeconds);
        final String contentSha256 = calculateContentHashPresign(request);
        final String canonicalRequest = createCanonicalRequest(request,
                contentSha256);
        final String stringToSign = createStringToSign(canonicalRequest,
                signerRequestParams);
        final byte[] signingKey = deriveSigningKey(sanitizedCredentials,
                signerRequestParams);
        final byte[] signature = computeSignature(stringToSignsigningKey,
                signerRequestParams);
        request.addParameter(, BinaryUtils.toHex(signature));
    }

    
Step 1 of the AWS Signature version 4 calculation. Refer to http://docs.aws .amazon.com/general/latest/gr/sigv4-create-canonical-request.html to generate the canonical request.
    protected String createCanonicalRequest(SignableRequest<?> request,
            String contentSha256) {
        /* This would url-encode the resource path for the first time. */
        final String path = SdkHttpUtils.appendUri(
                request.getEndpoint().getPath(), request.getResourcePath());
        final StringBuilder canonicalRequestBuilder = new StringBuilder(request
                .getHttpMethod().toString());
        canonicalRequestBuilder.append()
                // This would optionally double url-encode the resource path
                .append(getCanonicalizedResourcePath(path))
                .append()
                .append(getCanonicalizedQueryString(request))
                .append()
                .append(getCanonicalizedHeaderString(request))
                .append()
                .append(getSignedHeadersString(request)).append()
                .append(contentSha256);
        final String canonicalRequest = canonicalRequestBuilder.toString();
        if (.isDebugEnabled())
            .debug("AWS4 Canonical Request: '\"" + canonicalRequest + "\"");
        return canonicalRequest;
    }

    
Step 2 of the AWS Signature version 4 calculation. Refer to http://docs.aws .amazon.com/general/latest/gr/sigv4-create-string-to-sign.html.
    protected String createStringToSign(String canonicalRequest,
            AWS4SignerRequestParams signerParams) {
        final StringBuilder stringToSignBuilder = new StringBuilder(
                signerParams.getSigningAlgorithm());
        stringToSignBuilder.append()
                .append(signerParams.getFormattedSigningDateTime())
                .append()
                .append(signerParams.getScope())
                .append()
                .append(BinaryUtils.toHex(hash(canonicalRequest)));
        final String stringToSign = stringToSignBuilder.toString();
        if (.isDebugEnabled())
            .debug("AWS4 String to Sign: '\"" + stringToSign + "\"");
        return stringToSign;
    }

    
Step 3 of the AWS Signature version 4 calculation. It involves deriving the signing key and computing the signature. Refer to http://docs.aws.amazon .com/general/latest/gr/sigv4-calculate-signature.html
    private final byte[] deriveSigningKey(AWSCredentials credentials,
            AWS4SignerRequestParams signerRequestParams) {
        final String cacheKey = computeSigningCacheKeyName(credentials,
                signerRequestParams);
        final long daysSinceEpochSigningDate = DateUtils
                .numberOfDaysSinceEpoch(signerRequestParams
                        .getSigningDateTimeMilli());
        SignerKey signerKey = .get(cacheKey);
        if (signerKey != null) {
            if (daysSinceEpochSigningDate == signerKey
                    .getNumberOfDaysSinceEpoch()) {
                return signerKey.getSigningKey();
            }
        }
        if (.isDebugEnabled()) {
            .debug("Generating a new signing key as the signing key not available in the cache for the date "
                    + ..toMillis(daysSinceEpochSigningDate));
        }
        byte[] signingKey = newSigningKey(credentials,
                signerRequestParams.getFormattedSigningDate(),
                signerRequestParams.getRegionName(),
                signerRequestParams.getServiceName());
        .add(cacheKeynew SignerKey(
                daysSinceEpochSigningDatesigningKey));
        return signingKey;
    }

    
Computes the name to be used to reference the signing key in the cache.
    private final String computeSigningCacheKeyName(AWSCredentials credentials,
            AWS4SignerRequestParams signerRequestParams) {
        final StringBuilder hashKeyBuilder = new StringBuilder(
                credentials.getAWSSecretKey());
        return hashKeyBuilder.append("-")
                .append(signerRequestParams.getRegionName())
                .append("-")
                .append(signerRequestParams.getServiceName()).toString();
    }

    
Step 3 of the AWS Signature version 4 calculation. It involves deriving the signing key and computing the signature. Refer to http://docs.aws.amazon .com/general/latest/gr/sigv4-calculate-signature.html
    protected final byte[] computeSignature(String stringToSign,
            byte[] signingKeyAWS4SignerRequestParams signerRequestParams) {
        return sign(stringToSign.getBytes(Charset.forName("UTF-8")), signingKey,
                .);
    }

    
Creates the authorization header to be included in the request.
    private String buildAuthorizationHeader(SignableRequest<?> request,
            byte[] signatureAWSCredentials credentials,
            AWS4SignerRequestParams signerParams) {
        final String signingCredentials = credentials.getAWSAccessKeyId() + "/"
                + signerParams.getScope();
        final String credential = "Credential="
                + signingCredentials;
        final String signerHeaders = "SignedHeaders="
                + getSignedHeadersString(request);
        final String signatureHeader = "Signature="
                + BinaryUtils.toHex(signature);
        final StringBuilder authHeaderBuilder = new StringBuilder();
        authHeaderBuilder.append()
                         .append(" ")
                         .append(credential)
                         .append(", ")
                         .append(signerHeaders)
                         .append(", ")
                         .append(signatureHeader);
        return authHeaderBuilder.toString();
    }

    
Includes all the signing headers as request parameters for pre-signing.
    private void addPreSignInformationToRequest(SignableRequest<?> request,
            AWSCredentials credentialsAWS4SignerRequestParams signerParams,
            String timeStamplong expirationInSeconds) {
        String signingCredentials = credentials.getAWSAccessKeyId() + "/"
                + signerParams.getScope();
        request.addParameter(timeStamp);
        request.addParameter(,
                getSignedHeadersString(request));
        request.addParameter(,
                Long.toString(expirationInSeconds));
        request.addParameter(signingCredentials);
    }
    @Override
    protected void addSessionCredentials(SignableRequest<?> request,
            AWSSessionCredentials credentials) {
        request.addHeader(credentials.getSessionToken());
    }
    protected String getCanonicalizedHeaderString(SignableRequest<?> request) {
        final List<StringsortedHeaders = new ArrayList<String>(request.getHeaders()
                .keySet());
        Collections.sort(sortedHeaders.);
        final Map<StringStringrequestHeaders = request.getHeaders();
        StringBuilder buffer = new StringBuilder();
        for (String header : sortedHeaders) {
            String key = header.toLowerCase().replaceAll("\\s+"" ");
            String value = requestHeaders.get(header);
            buffer.append(key).append(":");
            if (value != null) {
                buffer.append(value.replaceAll("\\s+"" "));
            }
            buffer.append("\n");
        }
        return buffer.toString();
    }
    protected String getSignedHeadersString(SignableRequest<?> request) {
        final List<StringsortedHeaders = new ArrayList<String>(request
                .getHeaders().keySet());
        Collections.sort(sortedHeaders.);
        StringBuilder buffer = new StringBuilder();
        for (String header : sortedHeaders) {
            if (buffer.length() > 0)
                buffer.append(";");
            buffer.append(header.toLowerCase());
        }
        return buffer.toString();
    }
    protected void addHostHeader(SignableRequest<?> request) {
        // AWS4 requires that we sign the Host header so we
        // have to have it in the request by the time we sign.
        final URI endpoint = request.getEndpoint();
        final StringBuilder hostHeaderBuilder = new StringBuilder(
                endpoint.getHost());
        if (SdkHttpUtils.isUsingNonDefaultPort(endpoint)) {
            hostHeaderBuilder.append(":").append(endpoint.getPort());
        }
        request.addHeader(hostHeaderBuilder.toString());
    }

    
Calculate the hash of the request's payload. Subclass could override this method to provide different values for "x-amz-content-sha256" header or do any other necessary set-ups on the request headers. (e.g. aws-chunked uses a pre-defined header value, and needs to change some headers relating to content-encoding and content-length.)
    protected String calculateContentHash(SignableRequest<?> request) {
        InputStream payloadStream = getBinaryRequestPayloadStream(request);
        ReadLimitInfo info = request.getReadLimitInfo();
        payloadStream.mark(info == null ? -1 : info.getReadLimit());
        String contentSha256 = BinaryUtils.toHex(hash(payloadStream));
        try {
            payloadStream.reset();
        } catch (IOException e) {
            throw new AmazonClientException(
                    "Unable to reset stream after calculating AWS4 signature",
                    e);
        }
        return contentSha256;
    }

    
Subclass could override this method to perform any additional procedure on the request payload, with access to the result from signing the header. (e.g. Signing the payload by chunk-encoding). The default implementation doesn't need to do anything.
    protected void processRequestPayload(SignableRequest<?> requestbyte[] signature,
            byte[] signingKeyAWS4SignerRequestParams signerRequestParams) {
        return;
    }

    
Calculate the hash of the request's payload. In case of pre-sign, the existing code would generate the hash of an empty byte array and returns it. This method can be overridden by sub classes to provide different values (e.g) For S3 pre-signing, the content hash calculation is different from the general implementation.
    protected String calculateContentHashPresign(SignableRequest<?> request) {
        return calculateContentHash(request);
    }

    
Checks if the credentials is an instance of AnonymousAWSCredentials
    private boolean isAnonymous(AWSCredentials credentials) {
        return credentials instanceof AnonymousAWSCredentials;
    }

    
Generates an expiration date for the presigned url. If user has specified an expiration date, check if it is in the given limit.
    private long generateExpirationDate(Date expirationDate) {
        long expirationInSeconds = expirationDate != null ? ((expirationDate
                .getTime() - System.currentTimeMillis()) / 1000L)
                : ;
        if (expirationInSeconds > ) {
            throw new AmazonClientException(
                    "Requests that are pre-signed by SigV4 algorithm are valid for at most 7 days. "
                            + "The expiration date set on the current request ["
                            + AWS4SignerUtils.formatTimestamp(expirationDate
                                    .getTime()) + "] has exceeded this limit.");
        }
        return expirationInSeconds;
    }

    
Generates a new signing key from the given parameters and returns it.
    private byte[] newSigningKey(AWSCredentials credentials,
            String dateStampString regionNameString serviceName) {
        byte[] kSecret = ("AWS4" + credentials.getAWSSecretKey())
                .getBytes(Charset.forName("UTF-8"));
        byte[] kDate = sign(dateStampkSecret.);
        byte[] kRegion = sign(regionNamekDate.);
        byte[] kService = sign(serviceNamekRegion,
                .);
        return sign(kService.);
    }
New to GrepCode? Check out our FAQ X