Home | History | Annotate | Download | only in signature
      1 /*
      2  * Copyright 2007 Google, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package net.oauth.signature;
     18 
     19 import java.io.ByteArrayInputStream;
     20 import java.io.UnsupportedEncodingException;
     21 import java.security.GeneralSecurityException;
     22 import java.security.KeyFactory;
     23 import java.security.PrivateKey;
     24 import java.security.PublicKey;
     25 import java.security.Signature;
     26 import java.security.cert.CertificateFactory;
     27 import java.security.cert.X509Certificate;
     28 import java.security.spec.EncodedKeySpec;
     29 import java.security.spec.PKCS8EncodedKeySpec;
     30 import java.security.spec.X509EncodedKeySpec;
     31 
     32 import net.oauth.OAuth;
     33 import net.oauth.OAuthAccessor;
     34 import net.oauth.OAuthException;
     35 
     36 /**
     37  * Class to handle RSA-SHA1 signatures on OAuth requests. A consumer
     38  * that wishes to use public-key signatures on messages does not need
     39  * a shared secret with the service provider, but it needs a private
     40  * RSA signing key. You create it like this:
     41  *
     42  * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key,
     43  *                                     null, provider);
     44  * c.setProperty(RSA_SHA1.PRIVATE_KEY, consumer_privateRSAKey);
     45  *
     46  * consumer_privateRSAKey must be an RSA signing key and
     47  * of type java.security.PrivateKey, String, or byte[]. In the latter two
     48  * cases, the key must be PKCS#8-encoded (byte[]) or PKCS#8-encoded and
     49  * then Base64-encoded (String).
     50  *
     51  * A service provider that wishes to verify signatures made by such a
     52  * consumer does not need a shared secret with the consumer, but it needs
     53  * to know the consumer's public key. You create the necessary
     54  * OAuthConsumer object (on the service provider's side) like this:
     55  *
     56  * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key,
     57  *                                     null, provider);
     58  * c.setProperty(RSA_SHA1.PUBLIC_KEY, consumer_publicRSAKey);
     59  *
     60  * consumer_publicRSAKey must be the consumer's public RSAkey and
     61  * of type java.security.PublicKey, String, or byte[]. In the latter two
     62  * cases, the key must be X509-encoded (byte[]) or X509-encoded and
     63  * then Base64-encoded (String).
     64  *
     65  * Alternatively, a service provider that wishes to verify signatures made
     66  * by such a consumer can use a X509 certificate containing the consumer's
     67  * public key. You create the necessary OAuthConsumer object (on the service
     68  * provider's side) like this:
     69  *
     70  * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key,
     71  *                                     null, provider);
     72  * c.setProperty(RSA_SHA1.X509_CERTIFICATE, consumer_cert);
     73  *
     74  * consumer_cert must be a X509 Certificate containing the consumer's public
     75  * key and be of type java.security.cert.X509Certificate, String,
     76  * or byte[]. In the latter two cases, the certificate must be DER-encoded
     77  * (byte[]) or PEM-encoded (String).
     78  *
     79  * @author Dirk Balfanz
     80  * @hide
     81  *
     82  */
     83 public class RSA_SHA1 extends OAuthSignatureMethod {
     84 
     85     final static public String PRIVATE_KEY = "RSA-SHA1.PrivateKey";
     86     final static public String PUBLIC_KEY = "RSA-SHA1.PublicKey";
     87     final static public String X509_CERTIFICATE = "RSA-SHA1.X509Certificate";
     88 
     89     private PrivateKey privateKey = null;
     90     private PublicKey publicKey = null;
     91 
     92     @Override
     93     protected void initialize(String name, OAuthAccessor accessor)
     94     throws OAuthException {
     95         super.initialize(name, accessor);
     96 
     97         Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY);
     98         try {
     99             if (privateKeyObject != null) {
    100                 if (privateKeyObject instanceof PrivateKey) {
    101                     privateKey = (PrivateKey)privateKeyObject;
    102                 } else if (privateKeyObject instanceof String) {
    103                     privateKey = getPrivateKeyFromPem((String)privateKeyObject);
    104                 } else if (privateKeyObject instanceof byte[]) {
    105                     privateKey = getPrivateKeyFromDer((byte[])privateKeyObject);
    106                 } else {
    107                     throw new IllegalArgumentException(
    108                             "Private key set through RSA_SHA1.PRIVATE_KEY must be of " +
    109                             "type PrivateKey, String, or byte[], and not " +
    110                             privateKeyObject.getClass().getName());
    111                 }
    112             }
    113 
    114             Object publicKeyObject = accessor.consumer.getProperty(PUBLIC_KEY);
    115             if (publicKeyObject != null) {
    116                 if (publicKeyObject instanceof PublicKey) {
    117                     publicKey = (PublicKey)publicKeyObject;
    118                 } else if (publicKeyObject instanceof String) {
    119                     publicKey = getPublicKeyFromPem((String)publicKeyObject);
    120                 } else if (publicKeyObject instanceof byte[]) {
    121                     publicKey = getPublicKeyFromDer((byte[])publicKeyObject);
    122                 } else {
    123                     throw new IllegalArgumentException(
    124                             "Public key set through RSA_SHA1.PRIVATE_KEY must be of " +
    125                             "type PublicKey, String, or byte[], and not " +
    126                             publicKeyObject.getClass().getName());
    127                 }
    128             } else {  // public key was null. perhaps they gave us a X509 cert.
    129                 Object certObject = accessor.consumer.getProperty(X509_CERTIFICATE);
    130                 if (certObject != null) {
    131                     if (certObject instanceof X509Certificate) {
    132                         publicKey = ((X509Certificate) certObject).getPublicKey();
    133                     } else if (certObject instanceof String) {
    134                         publicKey = getPublicKeyFromPemCert((String)certObject);
    135                     } else if (certObject instanceof byte[]) {
    136                         publicKey = getPublicKeyFromDerCert((byte[])certObject);
    137                     } else {
    138                         throw new IllegalArgumentException(
    139                                 "X509Certificate set through RSA_SHA1.X509_CERTIFICATE" +
    140                                 " must be of type X509Certificate, String, or byte[]," +
    141                                 " and not " + certObject.getClass().getName());
    142                     }
    143                 }
    144             }
    145         } catch (GeneralSecurityException e) {
    146             throw new OAuthException(e);
    147         }
    148     }
    149 
    150     private PublicKey getPublicKeyFromPemCert(String certObject)
    151             throws GeneralSecurityException {
    152         CertificateFactory fac = CertificateFactory.getInstance("X509");
    153         ByteArrayInputStream in = new ByteArrayInputStream(certObject.getBytes());
    154         X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
    155         return cert.getPublicKey();
    156     }
    157 
    158     private PublicKey getPublicKeyFromDerCert(byte[] certObject)
    159             throws GeneralSecurityException {
    160         CertificateFactory fac = CertificateFactory.getInstance("X509");
    161         ByteArrayInputStream in = new ByteArrayInputStream(certObject);
    162         X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
    163         return cert.getPublicKey();
    164     }
    165 
    166     private PublicKey getPublicKeyFromDer(byte[] publicKeyObject)
    167             throws GeneralSecurityException {
    168         KeyFactory fac = KeyFactory.getInstance("RSA");
    169         EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyObject);
    170         return fac.generatePublic(pubKeySpec);
    171     }
    172 
    173     private PublicKey getPublicKeyFromPem(String publicKeyObject)
    174             throws GeneralSecurityException {
    175         return getPublicKeyFromDer(decodeBase64(publicKeyObject));
    176     }
    177 
    178     private PrivateKey getPrivateKeyFromDer(byte[] privateKeyObject)
    179             throws GeneralSecurityException {
    180         KeyFactory fac = KeyFactory.getInstance("RSA");
    181         EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject);
    182         return fac.generatePrivate(privKeySpec);
    183     }
    184 
    185     private PrivateKey getPrivateKeyFromPem(String privateKeyObject)
    186             throws GeneralSecurityException {
    187         return getPrivateKeyFromDer(decodeBase64(privateKeyObject));
    188     }
    189 
    190     @Override
    191     protected String getSignature(String baseString) throws OAuthException {
    192         try {
    193             byte[] signature = sign(baseString.getBytes(OAuth.ENCODING));
    194             return base64Encode(signature);
    195         } catch (UnsupportedEncodingException e) {
    196             throw new OAuthException(e);
    197         } catch (GeneralSecurityException e) {
    198             throw new OAuthException(e);
    199         }
    200     }
    201 
    202     @Override
    203     protected boolean isValid(String signature, String baseString)
    204             throws OAuthException {
    205         try {
    206             return verify(decodeBase64(signature),
    207                           baseString.getBytes(OAuth.ENCODING));
    208         } catch (UnsupportedEncodingException e) {
    209             throw new OAuthException(e);
    210         } catch (GeneralSecurityException e) {
    211             throw new OAuthException(e);
    212         }
    213     }
    214 
    215     private byte[] sign(byte[] message) throws GeneralSecurityException {
    216         if (privateKey == null) {
    217             throw new IllegalStateException("need to set private key with " +
    218                                             "OAuthConsumer.setProperty when " +
    219                                             "generating RSA-SHA1 signatures.");
    220         }
    221         Signature signer = Signature.getInstance("SHA1withRSA");
    222         signer.initSign(privateKey);
    223         signer.update(message);
    224         return signer.sign();
    225     }
    226 
    227     private boolean verify(byte[] signature, byte[] message)
    228             throws GeneralSecurityException {
    229         if (publicKey == null) {
    230             throw new IllegalStateException("need to set public key with " +
    231                                             " OAuthConsumer.setProperty when " +
    232                                             "verifying RSA-SHA1 signatures.");
    233         }
    234         Signature verifier = Signature.getInstance("SHA1withRSA");
    235         verifier.initVerify(publicKey);
    236         verifier.update(message);
    237         return verifier.verify(signature);
    238     }
    239 }
    240