Index: 3rdParty_sources/oauth-provider/net/oauth/OAuthValidator.java =================================================================== diff -u --- 3rdParty_sources/oauth-provider/net/oauth/OAuthValidator.java (revision 0) +++ 3rdParty_sources/oauth-provider/net/oauth/OAuthValidator.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Google, Inc. + * + * 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 net.oauth; + +import java.io.IOException; +import java.net.URISyntaxException; + +//TODO: move this interface into oauth-provider +/** + * An algorithm to determine whether a message has a valid signature, a correct + * version number, a fresh timestamp, etc. + * + * @author Dirk Balfanz + * @author John Kristian + */ +public interface OAuthValidator { + + /** + * Check that the given message from the given accessor is valid. + * + * @throws OAuthException + * the message doesn't conform to OAuth. The exception contains + * information that conforms to the OAuth Problem + * Reporting extension. + * @throws IOException + * the message couldn't be read. + * @throws URISyntaxException + * the message URL is invalid. + */ + public void validateMessage(OAuthMessage message, OAuthAccessor accessor) + throws OAuthException, IOException, URISyntaxException; + +} Index: 3rdParty_sources/oauth-provider/net/oauth/SimpleOAuthValidator.java =================================================================== diff -u --- 3rdParty_sources/oauth-provider/net/oauth/SimpleOAuthValidator.java (revision 0) +++ 3rdParty_sources/oauth-provider/net/oauth/SimpleOAuthValidator.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,348 @@ +/* + * Copyright 2008 Google, Inc. + * + * 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 net.oauth; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.log4j.Logger; + +import net.oauth.signature.OAuthSignatureMethod; + +//TODO: move this class into oauth-provider +/** + * A simple OAuthValidator, which checks the version, whether the timestamp is + * close to now, the nonce hasn't been used before and the signature is valid. + * Each check may be overridden. + *

+ * This implementation is less than industrial strength: + *

+ * For a big service provider, it might be better to store used nonces in a + * database. + * + * @author Dirk Balfanz + * @author John Kristian + */ +public class SimpleOAuthValidator implements OAuthValidator { + + /** The default maximum age of timestamps is 5 minutes. */ + public static final long DEFAULT_MAX_TIMESTAMP_AGE = 5 * 60 * 1000L; + public static final long DEFAULT_TIMESTAMP_WINDOW = DEFAULT_MAX_TIMESTAMP_AGE; + + private static Logger log = Logger.getLogger(SimpleOAuthValidator.class); + + /** + * Names of parameters that may not appear twice in a valid message. + * This limitation is specified by OAuth Core section 5. + */ + public static final Set SINGLE_PARAMETERS = constructSingleParameters(); + + private static Set constructSingleParameters() { + Set s = new HashSet(); + for (String p : new String[] { OAuth.OAUTH_CONSUMER_KEY, OAuth.OAUTH_TOKEN, OAuth.OAUTH_TOKEN_SECRET, + OAuth.OAUTH_CALLBACK, OAuth.OAUTH_SIGNATURE_METHOD, OAuth.OAUTH_SIGNATURE, OAuth.OAUTH_TIMESTAMP, + OAuth.OAUTH_NONCE, OAuth.OAUTH_VERSION }) { + s.add(p); + } + return Collections.unmodifiableSet(s); + } + + /** + * Construct a validator that rejects messages more than five minutes old or + * with a OAuth version other than 1.0. + */ + public SimpleOAuthValidator() { + this(DEFAULT_TIMESTAMP_WINDOW, Double.parseDouble(OAuth.VERSION_1_0)); + } + + /** + * Public constructor. + * + * @param maxTimestampAgeMsec + * the range of valid timestamps, in milliseconds into the past + * or future. So the total range of valid timestamps is twice + * this value, rounded to the nearest second. + * @param maxVersion + * the maximum valid oauth_version + */ + public SimpleOAuthValidator(long maxTimestampAgeMsec, double maxVersion) { + this.maxTimestampAgeMsec = maxTimestampAgeMsec; + this.maxVersion = maxVersion; + } + + protected final double minVersion = 1.0; + protected final double maxVersion; + protected final long maxTimestampAgeMsec; + private final Set usedNonces = new TreeSet(); + + /** + * Allow objects that are no longer useful to become garbage. + * + * @return the earliest point in time at which another call will release + * some garbage, or null to indicate there's nothing currently + * stored that will become garbage in future. This value may change, + * each time releaseGarbage or validateNonce is called. + */ + public Date releaseGarbage() { + return removeOldNonces(currentTimeMsec()); + } + + /** + * Remove usedNonces with timestamps that are too old to be valid. + */ + private Date removeOldNonces(long currentTimeMsec) { + UsedNonce next = null; + UsedNonce min = new UsedNonce((currentTimeMsec - maxTimestampAgeMsec + 500) / 1000L); + synchronized (usedNonces) { + // Because usedNonces is a TreeSet, its iterator produces + // elements from oldest to newest (their natural order). + for (Iterator iter = usedNonces.iterator(); iter.hasNext();) { + UsedNonce used = iter.next(); + if (min.compareTo(used) <= 0) { + next = used; + break; // all the rest are also new enough + } + iter.remove(); // too old + } + } + if (next == null) + return null; + return new Date((next.getTimestamp() * 1000L) + maxTimestampAgeMsec + 500); + } + + /** {@inherit} + * @throws URISyntaxException */ + public void validateMessage(OAuthMessage message, OAuthAccessor accessor) + throws OAuthException, IOException, URISyntaxException { + checkSingleParameters(message); + validateVersion(message); + validateTimestampAndNonce(message); + validateSignature(message, accessor); + } + + /** Throw an exception if any SINGLE_PARAMETERS occur repeatedly. */ + protected void checkSingleParameters(OAuthMessage message) throws IOException, OAuthException { + // Check for repeated oauth_ parameters: + boolean repeated = false; + Map> nameToValues = new HashMap>(); + for (Map.Entry parameter : message.getParameters()) { + String name = parameter.getKey(); + if (SINGLE_PARAMETERS.contains(name)) { + Collection values = nameToValues.get(name); + if (values == null) { + values = new ArrayList(); + nameToValues.put(name, values); + } else { + repeated = true; + } + values.add(parameter.getValue()); + } + } + if (repeated) { + Collection rejected = new ArrayList(); + for (Map.Entry> p : nameToValues.entrySet()) { + String name = p.getKey(); + Collection values = p.getValue(); + if (values.size() > 1) { + for (String value : values) { + rejected.add(new OAuth.Parameter(name, value)); + } + } + } + OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_REJECTED); + problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_REJECTED, OAuth.formEncode(rejected)); + throw problem; + } + } + + protected void validateVersion(OAuthMessage message) + throws OAuthException, IOException { + String versionString = message.getParameter(OAuth.OAUTH_VERSION); + if (versionString != null) { + double version = Double.parseDouble(versionString); + if (version < minVersion || maxVersion < version) { + // *LAMS* added by LAMS + log.debug("Error. oauth_version parameter (" + version + ") must be greater than minVersion=" + + minVersion + " and less than maxVersion=" + maxVersion); + + OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.VERSION_REJECTED); + problem.setParameter(OAuth.Problems.OAUTH_ACCEPTABLE_VERSIONS, minVersion + "-" + maxVersion); + throw problem; + } + } + } + + /** + * Throw an exception if the timestamp is out of range or the nonce has been + * validated previously. + */ + protected void validateTimestampAndNonce(OAuthMessage message) + throws IOException, OAuthProblemException { + message.requireParameters(OAuth.OAUTH_TIMESTAMP, OAuth.OAUTH_NONCE); + long timestamp = Long.parseLong(message.getParameter(OAuth.OAUTH_TIMESTAMP)); + long now = currentTimeMsec(); + validateTimestamp(message, timestamp, now); + validateNonce(message, timestamp, now); + } + + /** Throw an exception if the timestamp [sec] is out of range. */ + protected void validateTimestamp(OAuthMessage message, long timestamp, long currentTimeMsec) throws IOException, + OAuthProblemException { + long min = (currentTimeMsec - maxTimestampAgeMsec + 500) / 1000L; + long max = (currentTimeMsec + maxTimestampAgeMsec + 500) / 1000L; + if (timestamp < min || max < timestamp) { + + // *LAMS* added by LAMS + log.debug("Error. oauth_timestamp parameter (" + timestamp + ") must be greater than min=" + min + + " and less than max=" + max); + + OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.TIMESTAMP_REFUSED); + problem.setParameter(OAuth.Problems.OAUTH_ACCEPTABLE_TIMESTAMPS, min + "-" + max); + throw problem; + } + } + + /** + * Throw an exception if the nonce has been validated previously. + * + * @return the earliest point in time at which a call to releaseGarbage + * will actually release some garbage, or null to indicate there's + * nothing currently stored that will become garbage in future. + */ + protected Date validateNonce(OAuthMessage message, long timestamp, long currentTimeMsec) throws IOException, + OAuthProblemException { + UsedNonce nonce = new UsedNonce(timestamp, + message.getParameter(OAuth.OAUTH_NONCE), message.getConsumerKey(), message.getToken()); + /* + * The OAuth standard requires the token to be omitted from the stored + * nonce. But I include it, to harmonize with a Consumer that generates + * nonces using several independent computers, each with its own token. + */ + boolean valid = false; + synchronized (usedNonces) { + valid = usedNonces.add(nonce); + } + if (!valid) { + + // *LAMS* added by LAMS + log.debug("Error. Set of usedNonces already contains newly constructed nonce (" + message.getParameter(OAuth.OAUTH_NONCE) + ")"); + + throw new OAuthProblemException(OAuth.Problems.NONCE_USED); + } + return removeOldNonces(currentTimeMsec); + } + + protected void validateSignature(OAuthMessage message, OAuthAccessor accessor) + throws OAuthException, IOException, URISyntaxException { + message.requireParameters(OAuth.OAUTH_CONSUMER_KEY, + OAuth.OAUTH_SIGNATURE_METHOD, OAuth.OAUTH_SIGNATURE); + OAuthSignatureMethod.newSigner(message, accessor).validate(message); + } + + /** Get the number of milliseconds since midnight, January 1, 1970 UTC. */ + protected long currentTimeMsec() { + return System.currentTimeMillis(); + } + + /** + * Selected parameters from an OAuth request, in a form suitable for + * detecting duplicate requests. The implementation is optimized for the + * comparison operations (compareTo, equals and hashCode). + * + * @author John Kristian + */ + private static class UsedNonce implements Comparable { + /** + * Construct an object containing the given timestamp, nonce and other + * parameters. The order of parameters is significant. + */ + UsedNonce(long timestamp, String... nonceEtc) { + StringBuilder key = new StringBuilder(String.format("%20d", Long.valueOf(timestamp))); + // The blank padding ensures that timestamps are compared as numbers. + for (String etc : nonceEtc) { + key.append("&").append(etc == null ? " " : OAuth.percentEncode(etc)); + // A null value is different from "" or any other String. + } + sortKey = key.toString(); + } + + private final String sortKey; + + long getTimestamp() { + int end = sortKey.indexOf("&"); + if (end < 0) + end = sortKey.length(); + return Long.parseLong(sortKey.substring(0, end).trim()); + } + + /** + * Determine the relative order of this and + * that, as specified by Comparable. The timestamp is most + * significant; that is, if the timestamps are different, return 1 or + * -1. If this contains only a timestamp (with no nonce + * etc.), return -1 or 0. The treatment of the nonce etc. is murky, + * although 0 is returned only if they're all equal. + */ + public int compareTo(UsedNonce that) { + return (that == null) ? 1 : sortKey.compareTo(that.sortKey); + } + + @Override + public int hashCode() { + return sortKey.hashCode(); + } + + /** + * Return true iff this and that contain equal + * timestamps, nonce etc., in the same order. + */ + @Override + public boolean equals(Object that) { + if (that == null) + return false; + if (that == this) + return true; + if (that.getClass() != getClass()) + return false; + return sortKey.equals(((UsedNonce) that).sortKey); + } + + @Override + public String toString() { + return sortKey; + } + } +} Index: 3rdParty_sources/oauth-provider/net/oauth/server/HttpRequestMessage.java =================================================================== diff -u --- 3rdParty_sources/oauth-provider/net/oauth/server/HttpRequestMessage.java (revision 0) +++ 3rdParty_sources/oauth-provider/net/oauth/server/HttpRequestMessage.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,92 @@ +/* + * Copyright 2008 Netflix, Inc. + * + * 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 net.oauth.server; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import net.oauth.OAuth; +import net.oauth.OAuthMessage; + +/** + * An HttpServletRequest, encapsulated as an OAuthMessage. + * + * @author John Kristian + */ +public class HttpRequestMessage extends OAuthMessage { + + public HttpRequestMessage(HttpServletRequest request, String URL) { + super(request.getMethod(), URL, getParameters(request)); + this.request = request; + copyHeaders(request, getHeaders()); + } + + private final HttpServletRequest request; + + @Override + public InputStream getBodyAsStream() throws IOException { + return request.getInputStream(); + } + + @Override + public String getBodyEncoding() { + return request.getCharacterEncoding(); + } + + private static void copyHeaders(HttpServletRequest request, Collection> into) { + Enumeration names = request.getHeaderNames(); + if (names != null) { + while (names.hasMoreElements()) { + String name = names.nextElement(); + Enumeration values = request.getHeaders(name); + if (values != null) { + while (values.hasMoreElements()) { + into.add(new OAuth.Parameter(name, values.nextElement())); + } + } + } + } + } + + public static List getParameters(HttpServletRequest request) { + List list = new ArrayList(); + for (Enumeration headers = request.getHeaders("Authorization"); headers != null + && headers.hasMoreElements();) { + String header = headers.nextElement(); + for (OAuth.Parameter parameter : OAuthMessage + .decodeAuthorization(header)) { + if (!"realm".equalsIgnoreCase(parameter.getKey())) { + list.add(parameter); + } + } + } + for (Object e : request.getParameterMap().entrySet()) { + Map.Entry entry = (Map.Entry) e; + String name = entry.getKey(); + for (String value : entry.getValue()) { + list.add(new OAuth.Parameter(name, value)); + } + } + return list; + } + +} Index: 3rdParty_sources/oauth-provider/net/oauth/server/OAuthServlet.java =================================================================== diff -u --- 3rdParty_sources/oauth-provider/net/oauth/server/OAuthServlet.java (revision 0) +++ 3rdParty_sources/oauth-provider/net/oauth/server/OAuthServlet.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,157 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth.server; + +import java.io.IOException; +import java.util.Map; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import net.oauth.OAuth; +import net.oauth.OAuthMessage; +import net.oauth.OAuthProblemException; + +/** + * Utility methods for servlets that implement OAuth. + * + * @author John Kristian + */ +public class OAuthServlet { + + /** + * Extract the parts of the given request that are relevant to OAuth. + * Parameters include OAuth Authorization headers and the usual request + * parameters in the query string and/or form encoded body. The header + * parameters come first, followed by the rest in the order they came from + * request.getParameterMap(). + * + * @param URL + * the official URL of this service; that is the URL a legitimate + * client would use to compute the digital signature. If this + * parameter is null, this method will try to reconstruct the URL + * from the HTTP request; which may be wrong in some cases. + */ + public static OAuthMessage getMessage(HttpServletRequest request, String URL) { + if (URL == null) { + URL = request.getRequestURL().toString(); + } + int q = URL.indexOf('?'); + if (q >= 0) { + URL = URL.substring(0, q); + // The query string parameters will be included in + // the result from getParameters(request). + } + return new HttpRequestMessage(request, URL); + } + + /** Reconstruct the requested URL, complete with query string (if any). */ + public static String getRequestURL(HttpServletRequest request) { + StringBuffer url = request.getRequestURL(); + String queryString = request.getQueryString(); + if (queryString != null) { + url.append("?").append(queryString); + } + return url.toString(); + } + + public static void handleException(HttpServletResponse response, + Exception e, String realm) throws IOException, ServletException { + handleException(response, e, realm, true); + } + + public static void handleException(HttpServletResponse response, + Exception e, String realm, boolean sendBody) throws IOException, + ServletException { + if (e instanceof OAuthProblemException) { + OAuthProblemException problem = (OAuthProblemException) e; + Object httpCode = problem.getParameters().get(OAuthProblemException.HTTP_STATUS_CODE); + if (httpCode == null) { + httpCode = PROBLEM_TO_HTTP_CODE.get(problem.getProblem()); + } + if (httpCode == null) { + httpCode = SC_FORBIDDEN; + } + response.reset(); + response.setStatus(Integer.parseInt(httpCode.toString())); + OAuthMessage message = new OAuthMessage(null, null, problem + .getParameters().entrySet()); + response.addHeader("WWW-Authenticate", message + .getAuthorizationHeader(realm)); + if (sendBody) { + sendForm(response, message.getParameters()); + } + } else if (e instanceof IOException) { + throw (IOException) e; + } else if (e instanceof ServletException) { + throw (ServletException) e; + } else if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else { + throw new ServletException(e); + } + } + + private static final Integer SC_FORBIDDEN = new Integer( + HttpServletResponse.SC_FORBIDDEN); + + private static final Map PROBLEM_TO_HTTP_CODE = OAuth.Problems.TO_HTTP_CODE; + + /** Send the given parameters as a form-encoded response body. */ + public static void sendForm(HttpServletResponse response, + Iterable parameters) throws IOException { + response.resetBuffer(); + response.setContentType(OAuth.FORM_ENCODED + ";charset=" + + OAuth.ENCODING); + OAuth.formEncode(parameters, response.getOutputStream()); + } + + /** + * Return the HTML representation of the given plain text. Characters that + * would have special significance in HTML are replaced by character entity + * references. Whitespace is not converted. + */ + public static String htmlEncode(String s) { + if (s == null) { + return null; + } + StringBuilder html = new StringBuilder(s.length()); + for (char c : s.toCharArray()) { + switch (c) { + case '<': + html.append("<"); + break; + case '>': + html.append(">"); + break; + case '&': + html.append("&"); + // This also takes care of numeric character references; + // for example © becomes &#169. + break; + case '"': + html.append("""); + break; + default: + html.append(c); + break; + } + } + return html.toString(); + } + +} Index: 3rdParty_sources/oauth-provider/net/oauth/server/package-info.java =================================================================== diff -u --- 3rdParty_sources/oauth-provider/net/oauth/server/package-info.java (revision 0) +++ 3rdParty_sources/oauth-provider/net/oauth/server/package-info.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,6 @@ +/** + * Classes for implementing an OAuth Service Provider; that is + * a server that controls access to protected resources. + */ +package net.oauth.server; + Index: 3rdParty_sources/oauth/net/oauth/ConsumerProperties.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/ConsumerProperties.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/ConsumerProperties.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,130 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * A pool of OAuthConsumers that are constructed from Properties. Each consumer + * has a name, which is a property of the OAuthConsumer. Other properties come + * from Properties whose names are prefixed with the consumer's name. For + * example, a consumer's credentials come from properties named + * [name].consumerKey and [name].consumerSecret. + * + * @author John Kristian + */ +public class ConsumerProperties { + + public static URL getResource(String name, ClassLoader loader) + throws IOException { + URL resource = loader.getResource(name); + if (resource == null) { + throw new IOException("resource not found: " + name); + } + return resource; + } + + public static Properties getProperties(URL source) throws IOException { + InputStream input = source.openStream(); + try { + Properties p = new Properties(); + p.load(input); + return p; + } finally { + input.close(); + } + } + + public ConsumerProperties(String resourceName, ClassLoader loader) + throws IOException { + this(getProperties(getResource(resourceName, loader))); + } + + public ConsumerProperties(Properties consumerProperties) { + this.consumerProperties = consumerProperties; + } + + private final Properties consumerProperties; + + private final Map pool = new HashMap(); + + /** Get the consumer with the given name. */ + public OAuthConsumer getConsumer(String name) throws MalformedURLException { + OAuthConsumer consumer; + synchronized (pool) { + consumer = pool.get(name); + } + if (consumer == null) { + consumer = newConsumer(name); + } + synchronized (pool) { + OAuthConsumer first = pool.get(name); + if (first == null) { + pool.put(name, consumer); + } else { + /* + * Another thread just constructed an identical OAuthConsumer. + * Use that one (and discard the one we just constructed). + */ + consumer = first; + } + } + return consumer; + } + + protected OAuthConsumer newConsumer(String name) + throws MalformedURLException { + String base = consumerProperties.getProperty(name + + ".serviceProvider.baseURL"); + URL baseURL = (base == null) ? null : new URL(base); + OAuthServiceProvider serviceProvider = new OAuthServiceProvider(getURL( + baseURL, name + ".serviceProvider.requestTokenURL"), getURL( + baseURL, name + ".serviceProvider.userAuthorizationURL"), + getURL(baseURL, name + ".serviceProvider.accessTokenURL")); + OAuthConsumer consumer = new OAuthConsumer(consumerProperties + .getProperty(name + ".callbackURL"), consumerProperties + .getProperty(name + ".consumerKey"), consumerProperties + .getProperty(name + ".consumerSecret"), serviceProvider); + consumer.setProperty("name", name); + if (baseURL != null) { + consumer.setProperty("serviceProvider.baseURL", baseURL); + } + for (Map.Entry prop : consumerProperties.entrySet()) { + String propName = (String) prop.getKey(); + if (propName.startsWith(name + ".consumer.")) { + String c = propName.substring(name.length() + 10); + consumer.setProperty(c, prop.getValue()); + } + } + return consumer; + } + + private String getURL(URL base, String name) throws MalformedURLException { + String url = consumerProperties.getProperty(name); + if (base != null) { + url = (new URL(base, url)).toExternalForm(); + } + return url; + } + +} Index: 3rdParty_sources/oauth/net/oauth/OAuth.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuth.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuth.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,381 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Miscellaneous constants, methods and types. + * + * @author John Kristian + */ +public class OAuth { + + public static final String VERSION_1_0 = "1.0"; + + /** The encoding used to represent characters as bytes. */ + public static final String ENCODING = "UTF-8"; + + /** The MIME type for a sequence of OAuth parameters. */ + public static final String FORM_ENCODED = "application/x-www-form-urlencoded"; + + public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key"; + public static final String OAUTH_TOKEN = "oauth_token"; + public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret"; + public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method"; + public static final String OAUTH_SIGNATURE = "oauth_signature"; + public static final String OAUTH_TIMESTAMP = "oauth_timestamp"; + public static final String OAUTH_NONCE = "oauth_nonce"; + public static final String OAUTH_VERSION = "oauth_version"; + public static final String OAUTH_CALLBACK = "oauth_callback"; + public static final String OAUTH_CALLBACK_CONFIRMED = "oauth_callback_confirmed"; + public static final String OAUTH_VERIFIER = "oauth_verifier"; + + public static final String HMAC_SHA1 = "HMAC-SHA1"; + public static final String RSA_SHA1 = "RSA-SHA1"; + + /** + * Strings used for problem + * reporting. + */ + public static class Problems { + public static final String VERSION_REJECTED = "version_rejected"; + public static final String PARAMETER_ABSENT = "parameter_absent"; + public static final String PARAMETER_REJECTED = "parameter_rejected"; + public static final String TIMESTAMP_REFUSED = "timestamp_refused"; + public static final String NONCE_USED = "nonce_used"; + public static final String SIGNATURE_METHOD_REJECTED = "signature_method_rejected"; + public static final String SIGNATURE_INVALID = "signature_invalid"; + public static final String CONSUMER_KEY_UNKNOWN = "consumer_key_unknown"; + public static final String CONSUMER_KEY_REJECTED = "consumer_key_rejected"; + public static final String CONSUMER_KEY_REFUSED = "consumer_key_refused"; + public static final String TOKEN_USED = "token_used"; + public static final String TOKEN_EXPIRED = "token_expired"; + public static final String TOKEN_REVOKED = "token_revoked"; + public static final String TOKEN_REJECTED = "token_rejected"; + public static final String ADDITIONAL_AUTHORIZATION_REQUIRED = "additional_authorization_required"; + public static final String PERMISSION_UNKNOWN = "permission_unknown"; + public static final String PERMISSION_DENIED = "permission_denied"; + public static final String USER_REFUSED = "user_refused"; + + public static final String OAUTH_ACCEPTABLE_VERSIONS = "oauth_acceptable_versions"; + public static final String OAUTH_ACCEPTABLE_TIMESTAMPS = "oauth_acceptable_timestamps"; + public static final String OAUTH_PARAMETERS_ABSENT = "oauth_parameters_absent"; + public static final String OAUTH_PARAMETERS_REJECTED = "oauth_parameters_rejected"; + public static final String OAUTH_PROBLEM_ADVICE = "oauth_problem_advice"; + + /** + * A map from an oauth_problem value to + * the appropriate HTTP response code. + */ + public static final Map TO_HTTP_CODE = mapToHttpCode(); + + private static Map mapToHttpCode() { + Integer badRequest = new Integer(400); + Integer unauthorized = new Integer(401); + Integer serviceUnavailable = new Integer(503); + Map map = new HashMap(); + + map.put(Problems.VERSION_REJECTED, badRequest); + map.put(Problems.PARAMETER_ABSENT, badRequest); + map.put(Problems.PARAMETER_REJECTED, badRequest); + map.put(Problems.TIMESTAMP_REFUSED, badRequest); + map.put(Problems.SIGNATURE_METHOD_REJECTED, badRequest); + + map.put(Problems.NONCE_USED, unauthorized); + map.put(Problems.TOKEN_USED, unauthorized); + map.put(Problems.TOKEN_EXPIRED, unauthorized); + map.put(Problems.TOKEN_REVOKED, unauthorized); + map.put(Problems.TOKEN_REJECTED, unauthorized); + map.put("token_not_authorized", unauthorized); + map.put(Problems.SIGNATURE_INVALID, unauthorized); + map.put(Problems.CONSUMER_KEY_UNKNOWN, unauthorized); + map.put(Problems.CONSUMER_KEY_REJECTED, unauthorized); + map.put(Problems.ADDITIONAL_AUTHORIZATION_REQUIRED, unauthorized); + map.put(Problems.PERMISSION_UNKNOWN, unauthorized); + map.put(Problems.PERMISSION_DENIED, unauthorized); + + map.put(Problems.USER_REFUSED, serviceUnavailable); + map.put(Problems.CONSUMER_KEY_REFUSED, serviceUnavailable); + return Collections.unmodifiableMap(map); + } + + } + + private static String characterEncoding = ENCODING; + + public static void setCharacterEncoding(String encoding) { + OAuth.characterEncoding = encoding; + } + + public static String decodeCharacters(byte[] from) { + if (characterEncoding != null) { + try { + return new String(from, characterEncoding); + } catch (UnsupportedEncodingException e) { + System.err.println(e + ""); + } + } + return new String(from); + } + + public static byte[] encodeCharacters(String from) { + if (characterEncoding != null) { + try { + return from.getBytes(characterEncoding); + } catch (UnsupportedEncodingException e) { + System.err.println(e + ""); + } + } + return from.getBytes(); + } + + /** Return true if the given Content-Type header means FORM_ENCODED. */ + public static boolean isFormEncoded(String contentType) { + if (contentType == null) { + return false; + } + int semi = contentType.indexOf(";"); + if (semi >= 0) { + contentType = contentType.substring(0, semi); + } + return FORM_ENCODED.equalsIgnoreCase(contentType.trim()); + } + + /** + * Construct a form-urlencoded document containing the given sequence of + * name/value pairs. Use OAuth percent encoding (not exactly the encoding + * mandated by HTTP). + */ + public static String formEncode(Iterable parameters) + throws IOException { + ByteArrayOutputStream b = new ByteArrayOutputStream(); + formEncode(parameters, b); + return decodeCharacters(b.toByteArray()); + } + + /** + * Write a form-urlencoded document into the given stream, containing the + * given sequence of name/value pairs. + */ + public static void formEncode(Iterable parameters, + OutputStream into) throws IOException { + if (parameters != null) { + boolean first = true; + for (Map.Entry parameter : parameters) { + if (first) { + first = false; + } else { + into.write('&'); + } + into.write(encodeCharacters(percentEncode(toString(parameter.getKey())))); + into.write('='); + into.write(encodeCharacters(percentEncode(toString(parameter.getValue())))); + } + } + } + + /** Parse a form-urlencoded document. */ + public static List decodeForm(String form) { + List list = new ArrayList(); + if (!isEmpty(form)) { + for (String nvp : form.split("\\&")) { + int equals = nvp.indexOf('='); + String name; + String value; + if (equals < 0) { + name = decodePercent(nvp); + value = null; + } else { + name = decodePercent(nvp.substring(0, equals)); + value = decodePercent(nvp.substring(equals + 1)); + } + list.add(new Parameter(name, value)); + } + } + return list; + } + + /** Construct a &-separated list of the given values, percentEncoded. */ + public static String percentEncode(Iterable values) { + StringBuilder p = new StringBuilder(); + for (Object v : values) { + if (p.length() > 0) { + p.append("&"); + } + p.append(OAuth.percentEncode(toString(v))); + } + return p.toString(); + } + + public static String percentEncode(String s) { + if (s == null) { + return ""; + } + try { + return URLEncoder.encode(s, ENCODING) + // OAuth encodes some characters differently: + .replace("+", "%20").replace("*", "%2A") + .replace("%7E", "~"); + // This could be done faster with more hand-crafted code. + } catch (UnsupportedEncodingException wow) { + throw new RuntimeException(wow.getMessage(), wow); + } + } + + public static String decodePercent(String s) { + try { + return URLDecoder.decode(s, ENCODING); + // This implements http://oauth.pbwiki.com/FlexibleDecoding + } catch (java.io.UnsupportedEncodingException wow) { + throw new RuntimeException(wow.getMessage(), wow); + } + } + + /** + * Construct a Map containing a copy of the given parameters. If several + * parameters have the same name, the Map will contain the first value, + * only. + */ + public static Map newMap(Iterable from) { + Map map = new HashMap(); + if (from != null) { + for (Map.Entry f : from) { + String key = toString(f.getKey()); + if (!map.containsKey(key)) { + map.put(key, toString(f.getValue())); + } + } + } + return map; + } + + /** Construct a list of Parameters from name, value, name, value... */ + public static List newList(String... parameters) { + List list = new ArrayList(parameters.length / 2); + for (int p = 0; p + 1 < parameters.length; p += 2) { + list.add(new Parameter(parameters[p], parameters[p + 1])); + } + return list; + } + + /** A name/value pair. */ + public static class Parameter implements Map.Entry { + + public Parameter(String key, String value) { + this.key = key; + this.value = value; + } + + private final String key; + + private String value; + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public String setValue(String value) { + try { + return this.value; + } finally { + this.value = value; + } + } + + @Override + public String toString() { + return percentEncode(getKey()) + '=' + percentEncode(getValue()); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Parameter that = (Parameter) obj; + if (key == null) { + if (that.key != null) + return false; + } else if (!key.equals(that.key)) + return false; + if (value == null) { + if (that.value != null) + return false; + } else if (!value.equals(that.value)) + return false; + return true; + } + } + + private static final String toString(Object from) { + return (from == null) ? null : from.toString(); + } + + /** + * Construct a URL like the given one, but with the given parameters added + * to its query string. + */ + public static String addParameters(String url, String... parameters) + throws IOException { + return addParameters(url, newList(parameters)); + } + + public static String addParameters(String url, + Iterable> parameters) + throws IOException { + String form = formEncode(parameters); + if (form == null || form.length() <= 0) { + return url; + } else { + return url + ((url.indexOf("?") < 0) ? '?' : '&') + form; + } + } + + public static boolean isEmpty(String str) { + return (str == null) || (str.length() == 0); + } +} Index: 3rdParty_sources/oauth/net/oauth/OAuthAccessor.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuthAccessor.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuthAccessor.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,100 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URISyntaxException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Properties of one User of an OAuthConsumer. Properties may be added freely, + * e.g. to support extensions. + * + * @author John Kristian + */ +public class OAuthAccessor implements Cloneable, Serializable { + + private static final long serialVersionUID = 5590788443138352999L; + + public final OAuthConsumer consumer; + public String requestToken; + public String accessToken; + public String tokenSecret; + + public OAuthAccessor(OAuthConsumer consumer) { + this.consumer = consumer; + this.requestToken = null; + this.accessToken = null; + this.tokenSecret = null; + } + + private final Map properties = new HashMap(); + + @Override + public OAuthAccessor clone() { + try { + return (OAuthAccessor) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + public Object getProperty(String name) { + return properties.get(name); + } + + public void setProperty(String name, Object value) { + properties.put(name, value); + } + + /** + * Construct a request message containing the given parameters but no body. + * Don't send the message, merely construct it. The caller will ordinarily + * send it, for example by calling OAuthClient.invoke or access. + * + * @param method + * the HTTP request method. If this is null, use the default + * method; that is getProperty("httpMethod") or (if that's null) + * consumer.getProperty("httpMethod") or (if that's null) + * OAuthMessage.GET. + */ + public OAuthMessage newRequestMessage(String method, String url, Collection parameters, + InputStream body) throws OAuthException, IOException, URISyntaxException { + if (method == null) { + method = (String) this.getProperty("httpMethod"); + if (method == null) { + method = (String) this.consumer.getProperty("httpMethod"); + if (method == null) { + method = OAuthMessage.GET; + } + } + } + OAuthMessage message = new OAuthMessage(method, url, parameters, body); + message.addRequiredParameters(this); + return message; + } + + public OAuthMessage newRequestMessage(String method, String url, Collection parameters) + throws OAuthException, IOException, URISyntaxException { + return newRequestMessage(method, url, parameters, null); + } + +} Index: 3rdParty_sources/oauth/net/oauth/OAuthConsumer.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuthConsumer.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuthConsumer.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,68 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Properties of an OAuth Consumer. Properties may be added freely, e.g. to + * support extensions. + * + * @author John Kristian + */ +public class OAuthConsumer implements Serializable { + + private static final long serialVersionUID = -2258581186977818580L; + + public final String callbackURL; + public final String consumerKey; + public final String consumerSecret; + public final OAuthServiceProvider serviceProvider; + + public OAuthConsumer(String callbackURL, String consumerKey, + String consumerSecret, OAuthServiceProvider serviceProvider) { + this.callbackURL = callbackURL; + this.consumerKey = consumerKey; + this.consumerSecret = consumerSecret; + this.serviceProvider = serviceProvider; + } + + private final Map properties = new HashMap(); + + public Object getProperty(String name) { + return properties.get(name); + } + + public void setProperty(String name, Object value) { + properties.put(name, value); + } + + /** + * The name of the property whose value is the Accept-Encoding header in + * HTTP requests. + */ + public static final String ACCEPT_ENCODING = "HTTP.header.Accept-Encoding"; + + /** + * The name of the property whose value is the Accessor Secret. + */ + public static final String ACCESSOR_SECRET = "oauth_accessor_secret"; + +} Index: 3rdParty_sources/oauth/net/oauth/OAuthException.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuthException.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuthException.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Google, Inc. + * + * 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 net.oauth; + +/** + * An exception thrown by the OAuth library. + */ +public class OAuthException extends Exception { + + /** + * For subclasses only. + */ + protected OAuthException() { + } + + /** + * @param message + */ + public OAuthException(String message) { + super(message); + } + + /** + * @param cause + */ + public OAuthException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public OAuthException(String message, Throwable cause) { + super(message, cause); + } + + private static final long serialVersionUID = 1L; + +} Index: 3rdParty_sources/oauth/net/oauth/OAuthMessage.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuthMessage.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuthMessage.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,393 @@ +/* + * Copyright 2007, 2008 Netflix, Inc. + * + * 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 net.oauth; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; + +import net.oauth.signature.OAuthSignatureMethod; + +/** + * A request or response message used in the OAuth protocol. + *

+ * The parameters in this class are not percent-encoded. Methods like + * OAuthClient.invoke and OAuthResponseMessage.completeParameters are + * responsible for percent-encoding parameters before transmission and decoding + * them after reception. + * + * @author John Kristian + */ +public class OAuthMessage { + + private static Logger log = Logger.getLogger(OAuthMessage.class); + + public OAuthMessage(String method, String URL, Collection parameters) { + this(method, URL, parameters, null); + } + + public OAuthMessage(String method, String URL, Collection parameters, + InputStream bodyAsStream) { + this.method = method; + this.URL = URL; + this.bodyAsStream = bodyAsStream; + if (parameters == null) { + this.parameters = new ArrayList>(); + } else { + this.parameters = new ArrayList>(parameters.size()); + for (Map.Entry p : parameters) { + this.parameters.add(new OAuth.Parameter( + toString(p.getKey()), toString(p.getValue()))); + } + } + } + + public String method; + public String URL; + + private final List> parameters; + private Map parameterMap; + private boolean parametersAreComplete = false; + private final List> headers = new ArrayList>(); + private final InputStream bodyAsStream; + + public String toString() { + return "OAuthMessage(" + method + ", " + URL + ", " + parameters + ")"; + } + + /** A caller is about to get a parameter. */ + private void beforeGetParameter() throws IOException { + if (!parametersAreComplete) { + completeParameters(); + parametersAreComplete = true; + } + } + + /** + * Finish adding parameters; for example read an HTTP response body and + * parse parameters from it. + */ + protected void completeParameters() throws IOException { + } + + public List> getParameters() throws IOException { + beforeGetParameter(); + return Collections.unmodifiableList(parameters); + } + + public void addParameter(String key, String value) { + addParameter(new OAuth.Parameter(key, value)); + } + + public void addParameter(Map.Entry parameter) { + parameters.add(parameter); + parameterMap = null; + } + + public void addParameters( + Collection> parameters) { + this.parameters.addAll(parameters); + parameterMap = null; + } + + public String getParameter(String name) throws IOException { + return getParameterMap().get(name); + } + + public String getConsumerKey() throws IOException { + return getParameter(OAuth.OAUTH_CONSUMER_KEY); + } + + public String getToken() throws IOException { + return getParameter(OAuth.OAUTH_TOKEN); + } + + public String getSignatureMethod() throws IOException { + return getParameter(OAuth.OAUTH_SIGNATURE_METHOD); + } + + public String getSignature() throws IOException { + return getParameter(OAuth.OAUTH_SIGNATURE); + } + + protected Map getParameterMap() throws IOException { + beforeGetParameter(); + if (parameterMap == null) { + parameterMap = OAuth.newMap(parameters); + } + return parameterMap; + } + + /** + * The MIME type of the body of this message. + * + * @return the MIME type, or null to indicate the type is unknown. + */ + public String getBodyType() { + return getHeader("Content-Type"); + } + + /** + * The character encoding of the body of this message. + * + * @return the name of an encoding, or "ISO-8859-1" if no charset has been + * specified. + */ + public String getBodyEncoding() { + return "ISO-8859-1"; + } + + /** + * The value of the last HTTP header with the given name. The name is case + * insensitive. + * + * @return the value of the last header, or null to indicate that there is + * no such header in this message. + */ + public final String getHeader(String name) { + String value = null; // no such header + for (Map.Entry header : getHeaders()) { + if (name.equalsIgnoreCase(header.getKey())) { + value = header.getValue(); + } + } + return value; + } + + /** All HTTP headers. You can add headers to this list. */ + public final List> getHeaders() { + return headers; + } + + /** + * Read the body of the HTTP request or response and convert it to a String. + * This method isn't repeatable, since it consumes and closes getBodyAsStream. + * + * @return the body, or null to indicate there is no body. + */ + public final String readBodyAsString() throws IOException + { + InputStream body = getBodyAsStream(); + return readAll(body, getBodyEncoding()); + } + + /** + * Get a stream from which to read the body of the HTTP request or response. + * This is designed to support efficient streaming of a large message. + * The caller must close the returned stream, to release the underlying + * resources such as the TCP connection for an HTTP response. + * + * @return a stream from which to read the body, or null to indicate there + * is no body. + */ + public InputStream getBodyAsStream() throws IOException { + return bodyAsStream; + } + + /** Construct a verbose description of this message and its origins. */ + public Map getDump() throws IOException { + Map into = new HashMap(); + dump(into); + return into; + } + + protected void dump(Map into) throws IOException { + into.put(OAuthProblemException.URL, URL); + if (parametersAreComplete) { + try { + into.putAll(getParameterMap()); + } catch (Exception ignored) { + } + } + } + + /** + * Verify that the required parameter names are contained in the actual + * collection. + * + * @throws OAuthProblemException + * one or more parameters are absent. + * @throws IOException + */ + public void requireParameters(String... names) + throws OAuthProblemException, IOException { + Set present = getParameterMap().keySet(); + List absent = new ArrayList(); + for (String required : names) { + if (!present.contains(required)) { + absent.add(required); + } + } + if (!absent.isEmpty()) { + + // *LAMS* added by LAMS + log.debug("Error. Required parameter missing: " + OAuth.percentEncode(absent)); + + OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.PARAMETER_ABSENT); + problem.setParameter(OAuth.Problems.OAUTH_PARAMETERS_ABSENT, OAuth.percentEncode(absent)); + throw problem; + } + } + + /** + * Add some of the parameters needed to request access to a protected + * resource, if they aren't already in the message. + * + * @throws IOException + * @throws URISyntaxException + */ + public void addRequiredParameters(OAuthAccessor accessor) + throws OAuthException, IOException, URISyntaxException { + final Map pMap = OAuth.newMap(parameters); + if (pMap.get(OAuth.OAUTH_TOKEN) == null && accessor.accessToken != null) { + addParameter(OAuth.OAUTH_TOKEN, accessor.accessToken); + } + final OAuthConsumer consumer = accessor.consumer; + if (pMap.get(OAuth.OAUTH_CONSUMER_KEY) == null) { + addParameter(OAuth.OAUTH_CONSUMER_KEY, consumer.consumerKey); + } + String signatureMethod = pMap.get(OAuth.OAUTH_SIGNATURE_METHOD); + if (signatureMethod == null) { + signatureMethod = (String) consumer.getProperty(OAuth.OAUTH_SIGNATURE_METHOD); + if (signatureMethod == null) { + signatureMethod = OAuth.HMAC_SHA1; + } + addParameter(OAuth.OAUTH_SIGNATURE_METHOD, signatureMethod); + } + if (pMap.get(OAuth.OAUTH_TIMESTAMP) == null) { + addParameter(OAuth.OAUTH_TIMESTAMP, (System.currentTimeMillis() / 1000) + ""); + } + if (pMap.get(OAuth.OAUTH_NONCE) == null) { + addParameter(OAuth.OAUTH_NONCE, System.nanoTime() + ""); + } + if (pMap.get(OAuth.OAUTH_VERSION) == null) { + addParameter(OAuth.OAUTH_VERSION, OAuth.VERSION_1_0); + } + this.sign(accessor); + } + + /** + * Add a signature to the message. + * + * @throws URISyntaxException + */ + public void sign(OAuthAccessor accessor) throws IOException, + OAuthException, URISyntaxException { + OAuthSignatureMethod.newSigner(this, accessor).sign(this); + } + + /** + * Construct a WWW-Authenticate or Authentication header value, containing + * the given realm plus all the parameters whose names begin with "oauth_". + */ + public String getAuthorizationHeader(String realm) throws IOException { + StringBuilder into = new StringBuilder(); + if (realm != null) { + into.append(" realm=\"").append(OAuth.percentEncode(realm)).append('"'); + } + beforeGetParameter(); + if (parameters != null) { + for (Map.Entry parameter : parameters) { + String name = toString(parameter.getKey()); + if (name.startsWith("oauth_")) { + if (into.length() > 0) into.append(","); + into.append(" "); + into.append(OAuth.percentEncode(name)).append("=\""); + into.append(OAuth.percentEncode(toString(parameter.getValue()))).append('"'); + } + } + } + return AUTH_SCHEME + into.toString(); + } + + /** + * Read all the data from the given stream, and close it. + * + * @return null if from is null, or the data from the stream converted to a + * String + */ + public static String readAll(InputStream from, String encoding) throws IOException + { + if (from == null) { + return null; + } + try { + StringBuilder into = new StringBuilder(); + Reader r = new InputStreamReader(from, encoding); + char[] s = new char[512]; + for (int n; 0 < (n = r.read(s));) { + into.append(s, 0, n); + } + return into.toString(); + } finally { + from.close(); + } + } + + /** + * Parse the parameters from an OAuth Authorization or WWW-Authenticate + * header. The realm is included as a parameter. If the given header doesn't + * start with "OAuth ", return an empty list. + */ + public static List decodeAuthorization(String authorization) { + List into = new ArrayList(); + if (authorization != null) { + Matcher m = AUTHORIZATION.matcher(authorization); + if (m.matches()) { + if (AUTH_SCHEME.equalsIgnoreCase(m.group(1))) { + for (String nvp : m.group(2).split("\\s*,\\s*")) { + m = NVP.matcher(nvp); + if (m.matches()) { + String name = OAuth.decodePercent(m.group(1)); + String value = OAuth.decodePercent(m.group(2)); + into.add(new OAuth.Parameter(name, value)); + } + } + } + } + } + return into; + } + + public static final String AUTH_SCHEME = "OAuth"; + + public static final String GET = "GET"; + public static final String POST = "POST"; + public static final String PUT = "PUT"; + public static final String DELETE = "DELETE"; + + private static final Pattern AUTHORIZATION = Pattern.compile("\\s*(\\w*)\\s+(.*)"); + private static final Pattern NVP = Pattern.compile("(\\S*)\\s*\\=\\s*\"([^\"]*)\""); + + private static final String toString(Object from) { + return (from == null) ? null : from.toString(); + } + +} Index: 3rdParty_sources/oauth/net/oauth/OAuthProblemException.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuthProblemException.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuthProblemException.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,148 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth; + +import java.util.HashMap; +import java.util.Map; + +/** + * An OAuth-related problem, described using a set of named parameters. One + * parameter identifies the basic problem, and the others provide supplementary + * diagnostic information. This can be used to capture information from a + * response that conforms to the OAuth Problem Reporting + * extension. + * + * @author John Kristian + */ +public class OAuthProblemException extends OAuthException { + + public static final String OAUTH_PROBLEM = "oauth_problem"; + /** The name of a parameter whose value is the HTTP request. */ + public static final String HTTP_REQUEST = "HTTP request"; + /** The name of a parameter whose value is the HTTP response. */ + public static final String HTTP_RESPONSE = "HTTP response"; + /** The name of a parameter whose value is the HTTP resopnse status code. */ + public static final String HTTP_STATUS_CODE = "HTTP status"; + /** The name of a parameter whose value is the response Location header. */ + public static final String HTTP_LOCATION = "Location"; + /** The name of a parameter whose value is the OAuth signature base string. */ + public static final String SIGNATURE_BASE_STRING = OAuth.OAUTH_SIGNATURE + " base string"; + /** The name of a parameter whose value is the request URL. */ + public static final String URL = "URL"; + + public OAuthProblemException() { + } + + public OAuthProblemException(String problem) { + super(problem); + if (problem != null) { + parameters.put(OAUTH_PROBLEM, problem); + } + } + + private final Map parameters = new HashMap(); + + @Override + public String getMessage() { + String msg = super.getMessage(); + if (msg != null) + return msg; + msg = getProblem(); + if (msg != null) + return msg; + Object response = getParameters().get(HTTP_RESPONSE); + if (response != null) { + msg = response.toString(); + int eol = msg.indexOf("\n"); + if (eol < 0) { + eol = msg.indexOf("\r"); + } + if (eol >= 0) { + msg = msg.substring(0, eol); + } + msg = msg.trim(); + if (msg.length() > 0) { + return msg; + } + } + response = getHttpStatusCode(); + if (response != null) { + return HTTP_STATUS_CODE + " " + response; + } + return null; + } + + public void setParameter(String name, Object value) { + getParameters().put(name, value); + } + + public Map getParameters() { + return parameters; + } + + public String getProblem() { + return (String) getParameters().get(OAUTH_PROBLEM); + } + + public int getHttpStatusCode() { + Object code = getParameters().get(HTTP_STATUS_CODE); + if (code == null) { + return 200; + } else if (code instanceof Number) { // the usual case + return ((Number) code).intValue(); + } else { + return Integer.parseInt(code.toString()); + } + } + + @Override + public String toString() { + final StringBuilder s = new StringBuilder(super.toString()); + try { + final String eol = System.getProperty("line.separator", "\n"); + final Map parameters = getParameters(); + for (String key : new String[] { OAuth.Problems.OAUTH_PROBLEM_ADVICE, URL, + SIGNATURE_BASE_STRING }) { + Object value = parameters.get(key); + if (value != null) + s.append(eol + key + ": " + value); + } + Object msg = parameters.get(HTTP_REQUEST); + if ((msg != null)) + s.append(eol + ">>>>>>>> " + HTTP_REQUEST + ":" + eol + msg); + msg = parameters.get(HTTP_RESPONSE); + if (msg != null) { + s.append(eol + "<<<<<<<< " + HTTP_RESPONSE + ":" + eol + msg); + } else { + for (Map.Entry parameter : parameters.entrySet()) { + String key = parameter.getKey(); + if (OAuth.Problems.OAUTH_PROBLEM_ADVICE.equals(key) + || URL.equals(key) || SIGNATURE_BASE_STRING.equals(key) + || HTTP_REQUEST.equals(key) || HTTP_RESPONSE.equals(key)) + continue; + s.append(eol + key + ": " + parameter.getValue()); + } + } + } catch (Exception ignored) { + } + return s.toString(); + } + + private static final long serialVersionUID = 1L; + +} Index: 3rdParty_sources/oauth/net/oauth/OAuthServiceProvider.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/OAuthServiceProvider.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/OAuthServiceProvider.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,81 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth; + +import java.io.Serializable; + +/** + * Properties of an OAuth Service Provider. + * + * @author John Kristian + */ +public class OAuthServiceProvider implements Serializable { + + private static final long serialVersionUID = 3306534392621038574L; + + public final String requestTokenURL; + public final String userAuthorizationURL; + public final String accessTokenURL; + + public OAuthServiceProvider(String requestTokenURL, + String userAuthorizationURL, String accessTokenURL) { + this.requestTokenURL = requestTokenURL; + this.userAuthorizationURL = userAuthorizationURL; + this.accessTokenURL = accessTokenURL; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((accessTokenURL == null) ? 0 : accessTokenURL.hashCode()); + result = prime * result + + ((requestTokenURL == null) ? 0 : requestTokenURL.hashCode()); + result = prime * result + + ((userAuthorizationURL == null) ? 0 : userAuthorizationURL.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OAuthServiceProvider other = (OAuthServiceProvider) obj; + if (accessTokenURL == null) { + if (other.accessTokenURL != null) + return false; + } else if (!accessTokenURL.equals(other.accessTokenURL)) + return false; + if (requestTokenURL == null) { + if (other.requestTokenURL != null) + return false; + } else if (!requestTokenURL.equals(other.requestTokenURL)) + return false; + if (userAuthorizationURL == null) { + if (other.userAuthorizationURL != null) + return false; + } else if (!userAuthorizationURL.equals(other.userAuthorizationURL)) + return false; + return true; + } + +} Index: 3rdParty_sources/oauth/net/oauth/ParameterStyle.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/ParameterStyle.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/ParameterStyle.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,27 @@ +package net.oauth; + +/** + * Where to place OAuth parameters in an HTTP message. The alternatives are + * summarized in OAuth Core + * under Consumer + * Request Parameters. + */ +public enum ParameterStyle { + /** + * Send parameters whose names begin with "oauth_" in an HTTP header, and + * other parameters (whose names don't begin with "oauth_") in either the + * message body or URL query string. The header formats are specified by + * OAuth Core under OAuth + * HTTP Authorization Scheme. + */ + AUTHORIZATION_HEADER, + + /** + * Send all parameters in the message body, with a Content-Type of + * application/x-www-form-urlencoded. + */ + BODY, + + /** Send all parameters in the query string part of the URL. */ + QUERY_STRING; +} Index: 3rdParty_sources/oauth/net/oauth/consumer.properties.sample =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/consumer.properties.sample (revision 0) +++ 3rdParty_sources/oauth/net/oauth/consumer.properties.sample (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,16 @@ +# NamedConsumerPool can gets consumer configuration parameters from a file like this. + +ma.gnolia.consumerKey: - Your key here - +ma.gnolia.consumerSecret: - Your secret here - +ma.gnolia.serviceProvider.requestTokenURL: http://ma.gnolia.com/oauth/get_request_token +ma.gnolia.serviceProvider.userAuthorizationURL: http://ma.gnolia.com/oauth/authorize +ma.gnolia.serviceProvider.accessTokenURL: http://ma.gnolia.com/oauth/get_access_token + +twitter.consumerKey: - Your key here - +twitter.consumerSecret: - Your secret here - +twitter.callbackURL: - Your URL here - +twitter.consumer.oauth_signature_method: PLAINTEXT +# There can be more consumer properties. +twitter.serviceProvider.requestTokenURL: http://twitter.com/oauth/request_token +twitter.serviceProvider.userAuthorizationURL: http://twitter.com/oauth/authorize +twitter.serviceProvider.accessTokenURL: http://twitter.com/oauth/access_token Index: 3rdParty_sources/oauth/net/oauth/package-info.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/package-info.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/package-info.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,4 @@ +/** + * Fundamental classes and algorithms for implementing OAuth. + */ +package net.oauth; Index: 3rdParty_sources/oauth/net/oauth/signature/Base64.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/Base64.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/Base64.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,714 @@ +/* + * Copyright 2001-2008 The Apache Software Foundation. + * The ASF licenses this file to You 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 net.oauth.signature; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

+ * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose + * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. + *

+ * + * @see RFC 2045 + * @author Apache Software Foundation + * @author John Kristian + */ +class Base64 { + /** + * Chunk size per RFC 2045 section 6.8. + * + *

+ * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any + * equal signs. + *

+ * + * @see RFC 2045 section 6.8 + */ + static final int CHUNK_SIZE = 76; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = {'\r','\n'}; + + /** + * This array is a lookup table that translates 6-bit positive integer + * index values into their "Base64 Alphabet" equivalents as specified + * in Table 1 of RFC 2045. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] intToBase64 = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * Byte used to pad output. + */ + private static final byte PAD = '='; + + /** + * This array is a lookup table that translates unicode characters + * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) + * into their 6-bit positive integer equivalents. Characters that + * are not in the Base64 alphabet but fall within the bounds of the + * array are translated to -1. + * + * Thanks to "commons" project in ws.apache.org for this code. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + */ + private static final byte[] base64ToInt = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + /** Mask used to extract 6 bits, used when encoding */ + private static final int MASK_6BITS = 0x3f; + + /** Mask used to extract 8 bits, used in decoding base64 bytes */ + private static final int MASK_8BITS = 0xff; + + // The static final fields above are used for the original static byte[] methods on Base64. + // The private member fields below are used with the new streaming approach, which requires + // some state be preserved between calls of encode() and decode(). + + + /** + * Line length for encoding. Not used when decoding. A value of zero or less implies + * no chunking of the base64 encoded data. + */ + private final int lineLength; + + /** + * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. + */ + private final byte[] lineSeparator; + + /** + * Convenience variable to help us determine when our buffer is going to run out of + * room and needs resizing. decodeSize = 3 + lineSeparator.length; + */ + private final int decodeSize; + + /** + * Convenience variable to help us determine when our buffer is going to run out of + * room and needs resizing. encodeSize = 4 + lineSeparator.length; + */ + private final int encodeSize; + + /** + * Buffer for streaming. + */ + private byte[] buf; + + /** + * Position where next character should be written in the buffer. + */ + private int pos; + + /** + * Position where next character should be read from the buffer. + */ + private int readPos; + + /** + * Variable tracks how many characters have been written to the current line. + * Only used when encoding. We use it to make sure each encoded line never + * goes beyond lineLength (if lineLength > 0). + */ + private int currentLinePos; + + /** + * Writes to the buffer only occur after every 3 reads when encoding, an + * every 4 reads when decoding. This variable helps track that. + */ + private int modulus; + + /** + * Boolean flag to indicate the EOF has been reached. Once EOF has been + * reached, this Base64 object becomes useless, and must be thrown away. + */ + private boolean eof; + + /** + * Place holder for the 3 bytes we're dealing with for our base64 logic. + * Bitwise operations store and extract the base64 encoding or decoding from + * this variable. + */ + private int x; + + /** + * Default constructor: lineLength is 76, and the lineSeparator is CRLF + * when encoding, and all forms can be decoded. + */ + public Base64() { + this(CHUNK_SIZE, CHUNK_SEPARATOR); + } + + /** + *

+ * Consumer can use this constructor to choose a different lineLength + * when encoding (lineSeparator is still CRLF). All forms of data can + * be decoded. + *

+ * Note: lineLengths that aren't multiples of 4 will still essentially + * end up being multiples of 4 in the encoded data. + *

+ * + * @param lineLength each line of encoded data will be at most this long + * (rounded up to nearest multiple of 4). + * If lineLength <= 0, then the output will not be divided into lines (chunks). + * Ignored when decoding. + */ + public Base64(int lineLength) { + this(lineLength, CHUNK_SEPARATOR); + } + + /** + *

+ * Consumer can use this constructor to choose a different lineLength + * and lineSeparator when encoding. All forms of data can + * be decoded. + *

+ * Note: lineLengths that aren't multiples of 4 will still essentially + * end up being multiples of 4 in the encoded data. + *

+ * @param lineLength Each line of encoded data will be at most this long + * (rounded up to nearest multiple of 4). Ignored when decoding. + * If <= 0, then output will not be divided into lines (chunks). + * @param lineSeparator Each line of encoded data will end with this + * sequence of bytes. + * If lineLength <= 0, then the lineSeparator is not used. + * @throws IllegalArgumentException The provided lineSeparator included + * some base64 characters. That's not going to work! + */ + public Base64(int lineLength, byte[] lineSeparator) { + this.lineLength = lineLength; + this.lineSeparator = new byte[lineSeparator.length]; + System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); + if (lineLength > 0) { + this.encodeSize = 4 + lineSeparator.length; + } else { + this.encodeSize = 4; + } + this.decodeSize = encodeSize - 1; + if (containsBase64Byte(lineSeparator)) { + String sep; + try { + sep = new String(lineSeparator, "UTF-8"); + } catch (UnsupportedEncodingException uee) { + sep = new String(lineSeparator); + } + throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]"); + } + } + + /** + * Returns true if this Base64 object has buffered data for reading. + * + * @return true if there is Base64 object still available for reading. + */ + boolean hasData() { return buf != null; } + + /** + * Returns the amount of buffered data available for reading. + * + * @return The amount of buffered data available for reading. + */ + int avail() { return buf != null ? pos - readPos : 0; } + + /** Doubles our buffer. */ + private void resizeBuf() { + if (buf == null) { + buf = new byte[8192]; + pos = 0; + readPos = 0; + } else { + byte[] b = new byte[buf.length * 2]; + System.arraycopy(buf, 0, b, 0, buf.length); + buf = b; + } + } + + /** + * Extracts buffered data into the provided byte[] array, starting + * at position bPos, up to a maximum of bAvail bytes. Returns how + * many bytes were actually extracted. + * + * @param b byte[] array to extract the buffered data into. + * @param bPos position in byte[] array to start extraction at. + * @param bAvail amount of bytes we're allowed to extract. We may extract + * fewer (if fewer are available). + * @return The number of bytes successfully extracted into the provided + * byte[] array. + */ + int readResults(byte[] b, int bPos, int bAvail) { + if (buf != null) { + int len = Math.min(avail(), bAvail); + if (buf != b) { + System.arraycopy(buf, readPos, b, bPos, len); + readPos += len; + if (readPos >= pos) { + buf = null; + } + } else { + // Re-using the original consumer's output array is only + // allowed for one round. + buf = null; + } + return len; + } else { + return eof ? -1 : 0; + } + } + + /** + * Small optimization where we try to buffer directly to the consumer's + * output array for one round (if consumer calls this method first!) instead + * of starting our own buffer. + * + * @param out byte[] array to buffer directly to. + * @param outPos Position to start buffering into. + * @param outAvail Amount of bytes available for direct buffering. + */ + void setInitialBuffer(byte[] out, int outPos, int outAvail) { + // We can re-use consumer's original output array under + // special circumstances, saving on some System.arraycopy(). + if (out != null && out.length == outAvail) { + buf = out; + pos = outPos; + readPos = outPos; + } + } + + /** + *

+ * Encodes all of the provided data, starting at inPos, for inAvail bytes. + * Must be called at least twice: once with the data to encode, and once + * with inAvail set to "-1" to alert encoder that EOF has been reached, + * so flush last remaining bytes (if not multiple of 3). + *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, + * and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ * + * @param in byte[] array of binary data to base64 encode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + */ + void encode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + + // inAvail < 0 is how we're informed of EOF in the underlying data we're + // encoding. + if (inAvail < 0) { + eof = true; + if (buf == null || buf.length - pos < encodeSize) { + resizeBuf(); + } + switch (modulus) { + case 1: + buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS]; + buf[pos++] = intToBase64[(x << 4) & MASK_6BITS]; + buf[pos++] = PAD; + buf[pos++] = PAD; + break; + + case 2: + buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS]; + buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS]; + buf[pos++] = intToBase64[(x << 2) & MASK_6BITS]; + buf[pos++] = PAD; + break; + } + if (lineLength > 0) { + System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length); + pos += lineSeparator.length; + } + } else { + for (int i = 0; i < inAvail; i++) { + if (buf == null || buf.length - pos < encodeSize) { + resizeBuf(); + } + modulus = (++modulus) % 3; + int b = in[inPos++]; + if (b < 0) { b += 256; } + x = (x << 8) + b; + if (0 == modulus) { + buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS]; + buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS]; + buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS]; + buf[pos++] = intToBase64[x & MASK_6BITS]; + currentLinePos += 4; + if (lineLength > 0 && lineLength <= currentLinePos) { + System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length); + pos += lineSeparator.length; + currentLinePos = 0; + } + } + } + } + } + + /** + *

+ * Decodes all of the provided data, starting at inPos, for inAvail bytes. + * Should be called at least twice: once with the data to decode, and once + * with inAvail set to "-1" to alert decoder that EOF has been reached. + * The "-1" call is not necessary when decoding, but it doesn't hurt, either. + *

+ * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) + * data is handled, since CR and LF are silently ignored, but has implications + * for other bytes, too. This method subscribes to the garbage-in, garbage-out + * philosophy: it will not check the provided data for validity. + *

+ * Thanks to "commons" project in ws.apache.org for the bitwise operations, + * and general approach. + * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ + *

+ + * @param in byte[] array of ascii data to base64 decode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + */ + void decode(byte[] in, int inPos, int inAvail) { + if (eof) { + return; + } + if (inAvail < 0) { + eof = true; + } + for (int i = 0; i < inAvail; i++) { + if (buf == null || buf.length - pos < decodeSize) { + resizeBuf(); + } + byte b = in[inPos++]; + if (b == PAD) { + x = x << 6; + switch (modulus) { + case 2: + x = x << 6; + buf[pos++] = (byte) ((x >> 16) & MASK_8BITS); + break; + case 3: + buf[pos++] = (byte) ((x >> 16) & MASK_8BITS); + buf[pos++] = (byte) ((x >> 8) & MASK_8BITS); + break; + } + // WE'RE DONE!!!! + eof = true; + return; + } else { + if (b >= 0 && b < base64ToInt.length) { + int result = base64ToInt[b]; + if (result >= 0) { + modulus = (++modulus) % 4; + x = (x << 6) + result; + if (modulus == 0) { + buf[pos++] = (byte) ((x >> 16) & MASK_8BITS); + buf[pos++] = (byte) ((x >> 8) & MASK_8BITS); + buf[pos++] = (byte) (x & MASK_8BITS); + } + } + } + } + } + } + + /** + * Returns whether or not the octet is in the base 64 alphabet. + * + * @param octet + * The value to test + * @return true if the value is defined in the the base 64 alphabet, false otherwise. + */ + public static boolean isBase64(byte octet) { + return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1); + } + + /** + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. + * Currently the method treats whitespace as valid. + * + * @param arrayOctet + * byte array to test + * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is + * empty; false, otherwise + */ + public static boolean isArrayByteBase64(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) { + return false; + } + } + return true; + } + + /* + * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. + * + * @param arrayOctet + * byte array to test + * @return true if any byte is a valid character in the Base64 alphabet; false herwise + */ + private static boolean containsBase64Byte(byte[] arrayOctet) { + for (int i = 0; i < arrayOctet.length; i++) { + if (isBase64(arrayOctet[i])) { + return true; + } + } + return false; + } + + /** + * Encodes binary data using the base64 algorithm but does not chunk the output. + * + * @param binaryData + * binary data to encode + * @return Base64 characters + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks + * + * @param binaryData + * binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Decodes a byte[] containing containing characters in the Base64 alphabet. + * + * @param pArray + * A byte array containing Base64 character data + * @return a byte array containing binary data + */ + public byte[] decode(byte[] pArray) { + return decodeBase64(pArray); + } + + /** + * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. + * + * @param binaryData + * Array containing binary data to encode. + * @param isChunked + * if true this encoder will chunk the base64 output into 76 character blocks + * @return Base64-encoded data. + * @throws IllegalArgumentException + * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { + if (binaryData == null || binaryData.length == 0) { + return binaryData; + } + Base64 b64 = isChunked ? new Base64() : new Base64(0); + + long len = (binaryData.length * 4) / 3; + long mod = len % 4; + if (mod != 0) { + len += 4 - mod; + } + if (isChunked) { + len += (1 + (len / CHUNK_SIZE)) * CHUNK_SEPARATOR.length; + } + + if (len > Integer.MAX_VALUE) { + throw new IllegalArgumentException( + "Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE); + } + byte[] buf = new byte[(int) len]; + b64.setInitialBuffer(buf, 0, buf.length); + b64.encode(binaryData, 0, binaryData.length); + b64.encode(binaryData, 0, -1); // Notify encoder of EOF. + + // Encoder might have resized, even though it was unnecessary. + if (b64.buf != buf) { + b64.readResults(buf, 0, buf.length); + } + return buf; + } + + /** + * Decodes Base64 data into octets + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + if (base64Data == null || base64Data.length == 0) { + return base64Data; + } + Base64 b64 = new Base64(); + + long len = (base64Data.length * 3) / 4; + byte[] buf = new byte[(int) len]; + b64.setInitialBuffer(buf, 0, buf.length); + b64.decode(base64Data, 0, base64Data.length); + b64.decode(base64Data, 0, -1); // Notify decoder of EOF. + + // We have no idea what the line-length was, so we + // cannot know how much of our array wasn't used. + byte[] result = new byte[b64.pos]; + b64.readResults(result, 0, result.length); + return result; + } + + /** + * Check if a byte value is whitespace or not. + * + * @param byteToCheck the byte to check + * @return true if byte is whitespace, false otherwise + */ + private static boolean isWhiteSpace(byte byteToCheck){ + switch (byteToCheck) { + case ' ' : + case '\n' : + case '\r' : + case '\t' : + return true; + default : + return false; + } + } + + /** + * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any + * characters outside of the base64 alphabet are to be ignored in base64 encoded data." + * + * @param data + * The base-64 encoded data to groom + * @return The data, less non-base64 characters (see RFC 2045). + */ + static byte[] discardNonBase64(byte[] data) { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) { + if (isBase64(data[i])) { + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } + + // Implementation of the Encoder Interface + + /** + * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet. + * + * @param pArray + * a byte array containing binary data + * @return A byte array containing only Base64 character data + */ + public byte[] encode(byte[] pArray) { + return encodeBase64(pArray, false); + } + + // Implementation of integer encoding used for crypto + /** + * Decode a byte64-encoded integer according to crypto + * standards such as W3C's XML-Signature + * + * @param pArray a byte array containing base64 character data + * @return A BigInteger + */ + public static BigInteger decodeInteger(byte[] pArray) { + return new BigInteger(1, decodeBase64(pArray)); + } + + /** + * Encode to a byte64-encoded integer according to crypto + * standards such as W3C's XML-Signature + * + * @param bigInt a BigInteger + * @return A byte array containing base64 character data + * @throws NullPointerException if null is passed in + */ + public static byte[] encodeInteger(BigInteger bigInt) { + if(bigInt == null) { + throw new NullPointerException("encodeInteger called with null parameter"); + } + + return encodeBase64(toIntegerBytes(bigInt), false); + } + + /** + * Returns a byte-array representation of a BigInteger + * without sign bit. + * + * @param bigInt BigInteger to be converted + * @return a byte array representation of the BigInteger parameter + */ + static byte[] toIntegerBytes(BigInteger bigInt) { + int bitlen = bigInt.bitLength(); + // round bitlen + bitlen = ((bitlen + 7) >> 3) << 3; + byte[] bigBytes = bigInt.toByteArray(); + + if(((bigInt.bitLength() % 8) != 0) && + (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) { + return bigBytes; + } + + // set up params for copying everything but sign bit + int startSrc = 0; + int len = bigBytes.length; + + // if bigInt is exactly byte-aligned, just skip signbit in copy + if((bigInt.bitLength() % 8) == 0) { + startSrc = 1; + len--; + } + + int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec + byte[] resizedBytes = new byte[bitlen / 8]; + + System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len); + + return resizedBytes; + } +} Index: 3rdParty_sources/oauth/net/oauth/signature/HMAC_SHA1.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/HMAC_SHA1.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/HMAC_SHA1.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,104 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth.signature; + +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.util.Arrays; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import net.oauth.OAuth; +import net.oauth.OAuthException; + +/** + * The HMAC-SHA1 signature method. + * + * @author John Kristian + */ +class HMAC_SHA1 extends OAuthSignatureMethod { + + @Override + protected String getSignature(String baseString) throws OAuthException { + try { + String signature = base64Encode(computeSignature(baseString)); + return signature; + } catch (GeneralSecurityException e) { + throw new OAuthException(e); + } catch (UnsupportedEncodingException e) { + throw new OAuthException(e); + } + } + + @Override + protected boolean isValid(String signature, String baseString) + throws OAuthException { + try { + byte[] expected = computeSignature(baseString); + byte[] actual = decodeBase64(signature); + return equals(expected, actual); + } catch (GeneralSecurityException e) { + throw new OAuthException(e); + } catch (UnsupportedEncodingException e) { + throw new OAuthException(e); + } + } + + private byte[] computeSignature(String baseString) + throws GeneralSecurityException, UnsupportedEncodingException { + SecretKey key = null; + synchronized (this) { + if (this.key == null) { + String keyString = OAuth.percentEncode(getConsumerSecret()) + + '&' + OAuth.percentEncode(getTokenSecret()); + byte[] keyBytes = keyString.getBytes(ENCODING); + this.key = new SecretKeySpec(keyBytes, MAC_NAME); + } + key = this.key; + } + Mac mac = Mac.getInstance(MAC_NAME); + mac.init(key); + byte[] text = baseString.getBytes(ENCODING); + return mac.doFinal(text); + } + + /** ISO-8859-1 or US-ASCII would work, too. */ + private static final String ENCODING = OAuth.ENCODING; + + private static final String MAC_NAME = "HmacSHA1"; + + private SecretKey key = null; + + @Override + public void setConsumerSecret(String consumerSecret) { + synchronized (this) { + key = null; + } + super.setConsumerSecret(consumerSecret); + } + + @Override + public void setTokenSecret(String tokenSecret) { + synchronized (this) { + key = null; + } + super.setTokenSecret(tokenSecret); + } + +} Index: 3rdParty_sources/oauth/net/oauth/signature/OAuthSignatureMethod.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/OAuthSignatureMethod.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/OAuthSignatureMethod.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,384 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth.signature; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.log4j.Logger; + +import net.oauth.OAuth; +import net.oauth.OAuthAccessor; +import net.oauth.OAuthConsumer; +import net.oauth.OAuthException; +import net.oauth.OAuthMessage; +import net.oauth.OAuthProblemException; + +/** + * A pair of algorithms for computing and verifying an OAuth digital signature. + *

+ * Static methods of this class implement a registry of signature methods. It's + * pre-populated with the standard OAuth algorithms. Appliations can replace + * them or add new ones. + * + * @author John Kristian + */ +public abstract class OAuthSignatureMethod { + + private static Logger log = Logger.getLogger(OAuthSignatureMethod.class); + + /** Add a signature to the message. + * @throws URISyntaxException + * @throws IOException */ + public void sign(OAuthMessage message) + throws OAuthException, IOException, URISyntaxException { + message.addParameter(new OAuth.Parameter("oauth_signature", + getSignature(message))); + } + + /** + * Check whether the message has a valid signature. + * @throws URISyntaxException + * + * @throws OAuthProblemException + * the signature is invalid + */ + public void validate(OAuthMessage message) + throws IOException, OAuthException, URISyntaxException { + message.requireParameters("oauth_signature"); + String signature = message.getSignature(); + String baseString = getBaseString(message); + if (!isValid(signature, baseString)) { + + // *LAMS* added by LAMS + log.debug("Error. Signature invalid. oauth_signature=" + signature + ", oauth_signature_base_string=" + + baseString + ", oauth_signature_method=" + message.getSignatureMethod()); + + OAuthProblemException problem = new OAuthProblemException( + "signature_invalid"); + problem.setParameter("oauth_signature", signature); + problem.setParameter("oauth_signature_base_string", baseString); + problem.setParameter("oauth_signature_method", message + .getSignatureMethod()); + throw problem; + } + } + + protected String getSignature(OAuthMessage message) + throws OAuthException, IOException, URISyntaxException { + String baseString = getBaseString(message); + String signature = getSignature(baseString); + // Logger log = Logger.getLogger(getClass().getName()); + // if (log.isLoggable(Level.FINE)) { + // log.fine(signature + "=getSignature(" + baseString + ")"); + // } + return signature; + } + + protected void initialize(String name, OAuthAccessor accessor) + throws OAuthException { + String secret = accessor.consumer.consumerSecret; + if (name.endsWith(_ACCESSOR)) { + // This code supports the 'Accessor Secret' extensions + // described in http://oauth.pbwiki.com/AccessorSecret + final String key = OAuthConsumer.ACCESSOR_SECRET; + Object accessorSecret = accessor.getProperty(key); + if (accessorSecret == null) { + accessorSecret = accessor.consumer.getProperty(key); + } + if (accessorSecret != null) { + secret = accessorSecret.toString(); + } + } + if (secret == null) { + secret = ""; + } + setConsumerSecret(secret); + } + + public static final String _ACCESSOR = "-Accessor"; + + /** Compute the signature for the given base string. */ + protected abstract String getSignature(String baseString) throws OAuthException; + + /** Decide whether the signature is valid. */ + protected abstract boolean isValid(String signature, String baseString) + throws OAuthException; + + private String consumerSecret; + + private String tokenSecret; + + protected String getConsumerSecret() { + return consumerSecret; + } + + protected void setConsumerSecret(String consumerSecret) { + this.consumerSecret = consumerSecret; + } + + public String getTokenSecret() { + return tokenSecret; + } + + public void setTokenSecret(String tokenSecret) { + this.tokenSecret = tokenSecret; + } + + public static String getBaseString(OAuthMessage message) + throws IOException, URISyntaxException { + List> parameters; + String url = message.URL; + int q = url.indexOf('?'); + if (q < 0) { + parameters = message.getParameters(); + } else { + // Combine the URL query string with the other parameters: + parameters = new ArrayList>(); + parameters.addAll(OAuth.decodeForm(message.URL.substring(q + 1))); + parameters.addAll(message.getParameters()); + url = url.substring(0, q); + } + return OAuth.percentEncode(message.method.toUpperCase()) + '&' + + OAuth.percentEncode(normalizeUrl(url)) + '&' + + OAuth.percentEncode(normalizeParameters(parameters)); + } + + protected static String normalizeUrl(String url) throws URISyntaxException { + URI uri = new URI(url); + String scheme = uri.getScheme().toLowerCase(); + String authority = uri.getAuthority().toLowerCase(); + boolean dropPort = (scheme.equals("http") && uri.getPort() == 80) + || (scheme.equals("https") && uri.getPort() == 443); + if (dropPort) { + // find the last : in the authority + int index = authority.lastIndexOf(":"); + if (index >= 0) { + authority = authority.substring(0, index); + } + } + String path = uri.getRawPath(); + if (path == null || path.length() <= 0) { + path = "/"; // conforms to RFC 2616 section 3.2.2 + } + // we know that there is no query and no fragment here. + return scheme + "://" + authority + path; + } + + protected static String normalizeParameters( + Collection parameters) throws IOException { + if (parameters == null) { + return ""; + } + List p = new ArrayList( + parameters.size()); + for (Map.Entry parameter : parameters) { + if (!"oauth_signature".equals(parameter.getKey())) { + p.add(new ComparableParameter(parameter)); + } + } + Collections.sort(p); + return OAuth.formEncode(getParameters(p)); + } + + /** + * Determine whether the given strings contain the same sequence of + * characters. The implementation discourages a timing attack. + */ + public static boolean equals(String x, String y) { + if (x == null) + return y == null; + else if (y == null) + return false; + else if (y.length() <= 0) + return x.length() <= 0; + char[] a = x.toCharArray(); + char[] b = y.toCharArray(); + char diff = (char) ((a.length == b.length) ? 0 : 1); + int j = 0; + for (int i = 0; i < a.length; ++i) { + diff |= a[i] ^ b[j]; + j = (j + 1) % b.length; + } + return diff == 0; + } + + /** + * Determine whether the given arrays contain the same sequence of bytes. + * The implementation discourages a timing attack. + */ + public static boolean equals(byte[] a, byte[] b) { + if (a == null) + return b == null; + else if (b == null) + return false; + else if (b.length <= 0) + return a.length <= 0; + byte diff = (byte) ((a.length == b.length) ? 0 : 1); + int j = 0; + for (int i = 0; i < a.length; ++i) { + diff |= a[i] ^ b[j]; + j = (j + 1) % b.length; + } + return diff == 0; + } + + public static byte[] decodeBase64(String s) { + byte[] b; + try { + b = s.getBytes(BASE64_ENCODING); + } catch (UnsupportedEncodingException e) { + System.err.println(e + ""); + b = s.getBytes(); + } + return BASE64.decode(b); + } + + public static String base64Encode(byte[] b) { + byte[] b2 = BASE64.encode(b); + try { + return new String(b2, BASE64_ENCODING); + } catch (UnsupportedEncodingException e) { + System.err.println(e + ""); + } + return new String(b2); + } + + /** + * The character encoding used for base64. Arguably US-ASCII is more + * accurate, but this one decodes all byte values unambiguously. + */ + private static final String BASE64_ENCODING = "ISO-8859-1"; + private static final Base64 BASE64 = new Base64(); + + public static OAuthSignatureMethod newSigner(OAuthMessage message, + OAuthAccessor accessor) throws IOException, OAuthException { + message.requireParameters(OAuth.OAUTH_SIGNATURE_METHOD); + OAuthSignatureMethod signer = newMethod(message.getSignatureMethod(), + accessor); + signer.setTokenSecret(accessor.tokenSecret); + return signer; + } + + /** The factory for signature methods. */ + public static OAuthSignatureMethod newMethod(String name, + OAuthAccessor accessor) throws OAuthException { + try { + Class methodClass = NAME_TO_CLASS.get(name); + if (methodClass != null) { + OAuthSignatureMethod method = (OAuthSignatureMethod) methodClass + .newInstance(); + method.initialize(name, accessor); + return method; + } + OAuthProblemException problem = new OAuthProblemException(OAuth.Problems.SIGNATURE_METHOD_REJECTED); + String acceptable = OAuth.percentEncode(NAME_TO_CLASS.keySet()); + if (acceptable.length() > 0) { + problem.setParameter("oauth_acceptable_signature_methods", + acceptable.toString()); + } + throw problem; + } catch (InstantiationException e) { + throw new OAuthException(e); + } catch (IllegalAccessException e) { + throw new OAuthException(e); + } + } + + /** + * Subsequently, newMethod(name) will attempt to instantiate the given + * class, with no constructor parameters. + */ + public static void registerMethodClass(String name, Class clazz) { + if (clazz == null) + unregisterMethod(name); + else + NAME_TO_CLASS.put(name, clazz); + } + + /** + * Subsequently, newMethod(name) will fail. + */ + public static void unregisterMethod(String name) { + NAME_TO_CLASS.remove(name); + } + + private static final Map NAME_TO_CLASS = new ConcurrentHashMap(); + static { + registerMethodClass("HMAC-SHA1", HMAC_SHA1.class); + registerMethodClass("PLAINTEXT", PLAINTEXT.class); + registerMethodClass("RSA-SHA1", RSA_SHA1.class); + registerMethodClass("HMAC-SHA1" + _ACCESSOR, HMAC_SHA1.class); + registerMethodClass("PLAINTEXT" + _ACCESSOR, PLAINTEXT.class); + } + + /** An efficiently sortable wrapper around a parameter. */ + private static class ComparableParameter implements + Comparable { + + ComparableParameter(Map.Entry value) { + this.value = value; + String n = toString(value.getKey()); + String v = toString(value.getValue()); + this.key = OAuth.percentEncode(n) + ' ' + OAuth.percentEncode(v); + // ' ' is used because it comes before any character + // that can appear in a percentEncoded string. + } + + final Map.Entry value; + + private final String key; + + private static String toString(Object from) { + return (from == null) ? null : from.toString(); + } + + public int compareTo(ComparableParameter that) { + return this.key.compareTo(that.key); + } + + @Override + public String toString() { + return key; + } + + } + + /** Retrieve the original parameters from a sorted collection. */ + private static List getParameters( + Collection parameters) { + if (parameters == null) { + return null; + } + List list = new ArrayList(parameters.size()); + for (ComparableParameter parameter : parameters) { + list.add(parameter.value); + } + return list; + } + +} Index: 3rdParty_sources/oauth/net/oauth/signature/PLAINTEXT.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/PLAINTEXT.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/PLAINTEXT.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,66 @@ +/* + * Copyright 2007 Netflix, Inc. + * + * 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 net.oauth.signature; + +import net.oauth.OAuth; +import net.oauth.OAuthException; + +/** + * The PLAINTEXT signature method. + * + * @author John Kristian + */ +class PLAINTEXT extends OAuthSignatureMethod { + + @Override + public String getSignature(String baseString) { + return getSignature(); + } + + @Override + protected boolean isValid(String signature, String baseString) + throws OAuthException { + return equals(getSignature(), signature); + } + + private synchronized String getSignature() { + if (signature == null) { + signature = OAuth.percentEncode(getConsumerSecret()) + '&' + + OAuth.percentEncode(getTokenSecret()); + } + return signature; + } + + private String signature = null; + + @Override + public void setConsumerSecret(String consumerSecret) { + synchronized (this) { + signature = null; + } + super.setConsumerSecret(consumerSecret); + } + + @Override + public void setTokenSecret(String tokenSecret) { + synchronized (this) { + signature = null; + } + super.setTokenSecret(tokenSecret); + } + +} Index: 3rdParty_sources/oauth/net/oauth/signature/RSA_SHA1.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/RSA_SHA1.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/RSA_SHA1.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,337 @@ +/* + * Copyright 2007 Google, Inc. + * + * 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 net.oauth.signature; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.EncodedKeySpec; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import net.oauth.OAuth; +import net.oauth.OAuthAccessor; +import net.oauth.OAuthException; +import net.oauth.signature.pem.PEMReader; +import net.oauth.signature.pem.PKCS1EncodedKeySpec; + +/** + * The RSA-SHA1 signature method. A consumer + * that wishes to use public-key signatures on messages does not need + * a shared secret with the service provider, but it needs a private + * RSA signing key. You create it like this: + * + * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key, + * null, provider); + * c.setProperty(RSA_SHA1.PRIVATE_KEY, consumer_privateRSAKey); + * + * consumer_privateRSAKey must be an RSA signing key and + * of type java.security.PrivateKey, String, byte[] or InputStream. + * The key must either PKCS#1 or PKCS#8 encoded. + * + * A service provider that wishes to verify signatures made by such a + * consumer does not need a shared secret with the consumer, but it needs + * to know the consumer's public key. You create the necessary + * OAuthConsumer object (on the service provider's side) like this: + * + * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key, + * null, provider); + * c.setProperty(RSA_SHA1.PUBLIC_KEY, consumer_publicRSAKey); + * + * consumer_publicRSAKey must be the consumer's public RSAkey and + * of type java.security.PublicKey, String, or byte[]. In the latter two + * cases, the key must be X509-encoded (byte[]) or X509-encoded and + * then Base64-encoded (String). + * + * Alternatively, a service provider that wishes to verify signatures made + * by such a consumer can use a X509 certificate containing the consumer's + * public key. You create the necessary OAuthConsumer object (on the service + * provider's side) like this: + * + * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key, + * null, provider); + * c.setProperty(RSA_SHA1.X509_CERTIFICATE, consumer_cert); + * + * consumer_cert must be a X509 Certificate containing the consumer's public + * key and be of type java.security.cert.X509Certificate, String, + * or byte[]. In the latter two cases, the certificate must be DER-encoded + * (byte[]) or PEM-encoded (String). + * + * @author Dirk Balfanz + * + */ +public class RSA_SHA1 extends OAuthSignatureMethod { + + final static public String PRIVATE_KEY = "RSA-SHA1.PrivateKey"; + final static public String PUBLIC_KEY = "RSA-SHA1.PublicKey"; + final static public String X509_CERTIFICATE = "RSA-SHA1.X509Certificate"; + + private PrivateKey privateKey = null; + private PublicKey publicKey = null; + + @Override + protected void initialize(String name, OAuthAccessor accessor) + throws OAuthException { + super.initialize(name, accessor); + + // Due to the support of PEM input stream, the keys must be cached. + // The stream may not be markable so it can't be read again. + try { + Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY); + if (privateKeyObject != null) { + privateKey = loadPrivateKey(privateKeyObject); + } + + Object publicKeyObject = accessor.consumer.getProperty(PUBLIC_KEY); + if (publicKeyObject != null) { + publicKey = loadPublicKey(publicKeyObject, false); + } else { // public key was null. perhaps they gave us a X509 cert. + Object certObject = accessor.consumer.getProperty(X509_CERTIFICATE); + if (certObject != null) { + publicKey = loadPublicKey(certObject, true); + } + } + } catch (GeneralSecurityException e) { + throw new OAuthException(e); + } catch (IOException e) { + throw new OAuthException(e); + } + } + + private PublicKey getPublicKeyFromDerCert(byte[] certObject) + throws GeneralSecurityException { + CertificateFactory fac = CertificateFactory.getInstance("X509"); + ByteArrayInputStream in = new ByteArrayInputStream(certObject); + X509Certificate cert = (X509Certificate)fac.generateCertificate(in); + return cert.getPublicKey(); + } + + private PublicKey getPublicKeyFromDer(byte[] publicKeyObject) + throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("RSA"); + EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyObject); + return fac.generatePublic(pubKeySpec); + } + + private PublicKey getPublicKeyFromPem(String pem) + throws GeneralSecurityException, IOException { + + InputStream stream = new ByteArrayInputStream( + pem.getBytes("UTF-8")); + + PEMReader reader = new PEMReader(stream); + byte[] bytes = reader.getDerBytes(); + PublicKey pubKey; + + if (PEMReader.PUBLIC_X509_MARKER.equals(reader.getBeginMarker())) { + KeySpec keySpec = new X509EncodedKeySpec(bytes); + KeyFactory fac = KeyFactory.getInstance("RSA"); + pubKey = fac.generatePublic(keySpec); + } else if (PEMReader.CERTIFICATE_X509_MARKER.equals(reader.getBeginMarker())) { + pubKey = getPublicKeyFromDerCert(bytes); + } else { + throw new IOException("Invalid PEM fileL: Unknown marker for " + + " public key or cert " + reader.getBeginMarker()); + } + + return pubKey; + } + + private PrivateKey getPrivateKeyFromDer(byte[] privateKeyObject) + throws GeneralSecurityException { + KeyFactory fac = KeyFactory.getInstance("RSA"); + EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject); + return fac.generatePrivate(privKeySpec); + } + + private PrivateKey getPrivateKeyFromPem(String pem) + throws GeneralSecurityException, IOException { + + InputStream stream = new ByteArrayInputStream( + pem.getBytes("UTF-8")); + + PEMReader reader = new PEMReader(stream); + byte[] bytes = reader.getDerBytes(); + KeySpec keySpec; + + if (PEMReader.PRIVATE_PKCS1_MARKER.equals(reader.getBeginMarker())) { + keySpec = (new PKCS1EncodedKeySpec(bytes)).getKeySpec(); + } else if (PEMReader.PRIVATE_PKCS8_MARKER.equals(reader.getBeginMarker())) { + keySpec = new PKCS8EncodedKeySpec(bytes); + } else { + throw new IOException("Invalid PEM file: Unknown marker " + + "for private key " + reader.getBeginMarker()); + } + + KeyFactory fac = KeyFactory.getInstance("RSA"); + return fac.generatePrivate(keySpec); + } + + @Override + protected String getSignature(String baseString) throws OAuthException { + try { + byte[] signature = sign(baseString.getBytes(OAuth.ENCODING)); + return base64Encode(signature); + } catch (UnsupportedEncodingException e) { + throw new OAuthException(e); + } catch (GeneralSecurityException e) { + throw new OAuthException(e); + } + } + + @Override + protected boolean isValid(String signature, String baseString) + throws OAuthException { + try { + return verify(decodeBase64(signature), + baseString.getBytes(OAuth.ENCODING)); + } catch (UnsupportedEncodingException e) { + throw new OAuthException(e); + } catch (GeneralSecurityException e) { + throw new OAuthException(e); + } + } + + private byte[] sign(byte[] message) throws GeneralSecurityException { + if (privateKey == null) { + throw new IllegalStateException("need to set private key with " + + "OAuthConsumer.setProperty when " + + "generating RSA-SHA1 signatures."); + } + Signature signer = Signature.getInstance("SHA1withRSA"); + signer.initSign(privateKey); + signer.update(message); + return signer.sign(); + } + + private boolean verify(byte[] signature, byte[] message) + throws GeneralSecurityException { + if (publicKey == null) { + throw new IllegalStateException("need to set public key with " + + " OAuthConsumer.setProperty when " + + "verifying RSA-SHA1 signatures."); + } + Signature verifier = Signature.getInstance("SHA1withRSA"); + verifier.initVerify(publicKey); + verifier.update(message); + return verifier.verify(signature); + } + + /** + * Load private key from various sources, including + *

    + *
  • A PrivateKey object + *
  • A string buffer for PEM + *
  • A byte array with PKCS#8 encoded key + *
+ * @param privateKeyObject + * @return The private key + * @throws IOException + * @throws GeneralSecurityException + */ + private PrivateKey loadPrivateKey(Object privateKeyObject) + throws IOException, GeneralSecurityException { + + PrivateKey privateKey; + + if (privateKeyObject instanceof PrivateKey) { + privateKey = (PrivateKey)privateKeyObject; + } else if (privateKeyObject instanceof String) { + try { + // PEM Reader's native string constructor is for filename. + privateKey = getPrivateKeyFromPem((String)privateKeyObject); + } catch (IOException e) { + // Check if it's PEM with markers stripped + privateKey = getPrivateKeyFromDer( + decodeBase64((String)privateKeyObject)); + } + } else if (privateKeyObject instanceof byte[]) { + privateKey = getPrivateKeyFromDer((byte[])privateKeyObject); + } else { + throw new IllegalArgumentException( + "Private key set through RSA_SHA1.PRIVATE_KEY must be of " + + "type PrivateKey, String or byte[] and not " + + privateKeyObject.getClass().getName()); + } + + return privateKey; + } + + /** + * Load a public key from key file or certificate. It can load from + * different sources depending on the type of the input, + *
    + *
  • A PublicKey object + *
  • A X509Certificate object + *
  • A string buffer for PEM + *
  • A byte array with X509 encoded key or certificate + *
+ * + * @param publicKeyObject The object for public key or certificate + * @param isCert True if this object is provided as Certificate + * @return The public key + * @throws IOException + * @throws GeneralSecurityException + */ + private PublicKey loadPublicKey(Object publicKeyObject, boolean isCert) + throws IOException, GeneralSecurityException { + + PublicKey publicKey; + + if (publicKeyObject instanceof PublicKey) { + publicKey = (PublicKey)publicKeyObject; + } else if (publicKeyObject instanceof X509Certificate) { + publicKey = ((X509Certificate) publicKeyObject).getPublicKey(); + } else if (publicKeyObject instanceof String) { + try { + publicKey = getPublicKeyFromPem((String)publicKeyObject); + } catch (IOException e) { + // Check if it's marker-stripped PEM for public key + if (isCert) + throw e; + publicKey = getPublicKeyFromDer( + decodeBase64((String)publicKeyObject)); + } + } else if (publicKeyObject instanceof byte[]) { + if (isCert) + publicKey = getPublicKeyFromDerCert((byte[])publicKeyObject); + else + publicKey = getPublicKeyFromDer((byte[])publicKeyObject); + } else { + String source; + if (isCert) + source = "RSA_SHA1.X509_CERTIFICATE"; + else + source = "RSA_SHA1.PUBLIC_KEY"; + throw new IllegalArgumentException( + "Public key or certificate set through " + source + " must be of " + + "type PublicKey, String or byte[], and not " + + publicKeyObject.getClass().getName()); + } + + return publicKey; + } +} Index: 3rdParty_sources/oauth/net/oauth/signature/package-info.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/package-info.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/package-info.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,4 @@ +/** + * Classes to compute and verify digital signatures. + */ +package net.oauth.signature; Index: 3rdParty_sources/oauth/net/oauth/signature/pem/Asn1Object.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/pem/Asn1Object.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/pem/Asn1Object.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,150 @@ +/**************************************************************************** + * Copyright (c) 1998-2009 AOL LLC. + * + * 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 net.oauth.signature.pem; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * An ASN.1 TLV. The object is not parsed. It can + * only handle integers and strings. + * + * @author zhang + * + */ +class Asn1Object { + + protected final int type; + protected final int length; + protected final byte[] value; + protected final int tag; + + /** + * Construct a ASN.1 TLV. The TLV could be either a + * constructed or primitive entity. + * + *

The first byte in DER encoding is made of following fields, + *

+     *-------------------------------------------------
+     *|Bit 8|Bit 7|Bit 6|Bit 5|Bit 4|Bit 3|Bit 2|Bit 1|
+     *-------------------------------------------------
+     *|  Class    | CF  |     +      Type             |
+     *-------------------------------------------------
+     * 
+ *
    + *
  • Class: Universal, Application, Context or Private + *
  • CF: Constructed flag. If 1, the field is constructed. + *
  • Type: This is actually called tag in ASN.1. It + * indicates data type (Integer, String) or a construct + * (sequence, choice, set). + *
+ * + * @param tag Tag or Identifier + * @param length Length of the field + * @param value Encoded octet string for the field. + */ + public Asn1Object(int tag, int length, byte[] value) { + this.tag = tag; + this.type = tag & 0x1F; + this.length = length; + this.value = value; + } + + public int getType() { + return type; + } + + public int getLength() { + return length; + } + + public byte[] getValue() { + return value; + } + + public boolean isConstructed() { + return (tag & DerParser.CONSTRUCTED) == DerParser.CONSTRUCTED; + } + + /** + * For constructed field, return a parser for its content. + * + * @return A parser for the construct. + * @throws IOException + */ + public DerParser getParser() throws IOException { + if (!isConstructed()) + throw new IOException("Invalid DER: can't parse primitive entity"); //$NON-NLS-1$ + + return new DerParser(value); + } + + /** + * Get the value as integer + * + * @return BigInteger + * @throws IOException + */ + public BigInteger getInteger() throws IOException { + if (type != DerParser.INTEGER) + throw new IOException("Invalid DER: object is not integer"); //$NON-NLS-1$ + + return new BigInteger(value); + } + + /** + * Get value as string. Most strings are treated + * as Latin-1. + * + * @return Java string + * @throws IOException + */ + public String getString() throws IOException { + + String encoding; + + switch (type) { + + // Not all are Latin-1 but it's the closest thing + case DerParser.NUMERIC_STRING: + case DerParser.PRINTABLE_STRING: + case DerParser.VIDEOTEX_STRING: + case DerParser.IA5_STRING: + case DerParser.GRAPHIC_STRING: + case DerParser.ISO646_STRING: + case DerParser.GENERAL_STRING: + encoding = "ISO-8859-1"; //$NON-NLS-1$ + break; + + case DerParser.BMP_STRING: + encoding = "UTF-16BE"; //$NON-NLS-1$ + break; + + case DerParser.UTF8_STRING: + encoding = "UTF-8"; //$NON-NLS-1$ + break; + + case DerParser.UNIVERSAL_STRING: + throw new IOException("Invalid DER: can't handle UCS-4 string"); //$NON-NLS-1$ + + default: + throw new IOException("Invalid DER: object is not a string"); //$NON-NLS-1$ + } + + return new String(value, encoding); + } +} Index: 3rdParty_sources/oauth/net/oauth/signature/pem/DerParser.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/pem/DerParser.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/pem/DerParser.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,170 @@ +/**************************************************************************** + * Copyright (c) 1998-2009 AOL LLC. + * + * 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 net.oauth.signature.pem; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; + +/** + * A bare-minimum ASN.1 DER decoder, just having enough functions to + * decode PKCS#1 private keys. Especially, it doesn't handle explicitly + * tagged types with an outer tag. + * + *

This parser can only handle one layer. To parse nested constructs, + * get a new parser for each layer using Asn1Object.getParser(). + * + *

There are many DER decoders in JRE but using them will tie this + * program to a specific JCE/JVM. + * + * @author zhang + * + */ +class DerParser { + + // Classes + public final static int UNIVERSAL = 0x00; + public final static int APPLICATION = 0x40; + public final static int CONTEXT = 0x80; + public final static int PRIVATE = 0xC0; + + // Constructed Flag + public final static int CONSTRUCTED = 0x20; + + // Tag and data types + public final static int ANY = 0x00; + public final static int BOOLEAN = 0x01; + public final static int INTEGER = 0x02; + public final static int BIT_STRING = 0x03; + public final static int OCTET_STRING = 0x04; + public final static int NULL = 0x05; + public final static int OBJECT_IDENTIFIER = 0x06; + public final static int REAL = 0x09; + public final static int ENUMERATED = 0x0a; + public final static int RELATIVE_OID = 0x0d; + + public final static int SEQUENCE = 0x10; + public final static int SET = 0x11; + + public final static int NUMERIC_STRING = 0x12; + public final static int PRINTABLE_STRING = 0x13; + public final static int T61_STRING = 0x14; + public final static int VIDEOTEX_STRING = 0x15; + public final static int IA5_STRING = 0x16; + public final static int GRAPHIC_STRING = 0x19; + public final static int ISO646_STRING = 0x1A; + public final static int GENERAL_STRING = 0x1B; + + public final static int UTF8_STRING = 0x0C; + public final static int UNIVERSAL_STRING = 0x1C; + public final static int BMP_STRING = 0x1E; + + public final static int UTC_TIME = 0x17; + public final static int GENERALIZED_TIME = 0x18; + + protected InputStream in; + + /** + * Create a new DER decoder from an input stream. + * + * @param in + * The DER encoded stream + */ + public DerParser(InputStream in) throws IOException { + this.in = in; + } + + /** + * Create a new DER decoder from a byte array. + * + * @param The + * encoded bytes + * @throws IOException + */ + public DerParser(byte[] bytes) throws IOException { + this(new ByteArrayInputStream(bytes)); + } + + /** + * Read next object. If it's constructed, the value holds + * encoded content and it should be parsed by a new + * parser from Asn1Object.getParser. + * + * @return A object + * @throws IOException + */ + public Asn1Object read() throws IOException { + int tag = in.read(); + + if (tag == -1) + throw new IOException("Invalid DER: stream too short, missing tag"); //$NON-NLS-1$ + + int length = getLength(); + + byte[] value = new byte[length]; + int n = in.read(value); + if (n < length) + throw new IOException("Invalid DER: stream too short, missing value"); //$NON-NLS-1$ + + Asn1Object o = new Asn1Object(tag, length, value); + + return o; + } + + /** + * Decode the length of the field. Can only support length + * encoding up to 4 octets. + * + *

In BER/DER encoding, length can be encoded in 2 forms, + *

    + *
  • Short form. One octet. Bit 8 has value "0" and bits 7-1 + * give the length. + *
  • Long form. Two to 127 octets (only 4 is supported here). + * Bit 8 of first octet has value "1" and bits 7-1 give the + * number of additional length octets. Second and following + * octets give the length, base 256, most significant digit first. + *
+ * @return The length as integer + * @throws IOException + */ + private int getLength() throws IOException { + + int i = in.read(); + if (i == -1) + throw new IOException("Invalid DER: length missing"); //$NON-NLS-1$ + + // A single byte short length + if ((i & ~0x7F) == 0) + return i; + + int num = i & 0x7F; + + // We can't handle length longer than 4 bytes + if ( i >= 0xFF || num > 4) + throw new IOException("Invalid DER: length field too big (" //$NON-NLS-1$ + + i + ")"); //$NON-NLS-1$ + + byte[] bytes = new byte[num]; + int n = in.read(bytes); + if (n < num) + throw new IOException("Invalid DER: length too short"); //$NON-NLS-1$ + + return new BigInteger(1, bytes).intValue(); + } + +} Index: 3rdParty_sources/oauth/net/oauth/signature/pem/PEMReader.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/pem/PEMReader.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/pem/PEMReader.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,134 @@ +/**************************************************************************** + * Copyright (c) 1998-2009 AOL LLC. + * + * 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. + * + **************************************************************************** + * + * @author: zhang + * @version: $Revision$ + * @created: Apr 24, 2009 + * + * Description: A class to decode PEM files + * + ****************************************************************************/ +package net.oauth.signature.pem; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import net.oauth.signature.OAuthSignatureMethod; + +/** + * This class convert PEM into byte array. The begin marker + * is saved and it can be used to determine the type of the + * PEM file. + * + * @author zhang + */ +public class PEMReader { + + // Begin markers for all supported PEM files + public static final String PRIVATE_PKCS1_MARKER = + "-----BEGIN RSA PRIVATE KEY-----"; + public static final String PRIVATE_PKCS8_MARKER = + "-----BEGIN PRIVATE KEY-----"; + public static final String CERTIFICATE_X509_MARKER = + "-----BEGIN CERTIFICATE-----"; + public static final String PUBLIC_X509_MARKER = + "-----BEGIN PUBLIC KEY-----"; + + private static final String BEGIN_MARKER = "-----BEGIN "; + + private InputStream stream; + private byte[] derBytes; + private String beginMarker; + + public PEMReader(InputStream inStream) throws IOException { + stream = inStream; + readFile(); + } + + public PEMReader(byte[] buffer) throws IOException { + this(new ByteArrayInputStream(buffer)); + } + + public PEMReader(String fileName) throws IOException { + this(new FileInputStream(fileName)); + } + + public byte[] getDerBytes() { + return derBytes; + } + + public String getBeginMarker() { + return beginMarker; + } + + /** + * Read the PEM file and save the DER encoded octet + * stream and begin marker. + * + * @throws IOException + */ + protected void readFile() throws IOException { + + String line; + BufferedReader reader = new BufferedReader( + new InputStreamReader(stream)); + try { + while ((line = reader.readLine()) != null) + { + if (line.indexOf(BEGIN_MARKER) != -1) + { + beginMarker = line.trim(); + String endMarker = beginMarker.replace("BEGIN", "END"); + derBytes = readBytes(reader, endMarker); + return; + } + } + throw new IOException("Invalid PEM file: no begin marker"); + } finally { + reader.close(); + } + } + + + /** + * Read the lines between BEGIN and END marker and convert + * the Base64 encoded content into binary byte array. + * + * @return DER encoded octet stream + * @throws IOException + */ + private byte[] readBytes(BufferedReader reader, String endMarker) throws IOException + { + String line = null; + StringBuffer buf = new StringBuffer(); + + while ((line = reader.readLine()) != null) + { + if (line.indexOf(endMarker) != -1) { + + return OAuthSignatureMethod.decodeBase64(buf.toString()); + } + + buf.append(line.trim()); + } + + throw new IOException("Invalid PEM file: No end marker"); + } +} Index: 3rdParty_sources/oauth/net/oauth/signature/pem/PKCS1EncodedKeySpec.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/pem/PKCS1EncodedKeySpec.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/pem/PKCS1EncodedKeySpec.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,116 @@ +/**************************************************************************** + * Copyright (c) 1998-2009 AOL LLC. + * + * 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. + * + **************************************************************************** + * + * @author: zhang + * @version: $Revision$ + * @created: Apr 24, 2009 + * + * Description: A KeySpec for PKCS#1 encoded RSA private key + * + ****************************************************************************/ +package net.oauth.signature.pem; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.spec.RSAPrivateCrtKeySpec; + +/** + * PKCS#1 encoded private key is commonly used with OpenSSL. It provides CRT parameters + * so the private key operation can be much faster than using exponent/modulus alone, + * which is the case for PKCS#8 encoded key. + * + *

Unfortunately, JCE doesn't have an API to decode the DER. This class takes DER + * buffer and decoded into CRT key. + * + * @author zhang + */ +public class PKCS1EncodedKeySpec { + + private RSAPrivateCrtKeySpec keySpec; + + /** + * Create a PKCS#1 keyspec from DER encoded buffer + * + * @param keyBytes DER encoded octet stream + * @throws IOException + */ + public PKCS1EncodedKeySpec(byte[] keyBytes) throws IOException { + decode(keyBytes); + } + + /** + * Get the key spec that JCE understands. + * + * @return CRT keyspec defined by JCE + */ + public RSAPrivateCrtKeySpec getKeySpec() { + return keySpec; + } + + /** + * Decode PKCS#1 encoded private key into RSAPrivateCrtKeySpec. + * + *

The ASN.1 syntax for the private key with CRT is + * + *

+     * -- 
+     * -- Representation of RSA private key with information for the CRT algorithm.
+     * --
+     * RSAPrivateKey ::= SEQUENCE {
+     *   version           Version, 
+     *   modulus           INTEGER,  -- n
+     *   publicExponent    INTEGER,  -- e
+     *   privateExponent   INTEGER,  -- d
+     *   prime1            INTEGER,  -- p
+     *   prime2            INTEGER,  -- q
+     *   exponent1         INTEGER,  -- d mod (p-1)
+     *   exponent2         INTEGER,  -- d mod (q-1) 
+     *   coefficient       INTEGER,  -- (inverse of q) mod p
+     *   otherPrimeInfos   OtherPrimeInfos OPTIONAL 
+     * }
+     * 
+ * + * @param keyBytes PKCS#1 encoded key + * @throws IOException + */ + + private void decode(byte[] keyBytes) throws IOException { + + DerParser parser = new DerParser(keyBytes); + + Asn1Object sequence = parser.read(); + if (sequence.getType() != DerParser.SEQUENCE) + throw new IOException("Invalid DER: not a sequence"); //$NON-NLS-1$ + + // Parse inside the sequence + parser = sequence.getParser(); + + parser.read(); // Skip version + BigInteger modulus = parser.read().getInteger(); + BigInteger publicExp = parser.read().getInteger(); + BigInteger privateExp = parser.read().getInteger(); + BigInteger prime1 = parser.read().getInteger(); + BigInteger prime2 = parser.read().getInteger(); + BigInteger exp1 = parser.read().getInteger(); + BigInteger exp2 = parser.read().getInteger(); + BigInteger crtCoef = parser.read().getInteger(); + + keySpec = new RSAPrivateCrtKeySpec( + modulus, publicExp, privateExp, prime1, prime2, + exp1, exp2, crtCoef); + } +} Index: 3rdParty_sources/oauth/net/oauth/signature/pem/package-info.java =================================================================== diff -u --- 3rdParty_sources/oauth/net/oauth/signature/pem/package-info.java (revision 0) +++ 3rdParty_sources/oauth/net/oauth/signature/pem/package-info.java (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -0,0 +1,4 @@ +/** + * Classes to handle cryptographic data in PEM formats. + */ +package net.oauth.signature.pem; Index: 3rdParty_sources/versions.txt =================================================================== diff -u -r61e489a64fd46325ed8b232df23b9ee923ca9217 -r92d4f7d34c60b79bfec1ca66a0fa5239fedd082c --- 3rdParty_sources/versions.txt (.../versions.txt) (revision 61e489a64fd46325ed8b232df23b9ee923ca9217) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision 92d4f7d34c60b79bfec1ca66a0fa5239fedd082c) @@ -32,6 +32,10 @@ MySQL Connector/J 5.1.38 +oauth 20100527 + +oauth-provider 20100527 + opensaml 2.6.0 openws 1.5.0 Index: lams_build/lib/basiclti-util/oauth-20100527.jar =================================================================== diff -u -r37286da230b2bacd7f7764d630c639c6b51dcf24 -r92d4f7d34c60b79bfec1ca66a0fa5239fedd082c Binary files differ Index: lams_build/lib/basiclti-util/oauth-provider-20100527.jar =================================================================== diff -u -r37286da230b2bacd7f7764d630c639c6b51dcf24 -r92d4f7d34c60b79bfec1ca66a0fa5239fedd082c Binary files differ