001    /**
002     * Copyright (c) 2000-2013 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.StringPool;
020    import com.liferay.portal.kernel.util.StringUtil;
021    import com.liferay.portal.kernel.util.Validator;
022    
023    import java.net.URLEncoder;
024    
025    import java.util.Iterator;
026    import java.util.Map;
027    import java.util.Set;
028    import java.util.TreeMap;
029    
030    import javax.crypto.Mac;
031    import javax.crypto.spec.SecretKeySpec;
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.BLANK;
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.EQUAL);
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                    if (Validator.isNull(amazonSecretAccessKey)) {
088                            return StringPool.BLANK;
089                    }
090    
091                    SecretKeySpec secretKeySpec = new SecretKeySpec(
092                            amazonSecretAccessKey.getBytes(), _HMAC_SHA256_ALGORITHM);
093    
094                    Mac mac = Mac.getInstance(_HMAC_SHA256_ALGORITHM);
095    
096                    mac.init(secretKeySpec);
097    
098                    byte[] bytes = mac.doFinal(data.getBytes());
099    
100                    String signature = Base64.encode(bytes);
101    
102                    return StringUtil.replace(
103                            signature, new String[] {StringPool.EQUAL, StringPool.PLUS},
104                            new String[] {"%3D", "%2B"});
105            }
106    
107            private static String _rfc3986Encode(String string) throws Exception {
108                    if (Validator.isNull(string)) {
109                            return StringPool.BLANK;
110                    }
111    
112                    string = URLEncoder.encode(string, StringPool.UTF8);
113    
114                    string = StringUtil.replace(
115                            string, new String[] {StringPool.STAR, StringPool.PLUS, "%7E"},
116                            new String[] {"%2A", "%2B", "~"});
117    
118                    return string;
119            }
120    
121            private static final String _HMAC_SHA256_ALGORITHM = "HmacSHA256";
122    
123    }