org.ligoj.app.plugin.prov.aws.auth.AWS4SignerForAuthorizationHeader.java Source code

Java tutorial

Introduction

Here is the source code for org.ligoj.app.plugin.prov.aws.auth.AWS4SignerForAuthorizationHeader.java

Source

/*
 * Licensed under MIT (https://github.com/ligoj/ligoj/blob/master/LICENSE)
 */
package org.ligoj.app.plugin.prov.aws.auth;

import java.time.Clock;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Service;

/**
 * AWS4 signer used to sign requests with an 'Authorization' header.
 */
@Service
public class AWS4SignerForAuthorizationHeader extends AWS4SignerBase {

    /** SHA256 hash of an empty request body **/
    public static final String EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";

    /**
     * format strings for the date/time and date stamps required during signing
     **/
    public static final String ISO8601_FORMAT = "yyyyMMdd'T'HHmmss'Z'";
    public static final String DATE_FORMAT = "yyyyMMdd";

    /**
     * clock used to date query
     */
    private Clock clock = Clock.systemUTC();

    /**
     * Computes an AWS4 signature for a request, ready for inclusion as an
     * 'Authorization' header.
     * 
     * @param query
     *            the query
     * @return The computed authorization string for the request. This value needs
     *         to be set as the header 'Authorization' on the subsequent HTTP
     *         request.
     */
    public String computeSignature(final AWS4SignatureQuery query) {
        // first get the date and time for the subsequent request, and convert
        // to ISO 8601 format for use in signature generation
        final ZonedDateTime now = ZonedDateTime.now(clock);

        final String dateTimeStamp = DateTimeFormatter.ofPattern(ISO8601_FORMAT).format(now);
        final String bodyHash;
        if (query.getBody() == null) {
            bodyHash = EMPTY_BODY_SHA256;
        } else {
            bodyHash = hash(query.getBody());
        }
        // update the headers with required 'x-amz-date' and 'host' values
        query.getHeaders().put("x-amz-date", dateTimeStamp);
        query.getHeaders().put("x-amz-content-sha256", bodyHash);
        query.getHeaders().put("Host", query.getHost());

        // canonicalize the headers; we need the set of header names as well as
        // the names and values to go into the signature process
        final String canonicalizedHeaderNames = getCanonicalizeHeaderNames(query.getHeaders());
        final String canonicalizedHeaders = getCanonicalizedHeaderString(query.getHeaders());

        // if any query string parameters have been supplied, canonicalize them
        final String canonicalizedQueryParameters = getCanonicalizedQueryString(query.getQueryParameters());

        // canonicalize the various components of the request
        final String canonicalRequest = getCanonicalRequest(query.getPath(), query.getMethod(),
                canonicalizedQueryParameters, canonicalizedHeaderNames, canonicalizedHeaders, bodyHash);

        // construct the string to be signed
        final String dateStamp = DateTimeFormatter.ofPattern(DATE_FORMAT).format(now);
        final String scope = dateStamp + "/" + query.getRegion() + "/" + query.getService() + "/" + TERMINATOR;
        final String stringToSign = getStringToSign(dateTimeStamp, scope, canonicalRequest);

        // compute the signing key
        final byte[] kSecret = (SCHEME + query.getSecretKey()).getBytes();
        final byte[] kDate = sign(dateStamp, kSecret);
        final byte[] kRegion = sign(query.getRegion(), kDate);
        final byte[] kService = sign(query.getService(), kRegion);
        final byte[] kSigning = sign(TERMINATOR, kService);
        final byte[] signature = sign(stringToSign, kSigning);

        final String credentialsAuthorizationHeader = "Credential=" + query.getAccessKey() + "/" + scope;
        final String signedHeadersAuthorizationHeader = "SignedHeaders=" + canonicalizedHeaderNames;
        final String signatureAuthorizationHeader = "Signature=" + Hex.encodeHexString(signature);

        return SCHEME + "-" + ALGORITHM + " " + credentialsAuthorizationHeader + ", "
                + signedHeadersAuthorizationHeader + ", " + signatureAuthorizationHeader;
    }
}