Home | History | Annotate | Download | only in net
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.net;
      6 
      7 import android.util.Log;
      8 
      9 import java.lang.reflect.Method;
     10 import java.security.NoSuchAlgorithmException;
     11 import java.security.PrivateKey;
     12 import java.security.Signature;
     13 import java.security.interfaces.DSAKey;
     14 import java.security.interfaces.DSAParams;
     15 import java.security.interfaces.DSAPrivateKey;
     16 import java.security.interfaces.ECKey;
     17 import java.security.interfaces.ECPrivateKey;
     18 import java.security.interfaces.RSAKey;
     19 import java.security.interfaces.RSAPrivateKey;
     20 import java.security.spec.ECParameterSpec;
     21 
     22 /**
     23  * Simple implementation of the AndroidKeyStore for use with an in-process Java KeyStore.
     24  */
     25 public class DefaultAndroidKeyStore implements AndroidKeyStore {
     26 
     27     private static final String TAG = "AndroidKeyStoreInProcessImpl";
     28 
     29     private static class DefaultAndroidPrivateKey implements AndroidPrivateKey {
     30         // The actual Java key being wrapped.
     31         final PrivateKey mKey;
     32         // Key store handling this key.
     33         final DefaultAndroidKeyStore mStore;
     34 
     35         DefaultAndroidPrivateKey(PrivateKey key, DefaultAndroidKeyStore store) {
     36             mKey = key;
     37             mStore = store;
     38         }
     39 
     40         PrivateKey getJavaKey() {
     41             return mKey;
     42         }
     43 
     44         @Override
     45         public AndroidKeyStore getKeyStore() {
     46             return mStore;
     47         }
     48     }
     49 
     50     public AndroidPrivateKey createKey(PrivateKey javaKey) {
     51         return new DefaultAndroidPrivateKey(javaKey, this);
     52     }
     53 
     54     @Override
     55     public byte[] getRSAKeyModulus(AndroidPrivateKey key) {
     56         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
     57         if (javaKey instanceof RSAKey) {
     58             return ((RSAKey) javaKey).getModulus().toByteArray();
     59         }
     60         Log.w(TAG, "Not a RSAKey instance!");
     61         return null;
     62     }
     63 
     64     @Override
     65     public byte[] getDSAKeyParamQ(AndroidPrivateKey key) {
     66         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
     67         if (javaKey instanceof DSAKey) {
     68             DSAParams params = ((DSAKey) javaKey).getParams();
     69             return params.getQ().toByteArray();
     70         }
     71         Log.w(TAG, "Not a DSAKey instance!");
     72         return null;
     73     }
     74 
     75     @Override
     76     public byte[] getECKeyOrder(AndroidPrivateKey key) {
     77         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
     78         if (javaKey instanceof ECKey) {
     79             ECParameterSpec params = ((ECKey) javaKey).getParams();
     80             return params.getOrder().toByteArray();
     81         }
     82         Log.w(TAG, "Not an ECKey instance!");
     83         return null;
     84     }
     85 
     86    @Override
     87     public byte[] getPrivateKeyEncodedBytes(AndroidPrivateKey key) {
     88         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
     89         return javaKey.getEncoded();
     90     }
     91 
     92     @Override
     93     public byte[] rawSignDigestWithPrivateKey(AndroidPrivateKey key,
     94                                                      byte[] message) {
     95         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
     96         // Get the Signature for this key.
     97         Signature signature = null;
     98         // Hint: Algorithm names come from:
     99         // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
    100         try {
    101             if (javaKey instanceof RSAPrivateKey) {
    102                 // IMPORTANT: Due to a platform bug, this will throw NoSuchAlgorithmException
    103                 // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
    104                 // See https://android-review.googlesource.com/#/c/40352/
    105                 signature = Signature.getInstance("NONEwithRSA");
    106             } else if (javaKey instanceof DSAPrivateKey) {
    107                 signature = Signature.getInstance("NONEwithDSA");
    108             } else if (javaKey instanceof ECPrivateKey) {
    109                 signature = Signature.getInstance("NONEwithECDSA");
    110             }
    111         } catch (NoSuchAlgorithmException e) {
    112             ;
    113         }
    114 
    115         if (signature == null) {
    116             Log.e(TAG, "Unsupported private key algorithm: " + javaKey.getAlgorithm());
    117             return null;
    118         }
    119 
    120         // Sign the message.
    121         try {
    122             signature.initSign(javaKey);
    123             signature.update(message);
    124             return signature.sign();
    125         } catch (Exception e) {
    126             Log.e(TAG, "Exception while signing message with " + javaKey.getAlgorithm() +
    127                         " private key: " + e);
    128             return null;
    129         }
    130     }
    131 
    132     @Override
    133     public int getPrivateKeyType(AndroidPrivateKey key) {
    134         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
    135         if (javaKey instanceof RSAPrivateKey)
    136             return PrivateKeyType.RSA;
    137         if (javaKey instanceof DSAPrivateKey)
    138             return PrivateKeyType.DSA;
    139         if (javaKey instanceof ECPrivateKey)
    140             return PrivateKeyType.ECDSA;
    141         else
    142             return PrivateKeyType.INVALID;
    143     }
    144 
    145     @Override
    146     public long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
    147         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
    148         // Sanity checks
    149         if (javaKey == null) {
    150             Log.e(TAG, "key == null");
    151             return 0;
    152         }
    153         if (!(javaKey instanceof RSAPrivateKey)) {
    154             Log.e(TAG, "does not implement RSAPrivateKey");
    155             return 0;
    156         }
    157         // First, check that this is a proper instance of OpenSSLRSAPrivateKey
    158         // or one of its sub-classes.
    159         Class<?> superClass;
    160         try {
    161             superClass = Class.forName(
    162                     "org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
    163         } catch (Exception e) {
    164             // This may happen if the target device has a completely different
    165             // implementation of the java.security APIs, compared to vanilla
    166             // Android. Highly unlikely, but still possible.
    167             Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
    168             return 0;
    169         }
    170         if (!superClass.isInstance(javaKey)) {
    171             // This may happen if the PrivateKey was not created by the "AndroidOpenSSL"
    172             // provider, which should be the default. That could happen if an OEM decided
    173             // to implement a different default provider. Also highly unlikely.
    174             Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" +
    175                        javaKey.getClass().getCanonicalName());
    176             return 0;
    177         }
    178 
    179         try {
    180             // Use reflection to invoke the 'getOpenSSLKey()' method on
    181             // the private key. This returns another Java object that wraps
    182             // a native EVP_PKEY. Note that the method is final, so calling
    183             // the superclass implementation is ok.
    184             Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
    185             getKey.setAccessible(true);
    186             Object opensslKey = null;
    187             try {
    188                 opensslKey = getKey.invoke(javaKey);
    189             } finally {
    190                 getKey.setAccessible(false);
    191             }
    192             if (opensslKey == null) {
    193                 // Bail when detecting OEM "enhancement".
    194                 Log.e(TAG, "getOpenSSLKey() returned null");
    195                 return 0;
    196             }
    197 
    198             // Use reflection to invoke the 'getPkeyContext' method on the
    199             // result of the getOpenSSLKey(). This is an 32-bit integer
    200             // which is the address of an EVP_PKEY object. Note that this
    201             // method these days returns a 64-bit long, but since this code
    202             // path is used for older Android versions, it may still return
    203             // a 32-bit int here. To be on the safe side, we cast the return
    204             // value via Number rather than directly to Integer or Long.
    205             Method getPkeyContext;
    206             try {
    207                 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
    208             } catch (Exception e) {
    209                 // Bail here too, something really not working as expected.
    210                 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
    211                 return 0;
    212             }
    213             getPkeyContext.setAccessible(true);
    214             long evp_pkey = 0;
    215             try {
    216                 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
    217             } finally {
    218                 getPkeyContext.setAccessible(false);
    219             }
    220             if (evp_pkey == 0) {
    221                 // The PrivateKey is probably rotten for some reason.
    222                 Log.e(TAG, "getPkeyContext() returned null");
    223             }
    224             return evp_pkey;
    225 
    226         } catch (Exception e) {
    227             Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
    228             return 0;
    229         }
    230     }
    231 
    232     @Override
    233     public void releaseKey(AndroidPrivateKey key) {
    234         // no-op for in-process. GC will handle key collection
    235     }
    236 }
    237