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     private Object getOpenSSLKeyForPrivateKey(AndroidPrivateKey key) {
    146         PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
    147         // Sanity checks
    148         if (javaKey == null) {
    149             Log.e(TAG, "key == null");
    150             return null;
    151         }
    152         if (!(javaKey instanceof RSAPrivateKey)) {
    153             Log.e(TAG, "does not implement RSAPrivateKey");
    154             return null;
    155         }
    156         // First, check that this is a proper instance of OpenSSLRSAPrivateKey
    157         // or one of its sub-classes.
    158         Class<?> superClass;
    159         try {
    160             superClass = Class.forName(
    161                     "org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
    162         } catch (Exception e) {
    163             // This may happen if the target device has a completely different
    164             // implementation of the java.security APIs, compared to vanilla
    165             // Android. Highly unlikely, but still possible.
    166             Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
    167             return null;
    168         }
    169         if (!superClass.isInstance(javaKey)) {
    170             // This may happen if the PrivateKey was not created by the "AndroidOpenSSL"
    171             // provider, which should be the default. That could happen if an OEM decided
    172             // to implement a different default provider. Also highly unlikely.
    173             Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" +
    174                        javaKey.getClass().getCanonicalName());
    175             return null;
    176         }
    177 
    178         try {
    179             // Use reflection to invoke the 'getOpenSSLKey()' method on the
    180             // private key. This returns another Java object that wraps a native
    181             // EVP_PKEY and OpenSSLEngine. Note that the method is final in Android
    182             // 4.1, so calling the superclass implementation is ok.
    183             Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
    184             getKey.setAccessible(true);
    185             Object opensslKey = null;
    186             try {
    187                 opensslKey = getKey.invoke(javaKey);
    188             } finally {
    189                 getKey.setAccessible(false);
    190             }
    191             if (opensslKey == null) {
    192                 // Bail when detecting OEM "enhancement".
    193                 Log.e(TAG, "getOpenSSLKey() returned null");
    194                 return null;
    195             }
    196             return opensslKey;
    197         } catch (Exception e) {
    198             Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
    199             return null;
    200         }
    201     }
    202 
    203     @Override
    204     public long getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
    205         Object opensslKey = getOpenSSLKeyForPrivateKey(key);
    206         if (opensslKey == null)
    207             return 0;
    208 
    209         try {
    210             // Use reflection to invoke the 'getPkeyContext' method on the
    211             // result of the getOpenSSLKey(). This is an 32-bit integer
    212             // which is the address of an EVP_PKEY object. Note that this
    213             // method these days returns a 64-bit long, but since this code
    214             // path is used for older Android versions, it may still return
    215             // a 32-bit int here. To be on the safe side, we cast the return
    216             // value via Number rather than directly to Integer or Long.
    217             Method getPkeyContext;
    218             try {
    219                 getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
    220             } catch (Exception e) {
    221                 // Bail here too, something really not working as expected.
    222                 Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
    223                 return 0;
    224             }
    225             getPkeyContext.setAccessible(true);
    226             long evp_pkey = 0;
    227             try {
    228                 evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue();
    229             } finally {
    230                 getPkeyContext.setAccessible(false);
    231             }
    232             if (evp_pkey == 0) {
    233                 // The PrivateKey is probably rotten for some reason.
    234                 Log.e(TAG, "getPkeyContext() returned null");
    235             }
    236             return evp_pkey;
    237 
    238         } catch (Exception e) {
    239             Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
    240             return 0;
    241         }
    242     }
    243 
    244     @Override
    245     public Object getOpenSSLEngineForPrivateKey(AndroidPrivateKey key) {
    246         // Find the system OpenSSLEngine class.
    247         Class<?> engineClass;
    248         try {
    249             engineClass = Class.forName(
    250                     "org.apache.harmony.xnet.provider.jsse.OpenSSLEngine");
    251         } catch (Exception e) {
    252             // This may happen if the target device has a completely different
    253             // implementation of the java.security APIs, compared to vanilla
    254             // Android. Highly unlikely, but still possible.
    255             Log.e(TAG, "Cannot find system OpenSSLEngine class: " + e);
    256             return null;
    257         }
    258 
    259         Object opensslKey = getOpenSSLKeyForPrivateKey(key);
    260         if (opensslKey == null)
    261             return null;
    262 
    263         try {
    264             // Use reflection to invoke the 'getEngine' method on the
    265             // result of the getOpenSSLKey().
    266             Method getEngine;
    267             try {
    268                 getEngine = opensslKey.getClass().getDeclaredMethod("getEngine");
    269             } catch (Exception e) {
    270                 // Bail here too, something really not working as expected.
    271                 Log.e(TAG, "No getEngine() method on OpenSSLKey member:" + e);
    272                 return null;
    273             }
    274             getEngine.setAccessible(true);
    275             Object engine = null;
    276             try {
    277                 engine = getEngine.invoke(opensslKey);
    278             } finally {
    279                 getEngine.setAccessible(false);
    280             }
    281             if (engine == null) {
    282                 // The PrivateKey is probably rotten for some reason.
    283                 Log.e(TAG, "getEngine() returned null");
    284             }
    285             // Sanity-check the returned engine.
    286             if (!engineClass.isInstance(engine)) {
    287                 Log.e(TAG, "Engine is not an OpenSSLEngine instance, its class name is:" +
    288                         engine.getClass().getCanonicalName());
    289                 return null;
    290             }
    291             return engine;
    292 
    293         } catch (Exception e) {
    294             Log.e(TAG, "Exception while trying to retrieve OpenSSLEngine object: " + e);
    295             return null;
    296         }
    297     }
    298 
    299     @Override
    300     public void releaseKey(AndroidPrivateKey key) {
    301         // no-op for in-process. GC will handle key collection
    302     }
    303 }
    304