001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.amazonrankings.util;
016    
017    import com.liferay.portal.kernel.util.Base64;
018    import com.liferay.portal.kernel.util.StringBundler;
019    import com.liferay.portal.kernel.util.StringUtil;
020    
021    import java.net.URLEncoder;
022    
023    import java.util.Iterator;
024    import java.util.Map;
025    import java.util.Set;
026    import java.util.TreeMap;
027    
028    import javax.crypto.Mac;
029    import javax.crypto.spec.SecretKeySpec;
030    
031    import jodd.util.StringPool;
032    
033    /**
034     * @author Barrie Selack
035     * @author Brian Wing Shun Chan
036     */
037    public class AmazonSignedRequestsUtil {
038    
039            public static String generateUrlWithSignature(
040                            Map<String, String> parameters)
041                    throws Exception {
042    
043                    String canonicalizedParameters = _canonicalizeParameters(parameters);
044    
045                    String signature = _generateSignature(
046                            "GET\necs.amazonaws.com\n/onca/xml\n" + canonicalizedParameters);
047    
048                    return "http://ecs.amazonaws.com/onca/xml?" + canonicalizedParameters +
049                            "&Signature=" + signature;
050            }
051    
052            private static String _canonicalizeParameters(
053                            Map<String, String> parameters)
054                    throws Exception {
055    
056                    if (parameters.isEmpty()) {
057                            return StringPool.EMPTY;
058                    }
059    
060                    StringBundler sb = new StringBundler();
061    
062                    parameters = new TreeMap<String, String>(parameters);
063    
064                    Set<Map.Entry<String, String>> parametersSet = parameters.entrySet();
065    
066                    Iterator<Map.Entry<String, String>> itr = parametersSet.iterator();
067    
068                    while (itr.hasNext()) {
069                            Map.Entry<String, String> parameter = itr.next();
070    
071                            sb.append(_rfc3986Encode(parameter.getKey()));
072                            sb.append(StringPool.EQUALS);
073                            sb.append(_rfc3986Encode(parameter.getValue()));
074    
075                            if (itr.hasNext()) {
076                                    sb.append(StringPool.AMPERSAND);
077                            }
078                    }
079    
080                    return sb.toString();
081            }
082    
083            private static String _generateSignature(String data) throws Exception {
084                    String amazonSecretAccessKey =
085                            AmazonRankingsUtil.getAmazonSecretAccessKey();
086    
087                    SecretKeySpec secretKeySpec = new SecretKeySpec(
088                            amazonSecretAccessKey.getBytes(), _HMAC_SHA256_ALGORITHM);
089    
090                    Mac mac = Mac.getInstance(_HMAC_SHA256_ALGORITHM);
091    
092                    mac.init(secretKeySpec);
093    
094                    byte[] bytes = mac.doFinal(data.getBytes());
095    
096                    String signature = Base64.encode(bytes);
097    
098                    return StringUtil.replace(
099                            signature, new String[] {StringPool.EQUALS, StringPool.PLUS},
100                            new String[] {"%3D", "%2B"});
101            }
102    
103            private static String _rfc3986Encode(String string) throws Exception {
104                    string = URLEncoder.encode(string, StringPool.UTF_8);
105    
106                    string = StringUtil.replace(
107                            string,
108                            new String[] {StringPool.ASTERISK, StringPool.PLUS, "%7E"},
109                            new String[] {"%2A", "%2B", "~"});
110    
111                    return string;
112            }
113    
114            private static final String _HMAC_SHA256_ALGORITHM = "HmacSHA256";
115    
116    }