org.wso2.carbon.connector.amazons3.auth.AmazonS3AuthConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.connector.amazons3.auth.AmazonS3AuthConnector.java

Source

/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License 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 org.wso2.carbon.connector.amazons3.auth;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseConstants;
import org.wso2.carbon.connector.amazons3.constants.AmazonS3Constants;
import org.wso2.carbon.connector.core.AbstractConnector;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Class AmazonS3AuthConnector which helps to generate authentication header for Amazon S3 WSO2 ESB Connector.
 */
public class AmazonS3AuthConnector extends AbstractConnector {
    private static Log log = LogFactory.getLog(AmazonS3AuthConnector.class);

    /**
     * Connect method which is generating authentication of the connector for each request.
     *
     * @param messageContext ESB messageContext.
     */
    public final void connect(final MessageContext messageContext) {
        final StringBuilder canonicalRequest = new StringBuilder();
        final StringBuilder stringToSign = new StringBuilder();
        final StringBuilder authHeader = new StringBuilder();
        final Map<String, String> parametersMap = getParametersMap(messageContext);
        final Locale defaultLocale = Locale.getDefault();

        final Date date = new Date();
        final TimeZone timeZone = TimeZone.getTimeZone(AmazonS3Constants.TIME_ZONE);
        final SimpleDateFormat dateFormat = new SimpleDateFormat(AmazonS3Constants.CURR_DATE_FORMAT, defaultLocale);
        dateFormat.setTimeZone(timeZone);
        final String currentDate = dateFormat.format(date);

        final DateFormat shortDateFormat = new SimpleDateFormat(AmazonS3Constants.SHORT_DATE_FORMAT);
        shortDateFormat.setTimeZone(timeZone);
        final String shortDate = shortDateFormat.format(date);
        try {
            final String dateTrimmed = currentDate.trim();
            final Map<String, String> amzHeadersMap = new HashMap<>();
            final Map<String, String> queryParamsMap = new HashMap<>();
            final StringBuilder canonicalHeaders = new StringBuilder();
            final StringBuilder canonicalQueryString = new StringBuilder();
            final StringBuilder signedHeader = new StringBuilder();

            canonicalRequest.append(parametersMap.get(AmazonS3Constants.METHOD_TYPE))
                    .append(AmazonS3Constants.NEW_LINE);
            // Setting the canonicalized resource.
            if (StringUtils.isNotEmpty(parametersMap.get(AmazonS3Constants.BUCKET_NAME))) {
                canonicalRequest.append(AmazonS3Constants.FORWARD_SLASH)
                        .append(parametersMap.get(AmazonS3Constants.BUCKET_NAME));
            }
            String urlRemainder = (String) messageContext.getProperty(AmazonS3Constants.URI_REMAINDER);
            if (urlRemainder != null && !urlRemainder.isEmpty()) {
                canonicalRequest.append(urlRemainder.replaceAll("%2F", "/"));
            }

            // Setting canonicalQueryString
            final Map<String, String> queryParametersMap = getQueryParamKeysMap();
            for (Map.Entry<String, String> entry : queryParametersMap.entrySet()) {
                String key = entry.getKey();
                String tempParam = parametersMap.get(key);
                if (!tempParam.isEmpty()) {
                    queryParamsMap.put(queryParametersMap.get(key),
                            tempParam.replaceAll(AmazonS3Constants.REGEX, AmazonS3Constants.EMPTY_STR));
                }
            }

            String queryString = (String) messageContext.getProperty(AmazonS3Constants.QUERY_STRING);
            final SortedSet<String> queryKeys = new TreeSet<>(queryParamsMap.keySet());

            for (String key : queryKeys) {
                if (key.equals(AmazonS3Constants.QUERY_STRING)) {
                    canonicalQueryString.append(URLEncoder.encode(queryString, AmazonS3Constants.UTF_8))
                            .append(AmazonS3Constants.EQUAL).append(AmazonS3Constants.EMPTY_STR)
                            .append(AmazonS3Constants.AMP);
                } else {
                    String queryValue = queryParamsMap.get(key);
                    canonicalQueryString.append(URLEncoder.encode(key, AmazonS3Constants.UTF_8))
                            .append(AmazonS3Constants.EQUAL)
                            .append(URLEncoder.encode(queryValue, AmazonS3Constants.UTF_8))
                            .append(AmazonS3Constants.AMP);
                }

            }

            //Remove additionally added ampersand at the end from canonicalQueryString and append to canonicalRequest.
            if (canonicalQueryString.length() > 0) {
                canonicalRequest.append(AmazonS3Constants.NEW_LINE);
                canonicalRequest.append(canonicalQueryString.substring(0, canonicalQueryString.length() - 1));
            } else {
                canonicalRequest.append(AmazonS3Constants.NEW_LINE);
            }

            if (Boolean.parseBoolean(parametersMap.get(AmazonS3Constants.IS_XAMZ_DATE))) {
                canonicalRequest.append(AmazonS3Constants.NEW_LINE);
                amzHeadersMap.put(AmazonS3Constants.HD_XAMZ_DATE, dateTrimmed);
                messageContext.setProperty(AmazonS3Constants.IS_XAMZ_DATE_VAL, dateTrimmed);
            } else {
                canonicalRequest.append(dateTrimmed).append(AmazonS3Constants.NEW_LINE);
            }

            final Map<String, String> amzHeaderKeysMap = getHeaderKeysMap();
            for (Map.Entry<String, String> entry : amzHeaderKeysMap.entrySet()) {
                String key = entry.getKey();
                String tempParam = parametersMap.get(key);
                if (!tempParam.isEmpty()) {
                    amzHeadersMap.put(amzHeaderKeysMap.get(key), tempParam);
                }
            }

            final SortedSet<String> keys = new TreeSet<>(amzHeadersMap.keySet());
            for (String key : keys) {
                String headerValues = amzHeadersMap.get(key);
                canonicalHeaders.append(key.toLowerCase(defaultLocale)).append(AmazonS3Constants.COLON)
                        .append(headerValues).append(AmazonS3Constants.NEW_LINE);
                signedHeader.append(key.toLowerCase(defaultLocale));
                signedHeader.append(AmazonS3Constants.SEMI_COLON);
            }
            canonicalRequest.append(canonicalHeaders).append(AmazonS3Constants.NEW_LINE);

            // Remove unwanted semi-colon at the end of the signedHeader string
            String signedHeaders = "";
            if (signedHeader.length() > 0) {
                signedHeaders = signedHeader.substring(0, signedHeader.length() - 1);
            }
            canonicalRequest.append(signedHeaders).append(AmazonS3Constants.NEW_LINE);
            canonicalRequest.append(AmazonS3Constants.UNSIGNED_PAYLOAD);

            // Create stringToSign
            stringToSign.append(AmazonS3Constants.AWS4_HMAC_SHA_256);
            stringToSign.append(AmazonS3Constants.NEW_LINE);
            stringToSign.append(dateTrimmed);
            stringToSign.append(AmazonS3Constants.NEW_LINE);

            stringToSign.append(shortDate);
            stringToSign.append(AmazonS3Constants.FORWARD_SLASH);
            stringToSign.append(messageContext.getProperty(AmazonS3Constants.REGION));
            stringToSign.append(AmazonS3Constants.FORWARD_SLASH);
            stringToSign.append(messageContext.getProperty(AmazonS3Constants.SERVICE));
            stringToSign.append(AmazonS3Constants.FORWARD_SLASH);
            stringToSign.append(messageContext.getProperty(AmazonS3Constants.TERMINATION_STRING));

            stringToSign.append(AmazonS3Constants.NEW_LINE);
            stringToSign.append(bytesToHex(hash(messageContext, canonicalRequest.toString())).toLowerCase());

            if ((messageContext.getProperty(AmazonS3Constants.SECRET_ACCESS_KEY) != null)
                    && (messageContext.getProperty(AmazonS3Constants.REGION) != null)
                    && (messageContext.getProperty(AmazonS3Constants.SERVICE) != null) && (shortDate) != null) {
                final byte[] signingKey = getSignatureKey(messageContext,
                        messageContext.getProperty(AmazonS3Constants.SECRET_ACCESS_KEY).toString(), shortDate,
                        messageContext.getProperty(AmazonS3Constants.REGION).toString(),
                        messageContext.getProperty(AmazonS3Constants.SERVICE).toString());

                // Construction of authorization header value to be included in API request
                authHeader.append(AmazonS3Constants.AWS4_HMAC_SHA_256);
                authHeader.append(" ");
                authHeader.append(AmazonS3Constants.CREDENTIAL);
                authHeader.append(AmazonS3Constants.EQUAL);
                authHeader.append(messageContext.getProperty(AmazonS3Constants.ACCESS_KEY_ID));
                authHeader.append(AmazonS3Constants.FORWARD_SLASH);
                authHeader.append(shortDate);
                authHeader.append(AmazonS3Constants.FORWARD_SLASH);
                authHeader.append(messageContext.getProperty(AmazonS3Constants.REGION));
                authHeader.append(AmazonS3Constants.FORWARD_SLASH);
                authHeader.append(messageContext.getProperty(AmazonS3Constants.SERVICE));
                authHeader.append(AmazonS3Constants.FORWARD_SLASH);
                authHeader.append(messageContext.getProperty(AmazonS3Constants.TERMINATION_STRING));
                authHeader.append(AmazonS3Constants.COMMA);
                authHeader.append(AmazonS3Constants.SIGNED_HEADERS);
                authHeader.append(AmazonS3Constants.EQUAL);
                authHeader.append(signedHeaders);
                authHeader.append(AmazonS3Constants.COMMA);
                authHeader.append(AmazonS3Constants.API_SIGNATURE);
                authHeader.append(AmazonS3Constants.EQUAL);
                authHeader.append(bytesToHex(hmacSHA256(signingKey, stringToSign.toString())).toLowerCase());

                // Adds authorization header to message context
                messageContext.setProperty(AmazonS3Constants.AUTH_CODE, authHeader.toString());
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("secretAccessKey or region or service or shortDate may be null"
                            + "Hence couldn't generate signingKey");
                }
                handleException(AmazonS3Constants.CONNECTOR_ERROR + " secretAccessKey or region or service or "
                        + "shortDate may be null" + "Hence couldn't generate signingKey.", messageContext);
            }
        } catch (InvalidKeyException exc) {
            storeErrorResponseStatus(messageContext, exc, AmazonS3Constants.INVALID_KEY_ERROR_CODE);
            handleException(AmazonS3Constants.INVALID_KEY_ERROR, exc, messageContext);
        } catch (NoSuchAlgorithmException exc) {
            storeErrorResponseStatus(messageContext, exc, AmazonS3Constants.NO_SUCH_ALGORITHM_ERROR_CODE);
            handleException(AmazonS3Constants.NO_SUCH_ALGORITHM_ERROR, exc, messageContext);
        } catch (IllegalStateException exc) {
            storeErrorResponseStatus(messageContext, exc, AmazonS3Constants.ILLEGAL_STATE_ERROR_CODE);
            handleException(AmazonS3Constants.CONNECTOR_ERROR, exc, messageContext);
        } catch (UnsupportedEncodingException exc) {
            storeErrorResponseStatus(messageContext, exc, AmazonS3Constants.UNSUPPORTED_ENCORDING_ERROR_CODE);
            handleException(AmazonS3Constants.CONNECTOR_ERROR, exc, messageContext);
        }
    }

    /**
     * getKeys method used to return list of predefined parameter keys.
     *
     * @return list of parameter key value.
     */
    private String[] getKeys() {

        return new String[] { AmazonS3Constants.ACCESS_KEY_ID, AmazonS3Constants.SECRET_ACCESS_KEY,
                AmazonS3Constants.METHOD_TYPE, AmazonS3Constants.CONTENT_MD5, AmazonS3Constants.CONTENT_TYPE,
                AmazonS3Constants.BUCKET_NAME, AmazonS3Constants.IS_XAMZ_DATE,
                AmazonS3Constants.XAMZ_SECURITY_TOKEN, AmazonS3Constants.XAMZ_ACL,
                AmazonS3Constants.XAMZ_GRANT_READ, AmazonS3Constants.XAMZ_GRANT_WRITE,
                AmazonS3Constants.XAMZ_GRANT_READ_ACP, AmazonS3Constants.XAMZ_GRANT_WRITE_ACP,
                AmazonS3Constants.XAMZ_GRANT_FULL_CONTROL, AmazonS3Constants.XAMZ_META,
                AmazonS3Constants.XAMZ_SERVE_ENCRYPTION, AmazonS3Constants.XAMZ_STORAGE_CLASS,
                AmazonS3Constants.XAMZ_WEBSITE_LOCATION, AmazonS3Constants.XAMZ_MFA,
                AmazonS3Constants.XAMZ_COPY_SOURCE, AmazonS3Constants.XAMZ_COPY_SOURCE_RANGE,
                AmazonS3Constants.XAMZ_METADATA_DIRECTIVE, AmazonS3Constants.XAMZ_COPY_SOURCE_IF_MATCH,
                AmazonS3Constants.XAMZ_COPY_SOURCE_IF_NONE_MATCH,
                AmazonS3Constants.XAMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE,
                AmazonS3Constants.XAMZ_COPY_SOURCE_IF_MODIFIED_SINCE, AmazonS3Constants.HOST,
                AmazonS3Constants.XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM,
                AmazonS3Constants.XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY,
                AmazonS3Constants.XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
                AmazonS3Constants.XAMZ_CONTENT_SHA256, AmazonS3Constants.DELIMITER, AmazonS3Constants.ENCODING_TYPE,
                AmazonS3Constants.KEY_MARKER, AmazonS3Constants.MAX_KEYS, AmazonS3Constants.PREFIX,
                AmazonS3Constants.VERSION_ID_MARKER, AmazonS3Constants.QUERY_STRING, AmazonS3Constants.VERSION_ID,
                AmazonS3Constants.MARKER, AmazonS3Constants.UPLOAD_ID_MARKER, AmazonS3Constants.MAX_UPLOADS,
                AmazonS3Constants.PART_NUMBER, AmazonS3Constants.UPLOAD_ID, AmazonS3Constants.RESPONSE_CONTENT_TYPE,
                AmazonS3Constants.RESPONSE_CONTENT_LANGUAGE, AmazonS3Constants.RESPONSE_EXPIRES,
                AmazonS3Constants.RESPONSE_CACHE_CONTROL, AmazonS3Constants.RESPONSE_CONTENT_DISPOSITION,
                AmazonS3Constants.RESPONSE_CONTENT_ENCODING, AmazonS3Constants.PART_NUMBER_MARKER,
                AmazonS3Constants.MAX_PARTS };

    }

    /**
     * getParametersMap method used to return list of parameter values passed in via proxy.
     *
     * @param messageContext ESB messageContext.
     * @return assigned parameter values as a HashMap.
     */
    private Map<String, String> getParametersMap(final MessageContext messageContext) {

        String[] keys = getKeys();
        Map<String, String> parametersMap = new HashMap<>();
        for (String key : keys) {
            String paramValue = (messageContext.getProperty(key) != null) ? (String) messageContext.getProperty(key)
                    : AmazonS3Constants.EMPTY_STR;
            parametersMap.put(key, paramValue);
        }
        return parametersMap;
    }

    /**
     * queryParamKeysMap method used to return list of query parameter keys with values.
     *
     * @return list of parameter key value.
     */
    private Map<String, String> getQueryParamKeysMap() {
        Map<String, String> queryParamKeysMap = new HashMap<>();

        queryParamKeysMap.put(AmazonS3Constants.DELIMITER, AmazonS3Constants.API_DELIMITER);
        queryParamKeysMap.put(AmazonS3Constants.ENCODING_TYPE, AmazonS3Constants.API_ENCODING_TYPE);
        queryParamKeysMap.put(AmazonS3Constants.KEY_MARKER, AmazonS3Constants.API_KEY_MARKER);
        queryParamKeysMap.put(AmazonS3Constants.MARKER, AmazonS3Constants.API_MARKER);
        queryParamKeysMap.put(AmazonS3Constants.MAX_KEYS, AmazonS3Constants.API_MAX_KEYS);
        queryParamKeysMap.put(AmazonS3Constants.PREFIX, AmazonS3Constants.API_PREFIX);
        queryParamKeysMap.put(AmazonS3Constants.VERSION_ID_MARKER, AmazonS3Constants.API_VERSION_ID_MARKER);
        queryParamKeysMap.put(AmazonS3Constants.QUERY_STRING, AmazonS3Constants.QUERY_STRING);
        queryParamKeysMap.put(AmazonS3Constants.VERSION_ID, AmazonS3Constants.API_VERSION_ID);
        queryParamKeysMap.put(AmazonS3Constants.UPLOAD_ID_MARKER, AmazonS3Constants.API_UPLOAD_ID_MARKER);
        queryParamKeysMap.put(AmazonS3Constants.MAX_UPLOADS, AmazonS3Constants.API_MAX_UPLOADS);
        queryParamKeysMap.put(AmazonS3Constants.PART_NUMBER, AmazonS3Constants.API_PART_NUMBER);
        queryParamKeysMap.put(AmazonS3Constants.UPLOAD_ID, AmazonS3Constants.API_UPLOAD_ID);
        queryParamKeysMap.put(AmazonS3Constants.RESPONSE_CONTENT_TYPE, AmazonS3Constants.API_RESPONSE_CONTENT_TYPE);
        queryParamKeysMap.put(AmazonS3Constants.RESPONSE_CONTENT_LANGUAGE,
                AmazonS3Constants.API_RESPONSE_CONTENT_LANGUAGE);
        queryParamKeysMap.put(AmazonS3Constants.RESPONSE_EXPIRES, AmazonS3Constants.API_RESPONSE_EXPIRES);
        queryParamKeysMap.put(AmazonS3Constants.RESPONSE_CACHE_CONTROL,
                AmazonS3Constants.API_RESPONSE_CACHE_CONTROL);
        queryParamKeysMap.put(AmazonS3Constants.RESPONSE_CONTENT_DISPOSITION,
                AmazonS3Constants.API_RESPONSE_CONTENT_DISPOSITION);
        queryParamKeysMap.put(AmazonS3Constants.RESPONSE_CONTENT_ENCODING,
                AmazonS3Constants.API_RESPONSE_CONTENT_ENCODING);
        queryParamKeysMap.put(AmazonS3Constants.PART_NUMBER_MARKER, AmazonS3Constants.API_PART_NUMBER_MARKER);
        queryParamKeysMap.put(AmazonS3Constants.MAX_PARTS, AmazonS3Constants.API_MAX_PARTS);

        return queryParamKeysMap;
    }

    /**
     * getHeaderKeysMap method used to return list of predefined XAMZ keys with values.
     *
     * @return list of Amz header keys and values Map.
     */
    private Map<String, String> getHeaderKeysMap() {

        Map<String, String> headerKeysMap = new HashMap<>();

        headerKeysMap.put(AmazonS3Constants.CONTENT_TYPE, AmazonS3Constants.API_CONTENT_TYPE);
        headerKeysMap.put(AmazonS3Constants.CONTENT_MD5, AmazonS3Constants.API_CONTENT_MD5);
        headerKeysMap.put(AmazonS3Constants.HOST, AmazonS3Constants.API_HOST);
        headerKeysMap.put(AmazonS3Constants.XAMZ_SECURITY_TOKEN, AmazonS3Constants.HD_XAMZ_SECURITY_TOKEN);
        headerKeysMap.put(AmazonS3Constants.XAMZ_ACL, AmazonS3Constants.HD_XAMZ_ACL);
        headerKeysMap.put(AmazonS3Constants.XAMZ_GRANT_READ, AmazonS3Constants.HD_XAMZ_GRANT_READ);
        headerKeysMap.put(AmazonS3Constants.XAMZ_GRANT_WRITE, AmazonS3Constants.HD_XAMZ_GRANT_WRITE);
        headerKeysMap.put(AmazonS3Constants.XAMZ_GRANT_READ_ACP, AmazonS3Constants.HD_XAMZ_GRANT_READ_ACP);
        headerKeysMap.put(AmazonS3Constants.XAMZ_GRANT_WRITE_ACP, AmazonS3Constants.HD_XAMZ_GRANT_WRITE_ACP);
        headerKeysMap.put(AmazonS3Constants.XAMZ_GRANT_FULL_CONTROL, AmazonS3Constants.HD_XAMZ_GRANT_FULL_CONTROL);
        headerKeysMap.put(AmazonS3Constants.XAMZ_META, AmazonS3Constants.HD_XAMZ_META);
        headerKeysMap.put(AmazonS3Constants.XAMZ_SERVE_ENCRYPTION, AmazonS3Constants.HD_XAMZ_SERVE_ENCRYPTION);
        headerKeysMap.put(AmazonS3Constants.XAMZ_STORAGE_CLASS, AmazonS3Constants.HD_XAMZ_STORAGE_CLASS);
        headerKeysMap.put(AmazonS3Constants.XAMZ_WEBSITE_LOCATION, AmazonS3Constants.HD_XAMZ_WEBSITE_LOCATION);
        headerKeysMap.put(AmazonS3Constants.XAMZ_MFA, AmazonS3Constants.HD_XAMZ_MFA);
        headerKeysMap.put(AmazonS3Constants.XAMZ_COPY_SOURCE, AmazonS3Constants.HD_XAMZ_COPY_SOURCE);
        headerKeysMap.put(AmazonS3Constants.XAMZ_COPY_SOURCE_RANGE, AmazonS3Constants.HD_XAMZ_COPY_SOURCE_RANGE);
        headerKeysMap.put(AmazonS3Constants.XAMZ_METADATA_DIRECTIVE, AmazonS3Constants.HD_XAMZ_METADATA_DIRECTIVE);
        headerKeysMap.put(AmazonS3Constants.XAMZ_COPY_SOURCE_IF_MATCH,
                AmazonS3Constants.HD_XAMZ_COPY_SOURCE_IF_MATCH);
        headerKeysMap.put(AmazonS3Constants.XAMZ_COPY_SOURCE_IF_NONE_MATCH,
                AmazonS3Constants.HD_XAMZ_COPY_SOURCE_IF_NONE_MATCH);
        headerKeysMap.put(AmazonS3Constants.XAMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE,
                AmazonS3Constants.HD_XAMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE);
        headerKeysMap.put(AmazonS3Constants.XAMZ_COPY_SOURCE_IF_MODIFIED_SINCE,
                AmazonS3Constants.HD_XAMZ_COPY_SOURCE_IF_MODIFIED_SINCE);
        headerKeysMap.put(AmazonS3Constants.XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM,
                AmazonS3Constants.HD_XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM);
        headerKeysMap.put(AmazonS3Constants.XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY,
                AmazonS3Constants.HD_XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY);
        headerKeysMap.put(AmazonS3Constants.XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
                AmazonS3Constants.HD_XMAZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5);
        headerKeysMap.put(AmazonS3Constants.XAMZ_CONTENT_SHA256, AmazonS3Constants.HD_XAMZ_CONTENT_SHA256);

        return headerKeysMap;
    }

    /**
     * Add a Throwable to a message context, the message from the throwable is embedded as the Synapse.
     * Constant ERROR_MESSAGE.
     *
     * @param ctxt      message context to which the error tags need to be added
     * @param throwable Throwable that needs to be parsed and added
     * @param errorCode errorCode mapped to the exception
     */
    private void storeErrorResponseStatus(final MessageContext ctxt, final Throwable throwable,
            final int errorCode) {
        ctxt.setProperty(SynapseConstants.ERROR_CODE, errorCode);
        ctxt.setProperty(SynapseConstants.ERROR_MESSAGE, throwable.getMessage());
        ctxt.setFaultResponse(true);
    }

    /**
     * Add a message to message context, the message from the throwable is embedded as the Synapse Constant
     * ERROR_MESSAGE.
     *
     * @param ctxt      message context to which the error tags need to be added
     * @param message   message to be returned to the user
     * @param errorCode errorCode mapped to the exception
     */
    private void storeErrorResponseStatus(final MessageContext ctxt, final String message, final int errorCode) {
        ctxt.setProperty(SynapseConstants.ERROR_CODE, errorCode);
        ctxt.setProperty(SynapseConstants.ERROR_MESSAGE, message);
        ctxt.setFaultResponse(true);
    }

    /**
     * Hashes the string contents (assumed to be UTF-8) using the SHA-256 algorithm.
     *
     * @param messageContext of the connector
     * @param text           text to be hashed
     * @return SHA-256 hashed text
     */
    private byte[] hash(final MessageContext messageContext, final String text) {
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance(AmazonS3Constants.SHA_256);
            messageDigest.update(text.getBytes(AmazonS3Constants.UTF_8));
        } catch (Exception exc) {
            storeErrorResponseStatus(messageContext, exc, AmazonS3Constants.ERROR_CODE_EXCEPTION);
            handleException(AmazonS3Constants.CONNECTOR_ERROR, exc, messageContext);
        }
        if (messageDigest == null) {
            log.error(AmazonS3Constants.CONNECTOR_ERROR);
            storeErrorResponseStatus(messageContext, AmazonS3Constants.CONNECTOR_ERROR,
                    AmazonS3Constants.ERROR_CODE_EXCEPTION);
            handleException(AmazonS3Constants.CONNECTOR_ERROR, messageContext);
        }
        if (messageDigest != null) {
            return messageDigest.digest();
        }
        return null;
    }

    /**
     * bytesToHex method HexEncoded the received byte array.
     *
     * @param bytes bytes to be hex encoded
     * @return hex encoded String of the given byte array
     */
    private static String bytesToHex(final byte[] bytes) {
        final char[] hexArray = AmazonS3Constants.HEX_ARRAY_STRING.toCharArray();
        char[] hexChars = new char[bytes.length * 2];

        for (int j = 0; j < bytes.length; j++) {
            final int byteVal = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[byteVal >>> 4];
            hexChars[j * 2 + 1] = hexArray[byteVal & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * Provides the HMAC SHA 256 encoded value(using the provided key) of the given data.
     *
     * @param key  to use for encoding
     * @param data to be encoded
     * @return HMAC SHA 256 encoded byte array
     * @throws NoSuchAlgorithmException     No such algorithm Exception
     * @throws InvalidKeyException          Invalid key Exception
     * @throws UnsupportedEncodingException Unsupported Encoding Exception
     * @throws IllegalStateException        Illegal State Exception
     */
    private static byte[] hmacSHA256(final byte[] key, final String data) throws NoSuchAlgorithmException,
            InvalidKeyException, IllegalStateException, UnsupportedEncodingException {
        final String algorithm = AmazonS3Constants.HAMC_SHA_256;
        final Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data.getBytes(AmazonS3Constants.UTF8));
    }

    /**
     * Returns the encoded signature key to be used for further encodings as per API doc.
     *
     * @param ctx         message context of the connector
     * @param key         key to be used for signing
     * @param dateStamp   current date stamp
     * @param regionName  region name given to the connector
     * @param serviceName Name of the service being addressed
     * @return Signature key
     * @throws UnsupportedEncodingException Unsupported Encoding Exception
     * @throws IllegalStateException        Illegal Argument Exception
     * @throws NoSuchAlgorithmException     No Such Algorithm Exception
     * @throws InvalidKeyException          Invalid Key Exception
     */
    private static byte[] getSignatureKey(final MessageContext ctx, final String key, final String dateStamp,
            final String regionName, final String serviceName) throws UnsupportedEncodingException,
            InvalidKeyException, NoSuchAlgorithmException, IllegalStateException {
        final byte[] kSecret = (AmazonS3Constants.AWS4 + key).getBytes(AmazonS3Constants.UTF8);
        final byte[] kDate = hmacSHA256(kSecret, dateStamp);
        final byte[] kRegion = hmacSHA256(kDate, regionName);
        final byte[] kService = hmacSHA256(kRegion, serviceName);
        return hmacSHA256(kService, ctx.getProperty(AmazonS3Constants.TERMINATION_STRING).toString());
    }

}