com.pearson.pdn.learningstudio.helloworld.OAuth1SignatureServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.pearson.pdn.learningstudio.helloworld.OAuth1SignatureServlet.java

Source

/*
* LearningStudio HelloWorld Application & API Explorer 
* 
* Need Help or Have Questions? 
* Please use the PDN Developer Community at https://community.pdn.pearson.com
*
* @category   LearningStudio HelloWorld
* @author     Wes Williams <wes.williams@pearson.com>
* @author     Pearson Developer Services Team <apisupport@pearson.com>
* @copyright  2014 Pearson Education Inc.
* @license    http://www.apache.org/licenses/LICENSE-2.0  Apache 2.0
* @version    1.0
* 
* Licensed 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 com.pearson.pdn.learningstudio.helloworld;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.params.KeyParameter;

public class OAuth1SignatureServlet extends OAuthServlet {
    private final static String SIGNATURE_METHOD = "CMAC-AES";

    @Override
    public Map<String, String> getOAuthHeaders(String resourceUrl, String httpMethod, String body)
            throws IOException {

        final String applicationId = resources.getString("applicationId");
        final String consumerKey = resources.getString("consumerKey");
        final String consumerSecret = resources.getString("consumerSecret");

        URL url = new URL(resourceUrl);

        // Set the Nonce and Timestamp parameters
        String nonce = getNonce();
        String timestamp = getTimestamp();

        byte[] requestBody = null;
        // Set the request body if making a POST or PUT request
        if ("POST".equalsIgnoreCase(httpMethod) || "PUT".equalsIgnoreCase(httpMethod)) {
            requestBody = body.getBytes("UTF-8");
        }

        // Create the OAuth parameter name/value pair
        Map<String, String> oauthParams = new LinkedHashMap<String, String>();
        oauthParams.put("oauth_consumer_key", consumerKey);
        oauthParams.put("application_id", applicationId);
        oauthParams.put("oauth_signature_method", SIGNATURE_METHOD);
        oauthParams.put("oauth_timestamp", timestamp);
        oauthParams.put("oauth_nonce", nonce);

        // Get the OAuth 1.0 Signature
        String signature = generateSignature(httpMethod, url, oauthParams, requestBody, consumerSecret);
        // Add the oauth_signature parameter to the set of OAuth Parameters
        oauthParams.put("oauth_signature", signature);

        // Generate a string of comma delimited: keyName="URL-encoded(value)" pairs

        StringBuilder sb = new StringBuilder();
        String delimiter = "";
        for (String keyName : oauthParams.keySet()) {
            sb.append(delimiter);
            String value = oauthParams.get((String) keyName);
            sb.append(keyName).append("=\"").append(URLEncoder.encode(value, "UTF-8")).append("\"");
            delimiter = ",";
        }

        String urlString = url.toString();
        // omit the queryString from the url
        int startOfQueryString = urlString.indexOf('?');
        if (startOfQueryString != -1) {
            urlString = urlString.substring(0, startOfQueryString);
        }

        // Build the X-Authorization request header
        String xauth = String.format("OAuth realm=\"%s\",%s", urlString, sb.toString());
        Map<String, String> headers = new TreeMap<String, String>();
        headers.put("X-Authorization", xauth);

        return headers;
    }

    private String normalizeParams(String httpMethod, URL url, Map<String, String> oauthParams, byte[] requestBody)
            throws UnsupportedEncodingException {

        // Sort the parameters in lexicographical order, 1st by Key then by Value
        Map<String, String> kvpParams = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
        kvpParams.putAll(oauthParams);

        // Place any query string parameters into a key value pair using equals
        // ("=") to mark
        // the key/value relationship and join each parameter with an ampersand
        // ("&")
        if (url.getQuery() != null) {
            for (String keyValue : url.getQuery().split("&")) {
                String[] p = keyValue.split("=");
                kvpParams.put(p[0], p[1]);
            }

        }

        // Include the body parameter if dealing with a POST or PUT request
        if ("POST".equals(httpMethod) || "PUT".equals(httpMethod)) {
            String body = Base64.encodeBase64String(requestBody).replaceAll("\r\n", "");
            // url encode the body 2 times now before combining other params
            body = URLEncoder.encode(body, "UTF-8");
            body = URLEncoder.encode(body, "UTF-8");
            kvpParams.put("body", body);
        }

        // separate the key and values with a "="
        // separate the kvp with a "&"
        StringBuilder combinedParams = new StringBuilder();
        String delimiter = "";
        for (String key : kvpParams.keySet()) {
            combinedParams.append(delimiter);
            combinedParams.append(key);
            combinedParams.append("=");
            combinedParams.append(kvpParams.get(key));
            delimiter = "&";
        }

        // url encode the entire string again before returning
        return URLEncoder.encode(combinedParams.toString(), "UTF-8");
    }

    private String generateSignature(String httpMethod, URL url, Map<String, String> oauthParams,
            byte[] requestBody, String secret) throws UnsupportedEncodingException {
        // Ensure the HTTP Method is upper-cased
        httpMethod = httpMethod.toUpperCase();

        // Construct the URL-encoded OAuth parameter portion of the signature
        // base string
        String encodedParams = normalizeParams(httpMethod, url, oauthParams, requestBody);

        // URL-encode the relative URL
        String encodedUri = URLEncoder.encode(url.getPath(), "UTF-8");

        // Build the signature base string to be signed with the Consumer Secret
        String baseString = String.format("%s&%s&%s", httpMethod, encodedUri, encodedParams);

        return generateCmac(secret, baseString);
    }

    private String getNonce() {
        return RandomStringUtils.randomAlphanumeric(32);
    }

    private String getTimestamp() {
        return Long.toString((System.currentTimeMillis() / 1000));
    }

    private String generateCmac(String key, String msg) throws UnsupportedEncodingException {
        byte[] keyBytes = key.getBytes("UTF-8");
        byte[] data = msg.getBytes("UTF-8");

        CMac macProvider = new CMac(new AESFastEngine());
        macProvider.init(new KeyParameter(keyBytes));
        macProvider.reset();

        macProvider.update(data, 0, data.length);
        byte[] output = new byte[macProvider.getMacSize()];
        macProvider.doFinal(output, 0);

        // Convert the CMAC to a Base64 string and remove the new line the
        // Base64 library adds
        String cmac = Base64.encodeBase64String(output).replaceAll("\r\n", "");

        return cmac;
    }

}