summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/PKCS11AuthenticationManager.java13
-rw-r--r--chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java27
-rw-r--r--chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellPKCS11AuthenticationManager.java4
-rw-r--r--net/android/android_private_key.cc25
-rw-r--r--net/android/android_private_key.h28
-rw-r--r--net/android/java/src/org/chromium/net/AndroidKeyStore.java203
-rw-r--r--net/android/java/src/org/chromium/net/AndroidPrivateKey.java18
-rw-r--r--net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java232
-rw-r--r--net/android/java/src/org/chromium/net/IRemoteAndroidKeyStore.aidl33
-rw-r--r--net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreCallbacks.aidl23
-rw-r--r--net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreInterface.aidl6
-rw-r--r--net/android/java/src/org/chromium/net/RemoteAndroidKeyStore.java139
-rw-r--r--net/android/keystore.cc51
-rw-r--r--net/android/keystore.h2
-rw-r--r--net/android/keystore_openssl.cc10
-rw-r--r--net/android/net_jni_registrar.cc2
-rw-r--r--net/net.gyp18
17 files changed, 614 insertions, 220 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/PKCS11AuthenticationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/PKCS11AuthenticationManager.java
index e3fd436..8e57a70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/PKCS11AuthenticationManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/PKCS11AuthenticationManager.java
@@ -6,7 +6,8 @@ package org.chromium.chrome.browser;
import android.content.Context;
-import java.security.PrivateKey;
+import org.chromium.net.AndroidPrivateKey;
+
import java.security.cert.X509Certificate;
/**
@@ -34,13 +35,13 @@ public interface PKCS11AuthenticationManager {
public X509Certificate[] getCertificateChain(String alias);
/**
- * Returns the PrivateKey for the requested alias, or null if no there is no result.
- */
- public PrivateKey getPrivateKey(String alias);
-
- /**
* Performs necessary initializing for using a PKCS11-based KeysStore. Note that this can
* perform expensive operations and cannot be done on the UI thread.
*/
public void initialize(Context context);
+
+ /**
+ * Returns the AndroidPrivateKey for the requested alias, or null if there is no result.
+ */
+ public AndroidPrivateKey getPrivateKey(String alias);
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java
index 31c41fd..ee7d599 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java
@@ -16,9 +16,10 @@ import org.chromium.base.ActivityStatus;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.base.ThreadUtils;
+import org.chromium.net.AndroidPrivateKey;
+import org.chromium.net.DefaultAndroidKeyStore;
import java.security.Principal;
-import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
@@ -34,9 +35,12 @@ import javax.security.auth.x500.X500Principal;
* finally pass the results back to the UI thread, which will return to the native code.
*/
@JNINamespace("chrome::android")
-class SSLClientCertificateRequest {
+public class SSLClientCertificateRequest {
static final String TAG = "SSLClientCertificateRequest";
+ private static final DefaultAndroidKeyStore sLocalKeyStore =
+ new DefaultAndroidKeyStore();
+
/**
* Common implementation for anynchronous task of handling the certificate request. This
* AsyncTask uses the abstract methods to retrieve the authentication material from a
@@ -47,7 +51,7 @@ class SSLClientCertificateRequest {
// These fields will store the results computed in doInBackground so that they can be posted
// back in onPostExecute.
private byte[][] mEncodedChain;
- private PrivateKey mPrivateKey;
+ private AndroidPrivateKey mAndroidPrivateKey;
// Pointer to the native certificate request needed to return the results.
private final int mNativePtr;
@@ -58,7 +62,7 @@ class SSLClientCertificateRequest {
// These overriden methods will be used to access the key store.
abstract String getAlias();
- abstract PrivateKey getPrivateKey(String alias);
+ abstract AndroidPrivateKey getPrivateKey(String alias);
abstract X509Certificate[] getCertificateChain(String alias);
@Override
@@ -66,8 +70,9 @@ class SSLClientCertificateRequest {
String alias = getAlias();
if (alias == null) return null;
- PrivateKey key = getPrivateKey(alias);
+ AndroidPrivateKey key = getPrivateKey(alias);
X509Certificate[] chain = getCertificateChain(alias);
+
if (key == null || chain == null || chain.length == 0) {
Log.w(TAG, "Empty client certificate chain?");
return null;
@@ -85,14 +90,14 @@ class SSLClientCertificateRequest {
}
mEncodedChain = encodedChain;
- mPrivateKey = key;
+ mAndroidPrivateKey = key;
return null;
}
@Override
protected void onPostExecute(Void result) {
ThreadUtils.assertOnUiThread();
- nativeOnSystemRequestCompletion(mNativePtr, mEncodedChain, mPrivateKey);
+ nativeOnSystemRequestCompletion(mNativePtr, mEncodedChain, mAndroidPrivateKey);
}
}
@@ -114,9 +119,9 @@ class SSLClientCertificateRequest {
}
@Override
- PrivateKey getPrivateKey(String alias) {
+ AndroidPrivateKey getPrivateKey(String alias) {
try {
- return KeyChain.getPrivateKey(mContext, alias);
+ return sLocalKeyStore.createKey(KeyChain.getPrivateKey(mContext, alias));
} catch (KeyChainException e) {
Log.w(TAG, "KeyChainException when looking for '" + alias + "' certificate");
return null;
@@ -160,7 +165,7 @@ class SSLClientCertificateRequest {
}
@Override
- PrivateKey getPrivateKey(String alias) {
+ AndroidPrivateKey getPrivateKey(String alias) {
return mPKCS11AuthManager.getPrivateKey(alias);
}
@@ -290,5 +295,5 @@ class SSLClientCertificateRequest {
// Called to pass request results to native side.
private static native void nativeOnSystemRequestCompletion(
- int requestPtr, byte[][] certChain, PrivateKey privateKey);
+ int requestPtr, byte[][] certChain, AndroidPrivateKey androidKey);
}
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellPKCS11AuthenticationManager.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellPKCS11AuthenticationManager.java
index 691e650..49a954b 100644
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellPKCS11AuthenticationManager.java
+++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellPKCS11AuthenticationManager.java
@@ -7,8 +7,8 @@ package org.chromium.chrome.testshell;
import android.content.Context;
import org.chromium.chrome.browser.PKCS11AuthenticationManager;
+import org.chromium.net.AndroidPrivateKey;
-import java.security.PrivateKey;
import java.security.cert.X509Certificate;
/**
@@ -36,7 +36,7 @@ public class TestShellPKCS11AuthenticationManager implements PKCS11Authenticatio
}
@Override
- public PrivateKey getPrivateKey(String alias) {
+ public AndroidPrivateKey getPrivateKey(String alias) {
return null;
}
}
diff --git a/net/android/android_private_key.cc b/net/android/android_private_key.cc
new file mode 100644
index 0000000..8f56085
--- /dev/null
+++ b/net/android/android_private_key.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/android/android_private_key.h"
+
+#include <vector>
+#include "jni/AndroidPrivateKey_jni.h"
+
+namespace net {
+namespace android {
+
+base::android::ScopedJavaLocalRef<jobject> GetKeyStore(
+ jobject private_key_ref) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ return Java_AndroidPrivateKey_getKeyStore(
+ env, private_key_ref);
+}
+
+bool RegisterAndroidPrivateKey(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace net
diff --git a/net/android/android_private_key.h b/net/android/android_private_key.h
new file mode 100644
index 0000000..9782a2d
--- /dev/null
+++ b/net/android/android_private_key.h
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_ANDROID_ANDROID_PRIVATE_KEY_H
+#define NET_ANDROID_ANDROID_PRIVATE_KEY_H
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+
+namespace net {
+namespace android {
+
+// Returns the KeyStore associated with a given AndroidPrivateKey
+NET_EXPORT base::android::ScopedJavaLocalRef<jobject> GetKeyStore(
+ jobject private_key);
+
+// Register JNI methods
+NET_EXPORT bool RegisterAndroidPrivateKey(JNIEnv* env);
+
+} // namespace android
+} // namespace net
+
+#endif // NET_ANDROID_ANDROID_PRIVATE_KEY_H
diff --git a/net/android/java/src/org/chromium/net/AndroidKeyStore.java b/net/android/java/src/org/chromium/net/AndroidKeyStore.java
index 1396bf1..6db0d0a 100644
--- a/net/android/java/src/org/chromium/net/AndroidKeyStore.java
+++ b/net/android/java/src/org/chromium/net/AndroidKeyStore.java
@@ -1,35 +1,17 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net;
-import android.util.Log;
-
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
-import java.lang.reflect.Method;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.interfaces.DSAKey;
-import java.security.interfaces.DSAParams;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.RSAKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.spec.ECParameterSpec;
-
+/**
+ * Specifies all the dependencies from the native OpenSSL engine on an Android KeyStore.
+ */
@JNINamespace("net::android")
-public class AndroidKeyStore {
-
- private static final String TAG = "AndroidKeyStore";
-
- ////////////////////////////////////////////////////////////////////
- //
- // Message signing support.
+public interface AndroidKeyStore {
/**
* Returns the public modulus of a given RSA private key as a byte
@@ -43,14 +25,7 @@ public class AndroidKeyStore {
* big-endian representation of a BigInteger.
*/
@CalledByNative
- public static byte[] getRSAKeyModulus(PrivateKey key) {
- if (key instanceof RSAKey) {
- return ((RSAKey) key).getModulus().toByteArray();
- } else {
- Log.w(TAG, "Not a RSAKey instance!");
- return null;
- }
- }
+ byte[] getRSAKeyModulus(AndroidPrivateKey key);
/**
* Returns the 'Q' parameter of a given DSA private key as a byte
@@ -63,15 +38,7 @@ public class AndroidKeyStore {
* a big-endian representation of a BigInteger.
*/
@CalledByNative
- public static byte[] getDSAKeyParamQ(PrivateKey key) {
- if (key instanceof DSAKey) {
- DSAParams params = ((DSAKey) key).getParams();
- return params.getQ().toByteArray();
- } else {
- Log.w(TAG, "Not a DSAKey instance!");
- return null;
- }
- }
+ byte[] getDSAKeyParamQ(AndroidPrivateKey key);
/**
* Returns the 'order' parameter of a given ECDSA private key as a
@@ -81,15 +48,7 @@ public class AndroidKeyStore {
* This is a big-endian representation of a BigInteger.
*/
@CalledByNative
- public static byte[] getECKeyOrder(PrivateKey key) {
- if (key instanceof ECKey) {
- ECParameterSpec params = ((ECKey) key).getParams();
- return params.getOrder().toByteArray();
- } else {
- Log.w(TAG, "Not an ECKey instance!");
- return null;
- }
- }
+ byte[] getECKeyOrder(AndroidPrivateKey key);
/**
* Returns the encoded data corresponding to a given PrivateKey.
@@ -100,9 +59,7 @@ public class AndroidKeyStore {
* @return encoded key as PKCS#8 byte array, can be null.
*/
@CalledByNative
- public static byte[] getPrivateKeyEncodedBytes(PrivateKey key) {
- return key.getEncoded();
- }
+ byte[] getPrivateKeyEncodedBytes(AndroidPrivateKey key);
/**
* Sign a given message with a given PrivateKey object. This method
@@ -125,7 +82,7 @@ public class AndroidKeyStore {
* message must be a hash and the function shall compute a direct
* DSA/ECDSA signature for it.
*
- * @param privateKey The PrivateKey handle.
+ * @param key The PrivateKey handle.
* @param message The message to sign.
* @return signature as a byte buffer.
*
@@ -134,66 +91,21 @@ public class AndroidKeyStore {
* getOpenSSLHandleForPrivateKey() below for work-around.
*/
@CalledByNative
- public static byte[] rawSignDigestWithPrivateKey(PrivateKey privateKey,
- byte[] message) {
- // Get the Signature for this key.
- Signature signature = null;
- // Hint: Algorithm names come from:
- // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
- try {
- if (privateKey instanceof RSAPrivateKey) {
- // IMPORTANT: Due to a platform bug, this will throw NoSuchAlgorithmException
- // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
- // See https://android-review.googlesource.com/#/c/40352/
- signature = Signature.getInstance("NONEwithRSA");
- } else if (privateKey instanceof DSAPrivateKey) {
- signature = Signature.getInstance("NONEwithDSA");
- } else if (privateKey instanceof ECPrivateKey) {
- signature = Signature.getInstance("NONEwithECDSA");
- }
- } catch (NoSuchAlgorithmException e) {
- ;
- }
-
- if (signature == null) {
- Log.e(TAG, "Unsupported private key algorithm: " + privateKey.getAlgorithm());
- return null;
- }
-
- // Sign the message.
- try {
- signature.initSign(privateKey);
- signature.update(message);
- return signature.sign();
- } catch (Exception e) {
- Log.e(TAG, "Exception while signing message with " + privateKey.getAlgorithm() +
- " private key: " + e);
- return null;
- }
- }
+ byte[] rawSignDigestWithPrivateKey(AndroidPrivateKey key, byte[] message);
/**
* Return the type of a given PrivateKey object. This is an integer
* that maps to one of the values defined by org.chromium.net.PrivateKeyType,
* which is itself auto-generated from net/android/private_key_type_list.h
- * @param privateKey The PrivateKey handle
+ * @param key The PrivateKey handle
* @return key type, or PrivateKeyType.INVALID if unknown.
*/
@CalledByNative
- public static int getPrivateKeyType(PrivateKey privateKey) {
- if (privateKey instanceof RSAPrivateKey)
- return PrivateKeyType.RSA;
- if (privateKey instanceof DSAPrivateKey)
- return PrivateKeyType.DSA;
- if (privateKey instanceof ECPrivateKey)
- return PrivateKeyType.ECDSA;
- else
- return PrivateKeyType.INVALID;
- }
+ int getPrivateKeyType(AndroidPrivateKey key);
/**
* Return the system EVP_PKEY handle corresponding to a given PrivateKey
- * object, obtained through reflection.
+ * object.
*
* This shall only be used when the "NONEwithRSA" signature is not
* available, as described in rawSignDigestWithPrivateKey(). I.e.
@@ -219,88 +131,15 @@ public class AndroidKeyStore {
* libcore/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java
* libcore/luni/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
*
- * @param privateKey The PrivateKey handle.
+ * @param key The PrivateKey handle.
* @return The EVP_PKEY handle, as a 32-bit integer (0 if not available)
*/
@CalledByNative
- public static int getOpenSSLHandleForPrivateKey(PrivateKey privateKey) {
- // Sanity checks
- if (privateKey == null) {
- Log.e(TAG, "privateKey == null");
- return 0;
- }
- if (!(privateKey instanceof RSAPrivateKey)) {
- Log.e(TAG, "does not implement RSAPrivateKey");
- return 0;
- }
- // First, check that this is a proper instance of OpenSSLRSAPrivateKey
- // or one of its sub-classes.
- Class<?> superClass;
- try {
- superClass = Class.forName(
- "org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
- } catch (Exception e) {
- // This may happen if the target device has a completely different
- // implementation of the java.security APIs, compared to vanilla
- // Android. Highly unlikely, but still possible.
- Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
- return 0;
- }
- if (!superClass.isInstance(privateKey)) {
- // This may happen if the PrivateKey was not created by the "AndroidOpenSSL"
- // provider, which should be the default. That could happen if an OEM decided
- // to implement a different default provider. Also highly unlikely.
- Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" +
- privateKey.getClass().getCanonicalName());
- return 0;
- }
+ int getOpenSSLHandleForPrivateKey(AndroidPrivateKey key);
- try {
- // Use reflection to invoke the 'getOpenSSLKey()' method on
- // the private key. This returns another Java object that wraps
- // a native EVP_PKEY. Note that the method is final, so calling
- // the superclass implementation is ok.
- Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
- getKey.setAccessible(true);
- Object opensslKey = null;
- try {
- opensslKey = getKey.invoke(privateKey);
- } finally {
- getKey.setAccessible(false);
- }
- if (opensslKey == null) {
- // Bail when detecting OEM "enhancement".
- Log.e(TAG, "getOpenSSLKey() returned null");
- return 0;
- }
-
- // Use reflection to invoke the 'getPkeyContext' method on the
- // result of the getOpenSSLKey(). This is an 32-bit integer
- // which is the address of an EVP_PKEY object.
- Method getPkeyContext;
- try {
- getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
- } catch (Exception e) {
- // Bail here too, something really not working as expected.
- Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
- return 0;
- }
- getPkeyContext.setAccessible(true);
- int evp_pkey = 0;
- try {
- evp_pkey = (Integer) getPkeyContext.invoke(opensslKey);
- } finally {
- getPkeyContext.setAccessible(false);
- }
- if (evp_pkey == 0) {
- // The PrivateKey is probably rotten for some reason.
- Log.e(TAG, "getPkeyContext() returned null");
- }
- return evp_pkey;
-
- } catch (Exception e) {
- Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
- return 0;
- }
- }
+ /**
+ * Called when the native OpenSSL engine no longer needs access to the underlying key.
+ */
+ @CalledByNative
+ void releaseKey(AndroidPrivateKey key);
}
diff --git a/net/android/java/src/org/chromium/net/AndroidPrivateKey.java b/net/android/java/src/org/chromium/net/AndroidPrivateKey.java
new file mode 100644
index 0000000..a6df6d4
--- /dev/null
+++ b/net/android/java/src/org/chromium/net/AndroidPrivateKey.java
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * Abstract private key that bundles the PrivateKey and AndroidKeyStore that it belongs to.
+ */
+@JNINamespace("net::android")
+public interface AndroidPrivateKey {
+ /** @return AndroidKeyStore that handles this key. */
+ @CalledByNative
+ AndroidKeyStore getKeyStore();
+}
diff --git a/net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java b/net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java
new file mode 100644
index 0000000..5232e46
--- /dev/null
+++ b/net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java
@@ -0,0 +1,232 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+import android.util.Log;
+
+import java.lang.reflect.Method;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.interfaces.DSAKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.spec.ECParameterSpec;
+
+/**
+ * Simple implementation of the AndroidKeyStore for use with an in-process Java KeyStore.
+ */
+public class DefaultAndroidKeyStore implements AndroidKeyStore {
+
+ private static final String TAG = "AndroidKeyStoreInProcessImpl";
+
+ private static class DefaultAndroidPrivateKey implements AndroidPrivateKey {
+ // The actual Java key being wrapped.
+ final PrivateKey mKey;
+ // Key store handling this key.
+ final DefaultAndroidKeyStore mStore;
+
+ DefaultAndroidPrivateKey(PrivateKey key, DefaultAndroidKeyStore store) {
+ mKey = key;
+ mStore = store;
+ }
+
+ PrivateKey getJavaKey() {
+ return mKey;
+ }
+
+ @Override
+ public AndroidKeyStore getKeyStore() {
+ return mStore;
+ }
+ }
+
+ public AndroidPrivateKey createKey(PrivateKey javaKey) {
+ return new DefaultAndroidPrivateKey(javaKey, this);
+ }
+
+ @Override
+ public byte[] getRSAKeyModulus(AndroidPrivateKey key) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ if (javaKey instanceof RSAKey) {
+ return ((RSAKey) javaKey).getModulus().toByteArray();
+ }
+ Log.w(TAG, "Not a RSAKey instance!");
+ return null;
+ }
+
+ @Override
+ public byte[] getDSAKeyParamQ(AndroidPrivateKey key) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ if (javaKey instanceof DSAKey) {
+ DSAParams params = ((DSAKey) javaKey).getParams();
+ return params.getQ().toByteArray();
+ }
+ Log.w(TAG, "Not a DSAKey instance!");
+ return null;
+ }
+
+ @Override
+ public byte[] getECKeyOrder(AndroidPrivateKey key) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ if (javaKey instanceof ECKey) {
+ ECParameterSpec params = ((ECKey) javaKey).getParams();
+ return params.getOrder().toByteArray();
+ }
+ Log.w(TAG, "Not an ECKey instance!");
+ return null;
+ }
+
+ @Override
+ public byte[] getPrivateKeyEncodedBytes(AndroidPrivateKey key) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ return javaKey.getEncoded();
+ }
+
+ @Override
+ public byte[] rawSignDigestWithPrivateKey(AndroidPrivateKey key,
+ byte[] message) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ // Get the Signature for this key.
+ Signature signature = null;
+ // Hint: Algorithm names come from:
+ // http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html
+ try {
+ if (javaKey instanceof RSAPrivateKey) {
+ // IMPORTANT: Due to a platform bug, this will throw NoSuchAlgorithmException
+ // on Android 4.0.x and 4.1.x. Fixed in 4.2 and higher.
+ // See https://android-review.googlesource.com/#/c/40352/
+ signature = Signature.getInstance("NONEwithRSA");
+ } else if (javaKey instanceof DSAPrivateKey) {
+ signature = Signature.getInstance("NONEwithDSA");
+ } else if (javaKey instanceof ECPrivateKey) {
+ signature = Signature.getInstance("NONEwithECDSA");
+ }
+ } catch (NoSuchAlgorithmException e) {
+ ;
+ }
+
+ if (signature == null) {
+ Log.e(TAG, "Unsupported private key algorithm: " + javaKey.getAlgorithm());
+ return null;
+ }
+
+ // Sign the message.
+ try {
+ signature.initSign(javaKey);
+ signature.update(message);
+ return signature.sign();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while signing message with " + javaKey.getAlgorithm() +
+ " private key: " + e);
+ return null;
+ }
+ }
+
+ @Override
+ public int getPrivateKeyType(AndroidPrivateKey key) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ if (javaKey instanceof RSAPrivateKey)
+ return PrivateKeyType.RSA;
+ if (javaKey instanceof DSAPrivateKey)
+ return PrivateKeyType.DSA;
+ if (javaKey instanceof ECPrivateKey)
+ return PrivateKeyType.ECDSA;
+ else
+ return PrivateKeyType.INVALID;
+ }
+
+ @Override
+ public int getOpenSSLHandleForPrivateKey(AndroidPrivateKey key) {
+ PrivateKey javaKey = ((DefaultAndroidPrivateKey) key).getJavaKey();
+ // Sanity checks
+ if (javaKey == null) {
+ Log.e(TAG, "key == null");
+ return 0;
+ }
+ if (!(javaKey instanceof RSAPrivateKey)) {
+ Log.e(TAG, "does not implement RSAPrivateKey");
+ return 0;
+ }
+ // First, check that this is a proper instance of OpenSSLRSAPrivateKey
+ // or one of its sub-classes.
+ Class<?> superClass;
+ try {
+ superClass = Class.forName(
+ "org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey");
+ } catch (Exception e) {
+ // This may happen if the target device has a completely different
+ // implementation of the java.security APIs, compared to vanilla
+ // Android. Highly unlikely, but still possible.
+ Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e);
+ return 0;
+ }
+ if (!superClass.isInstance(key)) {
+ // This may happen if the PrivateKey was not created by the "AndroidOpenSSL"
+ // provider, which should be the default. That could happen if an OEM decided
+ // to implement a different default provider. Also highly unlikely.
+ Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" +
+ javaKey.getClass().getCanonicalName());
+ return 0;
+ }
+
+ try {
+ // Use reflection to invoke the 'getOpenSSLKey()' method on
+ // the private key. This returns another Java object that wraps
+ // a native EVP_PKEY. Note that the method is final, so calling
+ // the superclass implementation is ok.
+ Method getKey = superClass.getDeclaredMethod("getOpenSSLKey");
+ getKey.setAccessible(true);
+ Object opensslKey = null;
+ try {
+ opensslKey = getKey.invoke(javaKey);
+ } finally {
+ getKey.setAccessible(false);
+ }
+ if (opensslKey == null) {
+ // Bail when detecting OEM "enhancement".
+ Log.e(TAG, "getOpenSSLKey() returned null");
+ return 0;
+ }
+
+ // Use reflection to invoke the 'getPkeyContext' method on the
+ // result of the getOpenSSLKey(). This is an 32-bit integer
+ // which is the address of an EVP_PKEY object.
+ Method getPkeyContext;
+ try {
+ getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext");
+ } catch (Exception e) {
+ // Bail here too, something really not working as expected.
+ Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e);
+ return 0;
+ }
+ getPkeyContext.setAccessible(true);
+ int evp_pkey = 0;
+ try {
+ evp_pkey = (Integer) getPkeyContext.invoke(opensslKey);
+ } finally {
+ getPkeyContext.setAccessible(false);
+ }
+ if (evp_pkey == 0) {
+ // The PrivateKey is probably rotten for some reason.
+ Log.e(TAG, "getPkeyContext() returned null");
+ }
+ return evp_pkey;
+
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while trying to retrieve system EVP_PKEY handle: " + e);
+ return 0;
+ }
+ }
+
+ @Override
+ public void releaseKey(AndroidPrivateKey key) {
+ // no-op for in-process. GC will handle key collection
+ }
+}
diff --git a/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStore.aidl b/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStore.aidl
new file mode 100644
index 0000000..ee4c65d
--- /dev/null
+++ b/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStore.aidl
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+import org.chromium.net.IRemoteAndroidKeyStoreCallbacks;
+
+/**
+ * Interface for communication with an Android KeyStore in another process.
+ */
+interface IRemoteAndroidKeyStore {
+ // Remote calls for SSlClientCertificateRequest - these allow retrieving
+ // the alias of the certificate to be used, its encoded chain and a handle
+ // for identifying a private key in the remote process.
+ String getClientCertificateAlias();
+ byte[] getEncodedCertificateChain(in String alias);
+ int getPrivateKeyHandle(in String alias);
+
+ // Registers callbacks for service->client communication.
+ void setClientCallbacks(IRemoteAndroidKeyStoreCallbacks callbacks);
+
+ // Remote calls for AndroidKeyStore - these functions are performing operations
+ // with a PrivateKey in the remote process using the handle provided by
+ // |getPrivateKeyHandle|.
+ byte[] getRSAKeyModulus(in int handle);
+ byte[] getPrivateKeyEncodedBytes(in int handle);
+ byte[] getDSAKeyParamQ(in int handle);
+ byte[] getECKeyOrder(in int handle);
+ byte[] rawSignDigestWithPrivateKey(in int handle, in byte[] message);
+ int getPrivateKeyType(in int handle);
+ void releaseKey(in int handle);
+}
diff --git a/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreCallbacks.aidl b/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreCallbacks.aidl
new file mode 100644
index 0000000..2c2564b
--- /dev/null
+++ b/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreCallbacks.aidl
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+/**
+ * Interface for communication from the remote authentication service back to the client.
+ */
+interface IRemoteAndroidKeyStoreCallbacks {
+ /**
+ * A critical failure has occurred and the service won't be able to recover.
+ * The client should unbind and optionally rebind at a later time.
+ */
+ void onDisabled();
+
+ /**
+ * The service has started up and is fully initialized. This allows for the
+ * service to take some time to initialize. Remote calls shouldn't be invoked
+ * until this call has fired.
+ */
+ void onInitComplete();
+}
diff --git a/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreInterface.aidl b/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreInterface.aidl
new file mode 100644
index 0000000..3b54d29
--- /dev/null
+++ b/net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreInterface.aidl
@@ -0,0 +1,6 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+interface org.chomium.net.IRemoteAndroidKeyStore;
+interface org.chromium.net.IRemoteAndroidKeyStoreCallbacks; \ No newline at end of file
diff --git a/net/android/java/src/org/chromium/net/RemoteAndroidKeyStore.java b/net/android/java/src/org/chromium/net/RemoteAndroidKeyStore.java
new file mode 100644
index 0000000..e979036
--- /dev/null
+++ b/net/android/java/src/org/chromium/net/RemoteAndroidKeyStore.java
@@ -0,0 +1,139 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Provides a remoted implementation of AndroidKeyStore where all calls are forwarded via
+ * binder to an external process.
+ */
+public class RemoteAndroidKeyStore implements AndroidKeyStore {
+
+ private static final String TAG = "AndroidKeyStoreRemoteImpl";
+
+ private static class RemotePrivateKey implements AndroidPrivateKey {
+ // Reference to the key on a remote store.
+ final int mHandle;
+ // Key store handling this key.
+ final RemoteAndroidKeyStore mStore;
+
+ RemotePrivateKey(int handle, RemoteAndroidKeyStore store) {
+ mHandle = handle;
+ mStore = store;
+ }
+
+ public int getHandle() {
+ return mHandle;
+ }
+
+ @Override
+ public AndroidKeyStore getKeyStore() {
+ return mStore;
+ }
+ }
+
+ private final IRemoteAndroidKeyStore mRemoteManager;
+
+ public RemoteAndroidKeyStore(IRemoteAndroidKeyStore manager) {
+ mRemoteManager = manager;
+ }
+
+ @Override
+ public byte[] getRSAKeyModulus(AndroidPrivateKey key) {
+ RemotePrivateKey remoteKey = (RemotePrivateKey) key;
+ try {
+ Log.d(TAG, "getRSAKeyModulus");
+ return mRemoteManager.getRSAKeyModulus(remoteKey.getHandle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] getDSAKeyParamQ(AndroidPrivateKey key) {
+ RemotePrivateKey remoteKey = (RemotePrivateKey) key;
+ try {
+ Log.d(TAG, "getDSAKeyParamQ");
+ return mRemoteManager.getDSAKeyParamQ(remoteKey.getHandle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] getECKeyOrder(AndroidPrivateKey key) {
+ RemotePrivateKey remoteKey = (RemotePrivateKey) key;
+ try {
+ Log.d(TAG, "getECKeyOrder");
+ return mRemoteManager.getECKeyOrder(remoteKey.getHandle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public byte[] rawSignDigestWithPrivateKey(AndroidPrivateKey key, byte[] message) {
+ RemotePrivateKey remoteKey = (RemotePrivateKey) key;
+ try {
+ Log.d(TAG, "rawSignDigestWithPrivateKey");
+ return mRemoteManager.rawSignDigestWithPrivateKey(remoteKey.getHandle(), message);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public int getPrivateKeyType(AndroidPrivateKey key) {
+ RemotePrivateKey remoteKey = (RemotePrivateKey) key;
+ try {
+ Log.d(TAG, "getPrivateKeyType");
+ return mRemoteManager.getPrivateKeyType(remoteKey.getHandle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ @Override
+ public byte[] getPrivateKeyEncodedBytes(AndroidPrivateKey key) {
+ // This should not be called as it's only for older versions of Android.
+ assert false;
+ return null;
+ }
+
+ @Override
+ public int getOpenSSLHandleForPrivateKey(AndroidPrivateKey privateKey) {
+ // This should not be called as it's only for older versions of Android.
+ assert false;
+ return 0;
+ }
+
+ public AndroidPrivateKey createKey(String alias) {
+ try {
+ int handle = mRemoteManager.getPrivateKeyHandle(alias);
+ return new RemotePrivateKey(handle, this);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ public void releaseKey(AndroidPrivateKey key) {
+ RemotePrivateKey remoteKey = (RemotePrivateKey) key;
+ try {
+ Log.d(TAG, "releaseKey");
+ mRemoteManager.releaseKey(remoteKey.getHandle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/net/android/keystore.cc b/net/android/keystore.cc
index a3d8cc1..cefd4f4 100644
--- a/net/android/keystore.cc
+++ b/net/android/keystore.cc
@@ -9,8 +9,8 @@
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/logging.h"
-
#include "jni/AndroidKeyStore_jni.h"
+#include "net/android/android_private_key.h"
using base::android::AttachCurrentThread;
using base::android::HasException;
@@ -28,7 +28,9 @@ bool GetRSAKeyModulus(
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> modulus_ref =
- Java_AndroidKeyStore_getRSAKeyModulus(env, private_key_ref);
+ Java_AndroidKeyStore_getRSAKeyModulus(env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
if (modulus_ref.is_null())
return false;
@@ -41,7 +43,10 @@ bool GetDSAKeyParamQ(jobject private_key_ref,
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> q_ref =
- Java_AndroidKeyStore_getDSAKeyParamQ(env, private_key_ref);
+ Java_AndroidKeyStore_getDSAKeyParamQ(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
if (q_ref.is_null())
return false;
@@ -54,7 +59,11 @@ bool GetECKeyOrder(jobject private_key_ref,
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> order_ref =
- Java_AndroidKeyStore_getECKeyOrder(env, private_key_ref);
+ Java_AndroidKeyStore_getECKeyOrder(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
+
if (order_ref.is_null())
return false;
@@ -62,12 +71,15 @@ bool GetECKeyOrder(jobject private_key_ref,
return true;
}
-bool GetPrivateKeyEncodedBytes(jobject private_key,
+bool GetPrivateKeyEncodedBytes(jobject private_key_ref,
std::vector<uint8>* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> encoded_ref =
- Java_AndroidKeyStore_getPrivateKeyEncodedBytes(env, private_key);
+ Java_AndroidKeyStore_getPrivateKeyEncodedBytes(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
if (encoded_ref.is_null())
return false;
@@ -91,7 +103,10 @@ bool RawSignDigestWithPrivateKey(
// Invoke platform API
ScopedJavaLocalRef<jbyteArray> signature_ref =
Java_AndroidKeyStore_rawSignDigestWithPrivateKey(
- env, private_key_ref, digest_ref.obj());
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref,
+ digest_ref.obj());
if (HasException(env) || signature_ref.is_null())
return false;
@@ -100,14 +115,16 @@ bool RawSignDigestWithPrivateKey(
return true;
}
-PrivateKeyType GetPrivateKeyType(jobject private_key) {
+PrivateKeyType GetPrivateKeyType(jobject private_key_ref) {
JNIEnv* env = AttachCurrentThread();
int type = Java_AndroidKeyStore_getPrivateKeyType(
- env, private_key);
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
return static_cast<PrivateKeyType>(type);
}
-EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key) {
+EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key_ref) {
JNIEnv* env = AttachCurrentThread();
// Note: the pointer is passed as a jint here because that's how it
// is stored in the Java object. Java doesn't have a primitive type
@@ -117,11 +134,21 @@ EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key) {
// Given that this routine shall only be called on Android < 4.2,
// this won't be a problem in the far future (e.g. when Android gets
// ported to 64-bit environments, if ever).
- int pkey =
- Java_AndroidKeyStore_getOpenSSLHandleForPrivateKey(env, private_key);
+ int pkey = Java_AndroidKeyStore_getOpenSSLHandleForPrivateKey(
+ env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
return reinterpret_cast<EVP_PKEY*>(pkey);
}
+void ReleaseKey(jobject private_key_ref) {
+ JNIEnv* env = AttachCurrentThread();
+ Java_AndroidKeyStore_releaseKey(env,
+ GetKeyStore(private_key_ref).obj(),
+ private_key_ref);
+ env->DeleteGlobalRef(private_key_ref);
+}
+
bool RegisterKeyStore(JNIEnv* env) {
return RegisterNativesImpl(env);
}
diff --git a/net/android/keystore.h b/net/android/keystore.h
index f14fa87..ac3babe 100644
--- a/net/android/keystore.h
+++ b/net/android/keystore.h
@@ -108,6 +108,8 @@ NET_EXPORT PrivateKeyType GetPrivateKeyType(jobject private_key);
// the returned key's reference count.
EVP_PKEY* GetOpenSSLSystemHandleForPrivateKey(jobject private_key);
+NET_EXPORT void ReleaseKey(jobject private_key);
+
// Register JNI methods
NET_EXPORT bool RegisterKeyStore(JNIEnv* env);
diff --git a/net/android/keystore_openssl.cc b/net/android/keystore_openssl.cc
index 5ad8473..afdca30 100644
--- a/net/android/keystore_openssl.cc
+++ b/net/android/keystore_openssl.cc
@@ -208,8 +208,7 @@ int RsaMethodFinish(RSA* rsa) {
jobject key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
if (key != NULL) {
RSA_set_app_data(rsa, NULL);
- JNIEnv* env = base::android::AttachCurrentThread();
- env->DeleteGlobalRef(key);
+ ReleaseKey(key);
}
// Actual return value is ignored by OpenSSL. There are no docs
// explaining what this is supposed to be.
@@ -413,8 +412,7 @@ int DsaMethodFinish(DSA* dsa) {
jobject key = reinterpret_cast<jobject>(DSA_get_ex_data(dsa,0));
if (key != NULL) {
DSA_set_ex_data(dsa, 0, NULL);
- JNIEnv* env = base::android::AttachCurrentThread();
- env->DeleteGlobalRef(key);
+ ReleaseKey(key);
}
// Actual return value is ignored by OpenSSL. There are no docs
// explaining what this is supposed to be.
@@ -493,9 +491,7 @@ void ExDataFree(void* parent,
return;
CRYPTO_set_ex_data(ad, idx, NULL);
-
- JNIEnv* env = base::android::AttachCurrentThread();
- env->DeleteGlobalRef(private_key);
+ ReleaseKey(private_key);
}
int ExDataDup(CRYPTO_EX_DATA* to,
diff --git a/net/android/net_jni_registrar.cc b/net/android/net_jni_registrar.cc
index 94c849a..b3e9437 100644
--- a/net/android/net_jni_registrar.cc
+++ b/net/android/net_jni_registrar.cc
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
+#include "net/android/android_private_key.h"
#include "net/android/gurl_utils.h"
#include "net/android/keystore.h"
#include "net/android/network_change_notifier_android.h"
@@ -19,6 +20,7 @@ namespace android {
static base::android::RegistrationMethod kNetRegisteredMethods[] = {
{ "AndroidCertVerifyResult", net::android::RegisterCertVerifyResult },
+ { "AndroidPrivateKey", net::android::RegisterAndroidPrivateKey},
{ "AndroidKeyStore", net::android::RegisterKeyStore },
{ "AndroidNetworkLibrary", net::android::RegisterNetworkLibrary },
{ "GURLUtils", net::RegisterGURLUtils },
diff --git a/net/net.gyp b/net/net.gyp
index 348c134..d8a1d56 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -65,6 +65,8 @@
'android/cert_verify_status_android_list.h',
'android/gurl_utils.cc',
'android/gurl_utils.h',
+ 'android/android_private_key.cc',
+ 'android/android_private_key.h',
'android/keystore.cc',
'android/keystore.h',
'android/keystore_openssl.cc',
@@ -3035,6 +3037,7 @@
'android/java/src/org/chromium/net/AndroidCertVerifyResult.java',
'android/java/src/org/chromium/net/AndroidKeyStore.java',
'android/java/src/org/chromium/net/AndroidNetworkLibrary.java',
+ 'android/java/src/org/chromium/net/AndroidPrivateKey.java',
'android/java/src/org/chromium/net/GURLUtils.java',
'android/java/src/org/chromium/net/NetworkChangeNotifier.java',
'android/java/src/org/chromium/net/ProxyChangeListener.java',
@@ -3070,10 +3073,25 @@
'certificate_mime_types_java',
'net_errors_java',
'private_key_types_java',
+ 'remote_android_keystore_aidl',
],
'includes': [ '../build/java.gypi' ],
},
{
+ # Processes the interface files for communication with an Android KeyStore
+ # running in a separate process.
+ 'target_name': 'remote_android_keystore_aidl',
+ 'type': 'none',
+ 'variables': {
+ 'aidl_interface_file': '../net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreInterface.aidl',
+ },
+ 'sources': [
+ '../net/android/java/src/org/chromium/net/IRemoteAndroidKeyStore.aidl',
+ '../net/android/java/src/org/chromium/net/IRemoteAndroidKeyStoreCallbacks.aidl',
+ ],
+ 'includes': [ '../build/java_aidl.gypi' ],
+ },
+ {
'target_name': 'net_java_test_support',
'type': 'none',
'variables': {