/****************************************************************************************
* Copyright 2013 IBM Corp. *
* *
* 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.ibm.sbt.security.authentication.oauth.consumer;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import com.ibm.commons.runtime.Context;
import com.ibm.commons.util.StringUtil;
import com.ibm.commons.util.io.StreamUtil;
import com.ibm.sbt.core.configuration.Configuration;
import com.ibm.sbt.security.authentication.oauth.OAuthException;
import com.ibm.sbt.security.encryption.HMACEncryptionUtility;
import com.ibm.sbt.services.util.SSLUtil;
/**
* HMAC OAuth1 Handler
*
* @author Vimal Dhupar
* @author Manish Kataria
*/
public class HMACOAuth1Handler extends OAuth1Handler implements Serializable {
protected String applicationAccessToken;
public HMACOAuth1Handler() {
}
public HMACOAuth1Handler(String consumerKey, String consumerSecret,
String credentialStore, String appId, String serviceName,
String requestTokenURL, String authorizationURL,
String accessTokenURL, String signatureMethod,
boolean forceTrustSSLCertificate, String applicationAccessToken) {
this.consumerKey = consumerKey;
this.consumerSecret = consumerSecret;
this.credentialStore = credentialStore;
this.appId = appId;
this.serviceName = serviceName;
this.requestTokenURL = requestTokenURL;
this.authorizationURL = authorizationURL;
this.accessTokenURL = accessTokenURL;
this.signatureMethod = signatureMethod;
this.forceTrustSSLCertificate = forceTrustSSLCertificate;
this.applicationAccessToken = applicationAccessToken;
}
private static final long serialVersionUID = 1L;
public String getApplicationAccessToken() {
return applicationAccessToken;
}
public void setApplicationAccessToken(String applicationAccessToken) {
this.applicationAccessToken = applicationAccessToken;
}
/*
* (non-Javadoc)
* @see com.ibm.sbt.security.authentication.oauth.consumer.OAuth1Handler#getRequestTokenFromServer()
*/
@Override
public void getRequestTokenFromServer() throws OAuthException {
int responseCode = HttpStatus.SC_OK;
Context context = Context.get();
String responseBody = "";
try {
HttpClient client = new DefaultHttpClient();
if (getForceTrustSSLCertificate()) {
client = SSLUtil.wrapHttpClient((DefaultHttpClient) client);
}
// In case of Twitter, this callback URL registered can be different from the URL specified below.
String callbackUrl = getCallbackUrl(context);
String consumerKey = getConsumerKey();
String nonce = getNonce();
String timeStamp = getTimestamp();
// HMAC requires parameter to be alphabetically sorted, using LinkedHashMap below to preserve
// ordering
LinkedHashMap<String, String> signatureParamsMap = new LinkedHashMap<String, String>();
signatureParamsMap.put(Configuration.CALLBACK, callbackUrl);
signatureParamsMap.put(Configuration.CONSUMER_KEY, consumerKey);
signatureParamsMap.put(Configuration.NONCE, nonce);
signatureParamsMap.put(Configuration.SIGNATURE_METHOD, getSignatureMethod());
signatureParamsMap.put(Configuration.TIMESTAMP, timeStamp);
signatureParamsMap.put(Configuration.VERSION, Configuration.OAUTH_VERSION1);
String consumerSecret = getConsumerSecret();
String requestPostUrl = getRequestTokenURL();
HttpPost method = new HttpPost(requestPostUrl);
String signature = HMACEncryptionUtility.generateHMACSignature(requestPostUrl,
method.getMethod(), consumerSecret, "", signatureParamsMap);
StringBuilder headerStr = new StringBuilder();
headerStr.append("OAuth ").append(Configuration.CALLBACK).append("=\"").append(callbackUrl)
.append("\"");
headerStr.append(",").append(Configuration.CONSUMER_KEY).append("=\"").append(consumerKey)
.append("\"");
headerStr.append(",").append(Configuration.SIGNATURE_METHOD).append("=\"")
.append(getSignatureMethod()).append("\"");
headerStr.append(",").append(Configuration.TIMESTAMP).append("=\"").append(timeStamp)
.append("\"");
headerStr.append(",").append(Configuration.NONCE).append("=\"").append(nonce).append("\"");
headerStr.append(",").append(Configuration.VERSION).append("=\"")
.append(Configuration.OAUTH_VERSION1).append("\"");
headerStr.append(",").append(Configuration.SIGNATURE).append("=\"")
.append(URLEncoder.encode(signature, "UTF-8")).append("\"");
method.setHeader("Authorization", headerStr.toString());
HttpResponse httpResponse = client.execute(method);
responseCode = httpResponse.getStatusLine().getStatusCode();
InputStream content = httpResponse.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
try {
responseBody = StreamUtil.readString(reader);
} finally {
StreamUtil.close(reader);
}
} catch (Exception e) {
throw new OAuthException(e, "Internal error - getRequestToken failed Exception: ");
}
if (responseCode != HttpStatus.SC_OK) {
String exceptionDetail = buildErrorMessage(responseCode, responseBody);
if (StringUtil.isNotEmpty(exceptionDetail)) {
throw new OAuthException(null,
"HMACOAuth1Handler.java : getRequestTokenFromServer failed." + exceptionDetail);
}
} else {
/*
* The Response from Twitter contains OAuth request token, OAuth request token secret, and a
* boolean oauth_callback_confirmed with value set as true or false.
*/
setRequestToken(getTokenValue(responseBody, Configuration.OAUTH_TOKEN));
setRequestTokenSecret(getTokenValue(responseBody, Configuration.OAUTH_TOKEN_SECRET));
/*
* OAUTH_CALLBACK_CONFIRMED : This property can be used for debugging applications which have not
* provided the Callback URL while registering the Application. If OAUTH_CALLBACK_CONFIRMED is
* returned as false, then the application needs to be modified to set a callback url. However,
* when the Application has specified the Callback Url, and is different from the callback Url we
* are passing, this property value is returned as true.
*/
setOAuthCallbackConfirmed(getTokenValue(responseBody, Configuration.OAUTH_CALLBACK_CONFIRMED));
}
}
/*
* (non-Javadoc)
* @see com.ibm.sbt.security.authentication.oauth.consumer.OAuth1Handler#getAccessTokenFromServer()
*/
@Override
public void getAccessTokenFromServer() throws OAuthException {
int responseCode = HttpStatus.SC_OK;
String responseBody = null;
try {
HttpClient client = new DefaultHttpClient();
if (getForceTrustSSLCertificate()) {
client = SSLUtil.wrapHttpClient((DefaultHttpClient) client);
}
StringBuilder requestPostUrl = new StringBuilder(getAccessTokenURL());
// adding the oauth_verifier to the request.
requestPostUrl.append("?");
requestPostUrl.append(Configuration.OAUTH_VERIFIER).append('=')
.append(URLEncoder.encode(verifierCode, "UTF-8"));
HttpPost method = new HttpPost(requestPostUrl.toString());
// Collecting parameters for preparing the Signature
String consumerKey = getConsumerKey();
String requestToken = getRequestToken();
String nonce = getNonce();
String timeStamp = getTimestamp();
/*
* Generate a map of parameters which are required for creating signature. We are using a Linked
* HashMap to preserver the order in which parameters are added to the Map, as the parameters need
* to be sorted for Twitter Signature generation.
*/
LinkedHashMap<String, String> signatureParamsMap = new LinkedHashMap<String, String>();
signatureParamsMap.put(Configuration.CONSUMER_KEY, consumerKey);
signatureParamsMap.put(Configuration.NONCE, nonce);
signatureParamsMap.put(Configuration.OAUTH_TOKEN, requestToken);
signatureParamsMap.put(Configuration.SIGNATURE_METHOD, getSignatureMethod());
signatureParamsMap.put(Configuration.TIMESTAMP, timeStamp);
signatureParamsMap.put(Configuration.VERSION, Configuration.OAUTH_VERSION1);
String requestTokenSecret = getRequestTokenSecret();
String consumerSecret = getConsumerSecret();
String signature = HMACEncryptionUtility.generateHMACSignature(requestPostUrl.toString(),
method.getMethod(), consumerSecret, requestTokenSecret, signatureParamsMap);
// Preparing the Header for getting access token
StringBuilder headerStr = new StringBuilder();
headerStr.append("OAuth ").append(Configuration.CONSUMER_KEY).append("=\"").append(consumerKey)
.append("\"");
headerStr.append(",").append(Configuration.SIGNATURE_METHOD).append("=\"")
.append(getSignatureMethod()).append("\"");
headerStr.append(",").append(Configuration.TIMESTAMP).append("=\"").append(timeStamp)
.append("\"");
headerStr.append(",").append(Configuration.NONCE).append("=\"").append(nonce).append("\"");
headerStr.append(",").append(Configuration.VERSION).append("=\"")
.append(Configuration.OAUTH_VERSION1).append("\"");
// This is the request token which is obtained from getRequestTokenFromServer() method.
headerStr.append(",").append(Configuration.OAUTH_TOKEN).append("=\"").append(requestToken)
.append("\"");
headerStr.append(",").append(Configuration.SIGNATURE).append("=\"")
.append(URLEncoder.encode(signature, "UTF-8")).append("\"");
method.setHeader("Authorization", headerStr.toString());
method.setHeader("Authorization", headerStr.toString());
HttpResponse httpResponse = client.execute(method);
responseCode = httpResponse.getStatusLine().getStatusCode();
InputStream content = httpResponse.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
try {
responseBody = StreamUtil.readString(reader);
} finally {
StreamUtil.close(reader);
}
} catch (Exception e) {
throw new OAuthException(e, "Internal error - getAccessToken failed Exception: ");
}
if (responseCode != HttpStatus.SC_OK) {
String exceptionDetail = buildErrorMessage(responseCode, responseBody);
if (exceptionDetail != null) {
throw new OAuthException(null,
"HMACOAuth1Handler.java : getAccessTokenFromServer failed. " + exceptionDetail);
}
} else {
/*
* Response from twitter contains Access Token, Access Token Secret, User Id and Screen Name of
* the Application.
*/
setAccessToken(getTokenValue(responseBody, Configuration.OAUTH_TOKEN));
setAccessTokenSecret(getTokenValue(responseBody, Configuration.OAUTH_TOKEN_SECRET));
}
}
/**
* createAuthorizationHeader
*
* @param url
* @param params
* @return
*/
public String createAuthorizationHeader(String url, Map<String, String> params) throws OAuthException {
String nonce = getNonce();
String timeStamp = getTimestamp();
String consumerKey = getConsumerKey();
String consumerSecret = getConsumerSecret();
String method = Context.get().getHttpRequest().getMethod();
/*
* This is the Access Token which is obtained from the Application, while registering the App. User
* will have to create these tokens, in the application, if not generated already. This Access Token
* if required for executing APIs on Twitter.
*/
/* This is the Access Token Secret which is obtained from the getAccessTokenFromServer() method. */
String tokenSecret = getAccessTokenSecret();
/*
* Generate a map of parameters which are required for creating signature. We are using a TreeMap here
* instead of Linked HashMap, since the authorization header creates a signature using the parameters
* passed by the User for API Execution. And all these parameters need to be sorted for Twitter
* Signature generation.
*/
TreeMap<String, String> treeMap = new TreeMap<String, String>();
treeMap.put(Configuration.CONSUMER_KEY, consumerKey);
treeMap.put(Configuration.NONCE, nonce);
treeMap.put(Configuration.SIGNATURE_METHOD, getSignatureMethod());
treeMap.put(Configuration.VERSION, Configuration.OAUTH_VERSION1);
treeMap.put(Configuration.TIMESTAMP, timeStamp);
treeMap.put(Configuration.OAUTH_TOKEN, applicationAccessToken);
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
treeMap.put(key, value);
}
String signature = "";
try {
// generate the complete URL first
signature = HMACEncryptionUtility.generateHMACSignature(url, method, consumerSecret, tokenSecret,
treeMap);
} catch (Exception e) {
throw new OAuthException(e, "createAuthorizationHeader failed with Exception");
}
StringBuilder headerStr = new StringBuilder();
headerStr.append("OAuth ").append(Configuration.CONSUMER_KEY).append("=\"").append(consumerKey)
.append("\"");
headerStr.append(",").append(Configuration.NONCE).append("=\"").append(nonce).append("\"");
try {
headerStr.append(",").append(Configuration.SIGNATURE).append("=\"")
.append(URLEncoder.encode(signature, "UTF-8")).append("\"");
} catch (UnsupportedEncodingException e1) {
throw new OAuthException(e1,
"createAuthorizationHeader failed with UnsupportedEncodingException");
}
headerStr.append(",").append(Configuration.SIGNATURE_METHOD).append("=\"")
.append(getSignatureMethod()).append("\"");
headerStr.append(",").append(Configuration.TIMESTAMP).append("=\"").append(timeStamp).append("\"");
headerStr.append(",").append(Configuration.OAUTH_TOKEN).append("=\"").append(applicationAccessToken)
.append("\"");
headerStr.append(",").append(Configuration.VERSION).append("=\"")
.append(Configuration.OAUTH_VERSION1).append("\"");
return headerStr.toString();
}
}