All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ninja.Aws4HashCalculator Maven / Gradle / Ivy

There is a newer version: 2.7
Show newest version
/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package ninja;

import sirius.kernel.commons.Strings;
import sirius.kernel.di.std.Part;
import sirius.kernel.di.std.Register;
import sirius.web.http.WebContext;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.hash.Hashing.sha256;
import static com.google.common.io.BaseEncoding.base16;

/**
 * Hash calculator for AWS
 * signature v4 calculation
 */
@Register(classes = Aws4HashCalculator.class)
public class Aws4HashCalculator {
    protected static final Pattern AWS_AUTH4_PATTERN =
            Pattern.compile("AWS4-HMAC-SHA256 Credential=([^/]+)/([^/]+)/([^/]+)/s3/aws4_request, SignedHeaders=([^,"
                            + "]+), Signature=(.+)");

    @Part
    private Storage storage;

    /**
     * Determines if the given request contains an AWS4 auth token.
     *
     * @param ctx the request to check
     * @return true if the request contains an AWS4 auth token, false  otherwise.
     */
    public boolean supports(final WebContext ctx) {
        final Matcher aws4Header = buildMatcher(ctx);
        return aws4Header.matches();
    }

    private Matcher initializedMatcher(final WebContext ctx) {
        Matcher matcher = buildMatcher(ctx);
        return matcher.matches() ? matcher : null;
    }

    private Matcher buildMatcher(final WebContext ctx) {
        return AWS_AUTH4_PATTERN.matcher(ctx.getHeaderValue("Authorization").asString(""));
    }

    /**
     * Computes the authentication hash as specified by the AWS SDK for verification purposes.
     *
     * @param ctx the current request to fetch parameters from
     * @return the computes hash value
     * @throws Exception in case of an unexpected error
     */
    public String computeHash(WebContext ctx) throws Exception {
        final MatchResult aws4Header = initializedMatcher(ctx);

        byte[] dateKey = hmacSHA256(("AWS4" + storage.getAwsSecretKey()).getBytes(UTF_8), aws4Header.group(2));
        byte[] dateRegionKey = hmacSHA256(dateKey, aws4Header.group(3));
        byte[] dateRegionServiceKey = hmacSHA256(dateRegionKey, "s3");
        byte[] signingKey = hmacSHA256(dateRegionServiceKey, "aws4_request");

        byte[] signedData = hmacSHA256(signingKey, buildStringToSign(ctx));
        return base16().lowerCase().encode(signedData);
    }

    private String buildStringToSign(final WebContext ctx) {
        final StringBuilder canonicalRequest = buildCanonicalRequest(ctx);
        final MatchResult aws4Header = initializedMatcher(ctx);
        return "AWS4-HMAC-SHA256\n"
               + getAmazonDateHeader(ctx)
               + "\n"
               + getAmazonDateHeader(ctx).substring(0, 8)
               + "/"
               + aws4Header.group(3)
               + "/s3/aws4_request\n"
               + hashedCanonicalRequest(canonicalRequest);
    }

    private String getAmazonDateHeader(final WebContext ctx) {
        return ctx.getHeaderValue("x-amz-date").asString();
    }

    private StringBuilder buildCanonicalRequest(final WebContext ctx) {
        final MatchResult aws4Header = initializedMatcher(ctx);
        StringBuilder canonicalRequest = new StringBuilder(ctx.getRequest().getMethod().name());
        canonicalRequest.append("\n");
        canonicalRequest.append(ctx.getRequestedURI());
        canonicalRequest.append("\n");
        canonicalRequest.append(ctx.getQueryString());
        canonicalRequest.append("\n");

        for (String name : aws4Header.group(4).split(";")) {
            canonicalRequest.append(name.trim());
            canonicalRequest.append(":");
            canonicalRequest.append(Strings.join(ctx.getRequest().headers().getAll(name), ",").trim());
            canonicalRequest.append("\n");
        }
        canonicalRequest.append("\n");
        canonicalRequest.append(aws4Header.group(4));
        canonicalRequest.append("\n");
        canonicalRequest.append(ctx.getHeader("x-amz-content-sha256"));
        return canonicalRequest;
    }

    private String hashedCanonicalRequest(final StringBuilder canonicalRequest) {
        return sha256().hashString(canonicalRequest, UTF_8).toString();
    }

    private byte[] hmacSHA256(byte[] key, String value) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(keySpec);
        return mac.doFinal(value.getBytes(UTF_8));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy