/*******************************************************************************
* Copyright 2011, 2012, 2013 fanfou.com, Xiaoke, Zhang
*
* 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.fanfou.app.opensource.auth;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.http.client.methods.HttpGet;
import android.text.TextUtils;
import android.util.Log;
import com.fanfou.app.opensource.AppContext;
import com.fanfou.app.opensource.http.SimpleRequestParam;
import com.fanfou.app.opensource.util.support.Base64;
/**
* @author mcxiaoke
* @version 1.0 2011.11.30
* @version 2.0 2011.12.01
* @version 2.1 2011.12.02
* @version 2.2 2011.12.07
*
*/
final class OAuthHelper {
public static final String OAUTH_VERSION1 = "1.0";
public static final String HMAC_SHA1 = "HmacSHA1";
public final static String KEY_SUFFIX = "FE0687E249EBF374";
public static final SimpleRequestParam OAUTH_SIGNATURE_METHOD = new SimpleRequestParam(
"oauth_signature_method", "HMAC-SHA1");
private static final String TAG = OAuthHelper.class.getSimpleName();
public final static Random RAND = new Random();
private static String alignParams(final List<SimpleRequestParam> params) {
Collections.sort(params);
return OAuthHelper.encodeParameters(params);
}
static String buildOAuthHeader(final String method, final String url,
List<SimpleRequestParam> params, final OAuthConfig provider,
final OAuthToken otoken) {
if (params == null) {
params = new ArrayList<SimpleRequestParam>();
}
final long timestamp = System.currentTimeMillis() / 1000;
final long nonce = timestamp + OAuthHelper.RAND.nextInt();
final List<SimpleRequestParam> oauthHeaderParams = new ArrayList<SimpleRequestParam>();
oauthHeaderParams.add(new SimpleRequestParam("oauth_consumer_key",
provider.getConsumerKey()));
oauthHeaderParams.add(OAuthHelper.OAUTH_SIGNATURE_METHOD);
oauthHeaderParams.add(new SimpleRequestParam("oauth_timestamp",
timestamp));
oauthHeaderParams.add(new SimpleRequestParam("oauth_nonce", nonce));
oauthHeaderParams.add(new SimpleRequestParam("oauth_version",
OAuthHelper.OAUTH_VERSION1));
if (null != otoken) {
oauthHeaderParams.add(new SimpleRequestParam("oauth_token", otoken
.getToken()));
}
final List<SimpleRequestParam> signatureBaseParams = new ArrayList<SimpleRequestParam>(
oauthHeaderParams.size() + params.size());
signatureBaseParams.addAll(oauthHeaderParams);
if ((method != HttpGet.METHOD_NAME) && (params != null)
&& !SimpleRequestParam.hasFile(params)) {
signatureBaseParams.addAll(params);
}
OAuthHelper.parseGetParams(url, signatureBaseParams);
final String encodedUrl = OAuthHelper.encode(OAuthHelper
.constructRequestURL(url));
final String encodedParams = OAuthHelper.encode(OAuthHelper
.alignParams(signatureBaseParams));
final StringBuffer base = new StringBuffer(method).append("&")
.append(encodedUrl).append("&").append(encodedParams);
final String oauthBaseString = base.toString();
if (AppContext.DEBUG) {
Log.d(OAuthHelper.TAG, "getOAuthHeader() url=" + url);
Log.d(OAuthHelper.TAG, "getOAuthHeader() encodedUrl=" + encodedUrl);
Log.d(OAuthHelper.TAG, "getOAuthHeader() encodedParams="
+ encodedParams);
Log.d(OAuthHelper.TAG, "getOAuthHeader() baseString="
+ oauthBaseString);
}
final SecretKeySpec spec = OAuthHelper.getSecretKeySpec(provider,
otoken);
oauthHeaderParams.add(new SimpleRequestParam("oauth_signature",
OAuthHelper.getSignature(oauthBaseString, spec)));
return "OAuth "
+ OAuthHelper.encodeParameters(oauthHeaderParams, ",", true);
}
static String buildXAuthHeader(final String username,
final String password, final String method, final String url,
final OAuthConfig provider) {
final long timestamp = System.currentTimeMillis() / 1000;
final long nonce = System.nanoTime() + OAuthHelper.RAND.nextInt();
final List<SimpleRequestParam> oauthHeaderParams = new ArrayList<SimpleRequestParam>();
oauthHeaderParams.add(new SimpleRequestParam("oauth_consumer_key",
provider.getConsumerKey()));
oauthHeaderParams.add(new SimpleRequestParam("oauth_signature_method",
"HMAC-SHA1"));
oauthHeaderParams.add(new SimpleRequestParam("oauth_timestamp",
timestamp));
oauthHeaderParams.add(new SimpleRequestParam("oauth_nonce", nonce));
oauthHeaderParams.add(new SimpleRequestParam("oauth_version", "1.0"));
oauthHeaderParams.add(new SimpleRequestParam("x_auth_username",
username));
oauthHeaderParams.add(new SimpleRequestParam("x_auth_password",
password));
oauthHeaderParams.add(new SimpleRequestParam("x_auth_mode",
"client_auth"));
final StringBuffer base = new StringBuffer(method)
.append("&")
.append(OAuthHelper.encode(OAuthHelper.constructRequestURL(url)))
.append("&");
base.append(OAuthHelper.encode(OAuthHelper
.alignParams(oauthHeaderParams)));
final String oauthBaseString = base.toString();
final SecretKeySpec spec = OAuthHelper.getSecretKeySpec(provider, null);
final String signature = OAuthHelper
.getSignature(oauthBaseString, spec);
oauthHeaderParams.add(new SimpleRequestParam("oauth_signature",
signature));
return "OAuth "
+ OAuthHelper.encodeParameters(oauthHeaderParams, ",", true);
}
private static String constructRequestURL(String url) {
final int index = url.indexOf("?");
if (-1 != index) {
url = url.substring(0, index);
}
final int slashIndex = url.indexOf("/", 8);
final String baseURL = url.substring(0, slashIndex).toLowerCase();
url = baseURL + url.substring(slashIndex);
if (AppContext.DEBUG) {
Log.d(OAuthHelper.TAG, "constructRequestURL result=" + url);
}
return url;
}
static long createNonce() {
return (System.currentTimeMillis() / 1000) + OAuthHelper.RAND.nextInt();
}
static long createTimestamp() {
return System.currentTimeMillis() / 1000;
}
static String encode(final String value) {
String encoded = null;
try {
encoded = URLEncoder.encode(value, "UTF-8");
} catch (final UnsupportedEncodingException ignore) {
}
if (!TextUtils.isEmpty(encoded)) {
final StringBuilder buf = new StringBuilder(encoded.length());
char focus;
for (int i = 0; i < encoded.length(); i++) {
focus = encoded.charAt(i);
if (focus == '*') {
buf.append("%2A");
} else if (focus == '+') {
buf.append("%20");
} else if ((focus == '%') && ((i + 1) < encoded.length())
&& (encoded.charAt(i + 1) == '7')
&& (encoded.charAt(i + 2) == 'E')) {
buf.append('~');
i += 2;
} else {
buf.append(focus);
}
}
return buf.toString();
}
return value;
}
private static String encodeParameters(
final List<SimpleRequestParam> httpParams) {
return OAuthHelper.encodeParameters(httpParams, "&", false);
}
private static String encodeParameters(
final List<SimpleRequestParam> httpParams, final String splitter,
final boolean quot) {
final StringBuffer buf = new StringBuffer();
for (final SimpleRequestParam param : httpParams) {
if (!param.isFile()) {
if (buf.length() != 0) {
if (quot) {
buf.append("\"");
}
buf.append(splitter);
}
buf.append(OAuthHelper.encode(param.getName())).append("=");
if (quot) {
buf.append("\"");
}
buf.append(OAuthHelper.encode(param.getValue()));
}
}
if (buf.length() != 0) {
if (quot) {
buf.append("\"");
}
}
return buf.toString();
}
static SecretKeySpec getSecretKeySpec(final OAuthConfig provider) {
final String oauthSignature = OAuthHelper.encode(provider
.getConsumerSercret()) + "&";
return new SecretKeySpec(oauthSignature.getBytes(),
OAuthHelper.HMAC_SHA1);
}
static SecretKeySpec getSecretKeySpec(final OAuthConfig provider,
final OAuthToken token) {
if (null == token) {
final String oauthSignature = OAuthHelper.encode(provider
.getConsumerSercret()) + "&";
return new SecretKeySpec(oauthSignature.getBytes(),
OAuthHelper.HMAC_SHA1);
} else {
final String oauthSignature = OAuthHelper.encode(provider
.getConsumerSercret())
+ "&"
+ OAuthHelper.encode(token.getTokenSecret());
return new SecretKeySpec(oauthSignature.getBytes(),
OAuthHelper.HMAC_SHA1);
}
}
private static String getSignature(final String data,
final SecretKeySpec spec) {
byte[] byteHMAC = null;
try {
final Mac mac = Mac.getInstance(OAuthHelper.HMAC_SHA1);
mac.init(spec);
byteHMAC = mac.doFinal(data.getBytes());
} catch (final InvalidKeyException ike) {
throw new AssertionError(ike);
} catch (final NoSuchAlgorithmException nsae) {
throw new AssertionError(nsae);
}
return Base64.encodeBytes(byteHMAC);
}
private static void parseGetParams(final String url,
final List<SimpleRequestParam> signatureBaseParams) {
final int queryStart = url.indexOf("?");
if (-1 != queryStart) {
final String[] queryStrs = url.substring(queryStart + 1).split("&");
try {
for (final String query : queryStrs) {
final String[] split = query.split("=");
if (split.length == 2) {
signatureBaseParams.add(new SimpleRequestParam(
URLDecoder.decode(split[0], "UTF-8"),
URLDecoder.decode(split[1], "UTF-8")));
} else {
signatureBaseParams.add(new SimpleRequestParam(
URLDecoder.decode(split[0], "UTF-8"), ""));
}
}
} catch (final UnsupportedEncodingException ignore) {
}
}
}
}