| /* |
| * 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); |
| } |
| } |