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

prerna.poi.main.helper.SignedRequestsHelper Maven / Gradle / Ivy

The newest version!
package prerna.poi.main.helper;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import prerna.util.Constants; 
 
/** 
 * This class contains the logic for signing requests 
 * to the Amazon Product Advertising API. 
 */ 
public class SignedRequestsHelper {
	
	private static final Logger classLogger = LogManager.getLogger(SignedRequestsHelper.class);
	
    /**
     * All strings are handled as UTF-8 
     */ 
    private static final String UTF8_CHARSET = "UTF-8"; 
     
    /**
     * The HMAC algorithm required by Amazon 
     */ 
    private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256"; 
     
    /**
     * This is the URI for the service, don't change unless you really know 
     * what you're doing. 
     */ 
    private static final String REQUEST_URI = "/onca/xml"; 
     
    /**
     * The sample uses HTTP GET to fetch the response. If you changed the sample 
     * to use HTTP POST instead, change the value below to POST.  
     */ 
    private static final String REQUEST_METHOD = "GET"; 
 
    private String endpoint = null; 
    private String awsAccessKeyId = null; 
    private String awsSecretKey = null; 
 
    private SecretKeySpec secretKeySpec = null; 
    private Mac mac = null; 
 
    /**
     * You must provide the three values below to initialize the helper. 
     *   
     * @param endpoint          Destination for the requests. 
     * @param awsAccessKeyId    Your AWS Access Key ID 
     * @param awsSecretKey      Your AWS Secret Key 
     */ 
    public static SignedRequestsHelper getInstance( 
            String endpoint,  
            String awsAccessKeyId,  
            String awsSecretKey 
    ) throws IllegalArgumentException, UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException 
    { 
        if (null == endpoint || endpoint.length() == 0) 
            { throw new IllegalArgumentException("endpoint is null or empty"); } 
        if (null == awsAccessKeyId || awsAccessKeyId.length() == 0)  
            { throw new IllegalArgumentException("awsAccessKeyId is null or empty"); } 
        if (null == awsSecretKey || awsSecretKey.length() == 0)    
            { throw new IllegalArgumentException("awsSecretKey is null or empty"); } 
         
        SignedRequestsHelper instance = new SignedRequestsHelper(); 
        instance.endpoint = endpoint.toLowerCase(); 
        instance.awsAccessKeyId = awsAccessKeyId; 
        instance.awsSecretKey = awsSecretKey; 
 
        byte[] secretyKeyBytes = instance.awsSecretKey.getBytes(UTF8_CHARSET); 
        instance.secretKeySpec = new SecretKeySpec(secretyKeyBytes, HMAC_SHA256_ALGORITHM); 
        instance.mac = Mac.getInstance(HMAC_SHA256_ALGORITHM); 
        instance.mac.init(instance.secretKeySpec); 
 
        return instance; 
    } 
     
    /**
     * The construct is private since we'd rather use getInstance() 
     */ 
    private SignedRequestsHelper() {} 
 
    /**
     * This method signs requests in hashmap form. It returns a URL that should 
     * be used to fetch the response. The URL returned should not be modified in 
     * any way, doing so will invalidate the signature and Amazon will reject 
     * the request. 
     */ 
    public String sign(Map params) { 
        // Let's add the AWSAccessKeyId and Timestamp parameters to the request. 
        params.put("AWSAccessKeyId", this.awsAccessKeyId); 
        params.put("Timestamp", this.timestamp()); 
 
        // The parameters need to be processed in lexicographical order, so we'll 
        // use a TreeMap implementation for that. 
        SortedMap sortedParamMap = new TreeMap(params); 
         
        // get the canonical form the query string 
        String canonicalQS = this.canonicalize(sortedParamMap); 
         
        // create the string upon which the signature is calculated  
        String toSign =  
            REQUEST_METHOD + "\n"  
            + this.endpoint + "\n" 
            + REQUEST_URI + "\n" 
            + canonicalQS; 
 
        // get the signature 
        String hmac = this.hmac(toSign); 
        String sig = this.percentEncodeRfc3986(hmac); 
 
        // construct the URL 
        String url =  
            "http://" + this.endpoint + REQUEST_URI + "?" + canonicalQS + "&Signature=" + sig; 
 
        return url; 
    } 
 
    /**
     * This method signs requests in query-string form. It returns a URL that 
     * should be used to fetch the response. The URL returned should not be 
     * modified in any way, doing so will invalidate the signature and Amazon 
     * will reject the request. 
     */ 
    public String sign(String queryString) { 
        // let's break the query string into it's constituent name-value pairs 
        Map params = this.createParameterMap(queryString); 
         
        // then we can sign the request as before 
        return this.sign(params); 
    } 
 
    /**
     * Compute the HMAC. 
     *   
     * @param stringToSign  String to compute the HMAC over. 
     * @return              base64-encoded hmac value. 
     */ 
    private String hmac(String stringToSign) { 
        String signature = null; 
        byte[] data; 
        byte[] rawHmac; 
        try { 
            data = stringToSign.getBytes(UTF8_CHARSET); 
            rawHmac = mac.doFinal(data); 
            Base64 encoder = new Base64(76,new byte[0]); 
            signature = new String(encoder.encode(rawHmac)); 
        } catch (UnsupportedEncodingException e) { 
        	classLogger.error(Constants.STACKTRACE, e);
            throw new RuntimeException(UTF8_CHARSET + " is unsupported!"); 
        } 
        return signature; 
    } 
 
    /**
     * Generate a ISO-8601 format timestamp as required by Amazon. 
     *   
     * @return  ISO-8601 format timestamp. 
     */ 
    private String timestamp() { 
        String timestamp = null; 
        Calendar cal = Calendar.getInstance(); 
        DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 
        dfm.setTimeZone(TimeZone.getTimeZone("GMT")); 
        timestamp = dfm.format(cal.getTime()); 
        return timestamp; 
    } 
 
    /**
     * Canonicalize the query string as required by Amazon. 
     *  
     * @param sortedParamMap    Parameter name-value pairs in lexicographical order. 
     * @return                  Canonical form of query string. 
     */ 
    private String canonicalize(SortedMap sortedParamMap) { 
        if (sortedParamMap.isEmpty()) { 
            return ""; 
        } 
 
        StringBuffer buffer = new StringBuffer(); 
        Iterator> iter = sortedParamMap.entrySet().iterator(); 
 
        while (iter.hasNext()) { 
            Map.Entry kvpair = iter.next(); 
            buffer.append(percentEncodeRfc3986(kvpair.getKey())); 
            buffer.append("="); 
            buffer.append(percentEncodeRfc3986(kvpair.getValue())); 
            if (iter.hasNext()) { 
                buffer.append("&"); 
            } 
        } 
        String cannoical = buffer.toString(); 
        return cannoical; 
    } 
 
    /**
     * Percent-encode values according the RFC 3986. The built-in Java 
     * URLEncoder does not encode according to the RFC, so we make the 
     * extra replacements. 
     *  
     * @param s decoded string 
     * @return  encoded string per RFC 3986 
     */ 
    private String percentEncodeRfc3986(String s) { 
        String out; 
        try { 
            out = URLEncoder.encode(s, UTF8_CHARSET) 
                .replace("+", "%20") 
                .replace("*", "%2A") 
                .replace("%7E", "~"); 
        } catch (UnsupportedEncodingException e) { 
            out = s; 
        } 
        return out; 
    } 
 
    /**
     * Takes a query string, separates the constituent name-value pairs 
     * and stores them in a hashmap. 
     *  
     * @param queryString 
     * @return 
     */ 
    private Map createParameterMap(String queryString) { 
        Map map = new HashMap(); 
        String[] pairs = queryString.split("&"); 
 
        for (String pair: pairs) { 
            if (pair.length() < 1) { 
                continue; 
            } 
 
            String[] tokens = pair.split("=",2); 
            for(int j=0; j


© 2015 - 2025 Weber Informatics LLC | Privacy Policy