summaryrefslogtreecommitdiff
path: root/core/src/main/java/net/oauth/signature
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/net/oauth/signature')
-rwxr-xr-xcore/src/main/java/net/oauth/signature/HMAC_SHA1.java103
-rwxr-xr-xcore/src/main/java/net/oauth/signature/OAuthSignatureMethod.java300
-rwxr-xr-xcore/src/main/java/net/oauth/signature/PLAINTEXT.java65
-rwxr-xr-xcore/src/main/java/net/oauth/signature/RSA_SHA1.java239
4 files changed, 707 insertions, 0 deletions
diff --git a/core/src/main/java/net/oauth/signature/HMAC_SHA1.java b/core/src/main/java/net/oauth/signature/HMAC_SHA1.java
new file mode 100755
index 0000000..dee72fd
--- /dev/null
+++ b/core/src/main/java/net/oauth/signature/HMAC_SHA1.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+/**
+ * @author John Kristian
+ * @hide
+ */
+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 Arrays.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);
+ }
+
+}
diff --git a/core/src/main/java/net/oauth/signature/OAuthSignatureMethod.java b/core/src/main/java/net/oauth/signature/OAuthSignatureMethod.java
new file mode 100755
index 0000000..967153d
--- /dev/null
+++ b/core/src/main/java/net/oauth/signature/OAuthSignatureMethod.java
@@ -0,0 +1,300 @@
+/*
+ * 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.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 net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthConsumer;
+import net.oauth.OAuthException;
+import net.oauth.OAuthMessage;
+import net.oauth.OAuthProblemException;
+import org.apache.commons.codec.binary.Base64;
+
+/**
+ * A pair of algorithms for computing and verifying an OAuth digital signature.
+ *
+ * @author John Kristian
+ * @hide
+ */
+public abstract class OAuthSignatureMethod {
+
+ /** 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)) {
+ 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<Map.Entry<String, String>> 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<Map.Entry<String, String>>();
+ 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<? extends Map.Entry> parameters) throws IOException {
+ if (parameters == null) {
+ return "";
+ }
+ List<ComparableParameter> p = new ArrayList<ComparableParameter>(
+ 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));
+ }
+
+ public static byte[] decodeBase64(String s) {
+ return BASE64.decode(s.getBytes());
+ }
+
+ public static String base64Encode(byte[] b) {
+ return new String(BASE64.encode(b));
+ }
+
+ 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(
+ "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) {
+ NAME_TO_CLASS.put(name, clazz);
+ }
+
+ private static final Map<String, Class> NAME_TO_CLASS = new ConcurrentHashMap<String, Class>();
+ 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> {
+
+ 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<Map.Entry> getParameters(
+ Collection<ComparableParameter> parameters) {
+ if (parameters == null) {
+ return null;
+ }
+ List<Map.Entry> list = new ArrayList<Map.Entry>(parameters.size());
+ for (ComparableParameter parameter : parameters) {
+ list.add(parameter.value);
+ }
+ return list;
+ }
+
+}
diff --git a/core/src/main/java/net/oauth/signature/PLAINTEXT.java b/core/src/main/java/net/oauth/signature/PLAINTEXT.java
new file mode 100755
index 0000000..910f903
--- /dev/null
+++ b/core/src/main/java/net/oauth/signature/PLAINTEXT.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+/**
+ * @author John Kristian
+ * @hide
+ */
+class PLAINTEXT extends OAuthSignatureMethod {
+
+ @Override
+ public String getSignature(String baseString) {
+ return getSignature();
+ }
+
+ @Override
+ protected boolean isValid(String signature, String baseString)
+ throws OAuthException {
+ return signature.equals(getSignature());
+ }
+
+ 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);
+ }
+
+}
diff --git a/core/src/main/java/net/oauth/signature/RSA_SHA1.java b/core/src/main/java/net/oauth/signature/RSA_SHA1.java
new file mode 100755
index 0000000..0aa99f1
--- /dev/null
+++ b/core/src/main/java/net/oauth/signature/RSA_SHA1.java
@@ -0,0 +1,239 @@
+/*
+ * 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.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.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import net.oauth.OAuth;
+import net.oauth.OAuthAccessor;
+import net.oauth.OAuthException;
+
+/**
+ * Class to handle RSA-SHA1 signatures on OAuth requests. 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, or byte[]. In the latter two
+ * cases, the key must be PKCS#8-encoded (byte[]) or PKCS#8-encoded and
+ * then Base64-encoded (String).
+ *
+ * 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
+ * @hide
+ *
+ */
+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);
+
+ Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY);
+ try {
+ if (privateKeyObject != null) {
+ if (privateKeyObject instanceof PrivateKey) {
+ privateKey = (PrivateKey)privateKeyObject;
+ } else if (privateKeyObject instanceof String) {
+ privateKey = getPrivateKeyFromPem((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());
+ }
+ }
+
+ Object publicKeyObject = accessor.consumer.getProperty(PUBLIC_KEY);
+ if (publicKeyObject != null) {
+ if (publicKeyObject instanceof PublicKey) {
+ publicKey = (PublicKey)publicKeyObject;
+ } else if (publicKeyObject instanceof String) {
+ publicKey = getPublicKeyFromPem((String)publicKeyObject);
+ } else if (publicKeyObject instanceof byte[]) {
+ publicKey = getPublicKeyFromDer((byte[])publicKeyObject);
+ } else {
+ throw new IllegalArgumentException(
+ "Public key set through RSA_SHA1.PRIVATE_KEY must be of " +
+ "type PublicKey, String, or byte[], and not " +
+ publicKeyObject.getClass().getName());
+ }
+ } else { // public key was null. perhaps they gave us a X509 cert.
+ Object certObject = accessor.consumer.getProperty(X509_CERTIFICATE);
+ if (certObject != null) {
+ if (certObject instanceof X509Certificate) {
+ publicKey = ((X509Certificate) certObject).getPublicKey();
+ } else if (certObject instanceof String) {
+ publicKey = getPublicKeyFromPemCert((String)certObject);
+ } else if (certObject instanceof byte[]) {
+ publicKey = getPublicKeyFromDerCert((byte[])certObject);
+ } else {
+ throw new IllegalArgumentException(
+ "X509Certificate set through RSA_SHA1.X509_CERTIFICATE" +
+ " must be of type X509Certificate, String, or byte[]," +
+ " and not " + certObject.getClass().getName());
+ }
+ }
+ }
+ } catch (GeneralSecurityException e) {
+ throw new OAuthException(e);
+ }
+ }
+
+ private PublicKey getPublicKeyFromPemCert(String certObject)
+ throws GeneralSecurityException {
+ CertificateFactory fac = CertificateFactory.getInstance("X509");
+ ByteArrayInputStream in = new ByteArrayInputStream(certObject.getBytes());
+ X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
+ return cert.getPublicKey();
+ }
+
+ 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 publicKeyObject)
+ throws GeneralSecurityException {
+ return getPublicKeyFromDer(decodeBase64(publicKeyObject));
+ }
+
+ 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 privateKeyObject)
+ throws GeneralSecurityException {
+ return getPrivateKeyFromDer(decodeBase64(privateKeyObject));
+ }
+
+ @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);
+ }
+}