private static String createCanonicalRequest(HttpServletRequest request,
                                                 String uri, byte[] payload,
                                                 String hashAlgorithm)
            throws IOException, NoSuchAlgorithmException {
        String authorizationHeader = request.getHeader("Authorization");
        String xAmzContentSha256 = request.getHeader(
                AwsHttpHeaders.CONTENT_SHA256);
        if (xAmzContentSha256 == null) {
            xAmzContentSha256 = request.getParameter("X-Amz-SignedHeaders");
        }
        String digest;
        if (authorizationHeader == null) {
            digest = "UNSIGNED-PAYLOAD";
        } else if ("STREAMING-AWS4-HMAC-SHA256-PAYLOAD".equals(
                xAmzContentSha256)) {
            digest = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
        } else if ("UNSIGNED-PAYLOAD".equals(xAmzContentSha256)) {
            digest = "UNSIGNED-PAYLOAD";
        } else {
            digest = getMessageDigest(payload, hashAlgorithm);
        }
        List<String> signedHeaders;
        if (authorizationHeader != null) {
            signedHeaders = extractSignedHeaders(authorizationHeader);
        } else {
            signedHeaders = Splitter.on(';').splitToList(request.getParameter(
                    "X-Amz-SignedHeaders"));
        }

        /*
         * CORS Preflight
         *
         * The signature is based on the canonical request, which includes the
         * HTTP Method.
         * For presigned URLs, the method must be replaced for OPTIONS request
         * to match
         */
        String method = request.getMethod();
        if ("OPTIONS".equals(method)) {
            String corsMethod = request.getHeader(
                    HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
            if (corsMethod != null) {
                method = corsMethod;
            }
        }

        String canonicalRequest = Joiner.on("\n").join(
                method,
                uri,
                buildCanonicalQueryString(request),
                buildCanonicalHeaders(request, signedHeaders) + "\n",
                Joiner.on(';').join(signedHeaders),
                digest);

        return getMessageDigest(
                canonicalRequest.getBytes(StandardCharsets.UTF_8),
                hashAlgorithm);
    }
Create Amazon V2 signature.  Reference:
http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html
src/main/java/org/gaul/s3proxy/AwsSignature.java