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