Merge changes Ibdf23227,I3681f98c

* changes:
  Update Wifi to use new keystore function
  Add signing to keystore
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f75208d..68ba2b1 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -26,11 +26,13 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.ObjectOutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.Writer;
 import java.nio.charset.Charsets;
 import java.security.KeyPair;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -73,6 +75,36 @@
     public static final String EXTENSION_PFX = ".pfx";
 
     /**
+     * Intent extra: name for the user's private key.
+     */
+    public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name";
+
+    /**
+     * Intent extra: data for the user's private key in PEM-encoded PKCS#8.
+     */
+    public static final String EXTRA_USER_PRIVATE_KEY_DATA = "user_private_key_data";
+
+    /**
+     * Intent extra: name for the user's certificate.
+     */
+    public static final String EXTRA_USER_CERTIFICATE_NAME = "user_certificate_name";
+
+    /**
+     * Intent extra: data for the user's certificate in PEM-encoded X.509.
+     */
+    public static final String EXTRA_USER_CERTIFICATE_DATA = "user_certificate_data";
+
+    /**
+     * Intent extra: name for CA certificate chain
+     */
+    public static final String EXTRA_CA_CERTIFICATES_NAME = "ca_certificates_name";
+
+    /**
+     * Intent extra: data for CA certificate chain in PEM-encoded X.509.
+     */
+    public static final String EXTRA_CA_CERTIFICATES_DATA = "ca_certificates_data";
+
+    /**
      * Convert objects to a PEM format, which is used for
      * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
      * entries.
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index f38f6ce..60fd7f7 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -23,7 +23,7 @@
  */
 interface IKeyChainService {
     // APIs used by KeyChain
-    byte[] getPrivateKey(String alias);
+    String requestPrivateKey(String alias);
     byte[] getCertificate(String alias);
 
     // APIs used by CertInstaller
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index fe03437..483ccb2 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -27,6 +27,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.io.IOException;
+import java.security.InvalidKeyException;
 import java.security.KeyPair;
 import java.security.Principal;
 import java.security.PrivateKey;
@@ -39,6 +40,8 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import libcore.util.Objects;
+
+import org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;
 import org.apache.harmony.xnet.provider.jsse.TrustedCertificateStore;
 
 /**
@@ -301,14 +304,21 @@
         }
         KeyChainConnection keyChainConnection = bind(context);
         try {
-            IKeyChainService keyChainService = keyChainConnection.getService();
-            byte[] privateKeyBytes = keyChainService.getPrivateKey(alias);
-            return toPrivateKey(privateKeyBytes);
+            final IKeyChainService keyChainService = keyChainConnection.getService();
+            final String keyId = keyChainService.requestPrivateKey(alias);
+            if (keyId == null) {
+                throw new KeyChainException("keystore had a problem");
+            }
+
+            final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+            return engine.getPrivateKeyById(keyId);
         } catch (RemoteException e) {
             throw new KeyChainException(e);
         } catch (RuntimeException e) {
             // only certain RuntimeExceptions can be propagated across the IKeyChainService call
             throw new KeyChainException(e);
+        } catch (InvalidKeyException e) {
+            throw new KeyChainException(e);
         } finally {
             keyChainConnection.close();
         }
@@ -356,18 +366,6 @@
         }
     }
 
-    private static PrivateKey toPrivateKey(byte[] bytes) {
-        if (bytes == null) {
-            throw new IllegalArgumentException("bytes == null");
-        }
-        try {
-            KeyPair keyPair = (KeyPair) Credentials.convertFromPem(bytes).get(0);
-            return keyPair.getPrivate();
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
-    }
-
     private static X509Certificate toCertificate(byte[] bytes) {
         if (bytes == null) {
             throw new IllegalArgumentException("bytes == null");
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 9058cae..a32e469 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -155,6 +155,78 @@
         return mError == KEY_NOT_FOUND;
     }
 
+    private boolean generate(byte[] key) {
+        execute('a', key);
+        return mError == NO_ERROR;
+    }
+
+    public boolean generate(String key) {
+        return generate(getBytes(key));
+    }
+
+    private boolean importKey(byte[] keyName, byte[] key) {
+        execute('m', keyName, key);
+        return mError == NO_ERROR;
+    }
+
+    public boolean importKey(String keyName, byte[] key) {
+        return importKey(getBytes(keyName), key);
+    }
+
+    private byte[] getPubkey(byte[] key) {
+        ArrayList<byte[]> values = execute('b', key);
+        return (values == null || values.isEmpty()) ? null : values.get(0);
+    }
+
+    public byte[] getPubkey(String key) {
+        return getPubkey(getBytes(key));
+    }
+
+    private boolean delKey(byte[] key) {
+        execute('k', key);
+        return mError == NO_ERROR;
+    }
+
+    public boolean delKey(String key) {
+        return delKey(getBytes(key));
+    }
+
+    private byte[] sign(byte[] keyName, byte[] data) {
+        final ArrayList<byte[]> values = execute('n', keyName, data);
+        return (values == null || values.isEmpty()) ? null : values.get(0);
+    }
+
+    public byte[] sign(String key, byte[] data) {
+        return sign(getBytes(key), data);
+    }
+
+    private boolean verify(byte[] keyName, byte[] data, byte[] signature) {
+        execute('v', keyName, data, signature);
+        return mError == NO_ERROR;
+    }
+
+    public boolean verify(String key, byte[] data, byte[] signature) {
+        return verify(getBytes(key), data, signature);
+    }
+
+    private boolean grant(byte[] key, byte[] uid) {
+        execute('x', key, uid);
+        return mError == NO_ERROR;
+    }
+
+    public boolean grant(String key, int uid) {
+        return grant(getBytes(key), Integer.toString(uid).getBytes());
+    }
+
+    private boolean ungrant(byte[] key, byte[] uid) {
+        execute('y', key, uid);
+        return mError == NO_ERROR;
+    }
+
+    public boolean ungrant(String key, int uid) {
+        return ungrant(getBytes(key), Integer.toString(uid).getBytes());
+    }
+
     public int getLastError() {
         return mError;
     }
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 15e253c..008d682 100755
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -44,19 +44,73 @@
     private static final String TEST_I18N_KEY = "\u4F60\u597D, \u4E16\u754C";
     private static final byte[] TEST_I18N_VALUE = TEST_I18N_KEY.getBytes(Charsets.UTF_8);
 
+    // Test vector data for signatures
+    private static final byte[] TEST_DATA = {
+            (byte) 0x00, (byte) 0xA0, (byte) 0xFF, (byte) 0x0A, (byte) 0x00, (byte) 0xFF,
+            (byte) 0xAA, (byte) 0x55, (byte) 0x05, (byte) 0x5A,
+    };
+
     private KeyStore mKeyStore = null;
 
     public KeyStoreTest() {
         super(Activity.class);
     }
 
+    private static final byte[] PRIVKEY_BYTES = hexToBytes(
+            "308204BE020100300D06092A864886F70D0101010500048204A8308204A4020100028201" +
+            "0100E0473E8AB8F2284FEB9E742FF9748FA118ED98633C92F52AEB7A2EBE0D3BE60329BE" +
+            "766AD10EB6A515D0D2CFD9BEA7930F0C306537899F7958CD3E85B01F8818524D312584A9" +
+            "4B251E3625B54141EDBFEE198808E1BB97FC7CB49B9EAAAF68E9C98D7D0EDC53BBC0FA00" +
+            "34356D6305FBBCC3C7001405386ABBC873CB0F3EF7425F3D33DF7B315AE036D2A0B66AFD" +
+            "47503B169BF36E3B5162515B715FDA83DEAF2C58AEB9ABFB3097C3CC9DD9DBE5EF296C17" +
+            "6139028E8A671E63056D45F40188D2C4133490845DE52C2534E9C6B2478C07BDAE928823" +
+            "B62D066C7770F9F63F3DBA247F530844747BE7AAA85D853B8BD244ACEC3DE3C89AB46453" +
+            "AB4D24C3AC6902030100010282010037784776A5F17698F5AC960DFB83A1B67564E648BD" +
+            "0597CF8AB8087186F2669C27A9ECBDD480F0197A80D07309E6C6A96F925331E57F8B4AC6" +
+            "F4D45EDA45A23269C09FC428C07A4E6EDF738A15DEC97FABD2F2BB47A14F20EA72FCFE4C" +
+            "36E01ADA77BD137CD8D4DA10BB162E94A4662971F175F985FA188F056CB97EE2816F43AB" +
+            "9D3747612486CDA8C16196C30818A995EC85D38467791267B3BF21F273710A6925862576" +
+            "841C5B6712C12D4BD20A2F3299ADB7C135DA5E9515ABDA76E7CAF2A3BE80551D073B78BF" +
+            "1162C48AD2B7F4743A0238EE4D252F7D5E7E6533CCAE64CCB39360075A2FD1E034EC3AE5" +
+            "CE9C408CCBF0E25E4114021687B3DD4754AE8102818100F541884BC3737B2922D4119EF4" +
+            "5E2DEE2CD4CBB75F45505A157AA5009F99C73A2DF0724AC46024306332EA898177634546" +
+            "5DC6DF1E0A6F140AFF3B7396E6A8994AC5DAA96873472FE37749D14EB3E075E629DBEB35" +
+            "83338A6F3649D0A2654A7A42FD9AB6BFA4AC4D481D390BB229B064BDC311CC1BE1B63189" +
+            "DA7C40CDECF2B102818100EA1A742DDB881CEDB7288C87E38D868DD7A409D15A43F445D5" +
+            "377A0B5731DDBFCA2DAF28A8E13CD5C0AFCEC3347D74A39E235A3CD9633F274DE2B94F92" +
+            "DF43833911D9E9F1CF58F27DE2E08FF45964C720D3EC2139DC7CAFC912953CDECB2F355A" +
+            "2E2C35A50FAD754CB3B23166424BA3B6E3112A2B898C38C5C15EDB238693390281805182" +
+            "8F1EC6FD996029901BAF1D7E337BA5F0AF27E984EAD895ACE62BD7DF4EE45A224089F2CC" +
+            "151AF3CD173FCE0474BCB04F386A2CDCC0E0036BA2419F54579262D47100BE931984A3EF" +
+            "A05BECF141574DC079B3A95C4A83E6C43F3214D6DF32D512DE198085E531E616B83FD7DD" +
+            "9D1F4E2607C3333D07C55D107D1D3893587102818100DB4FB50F50DE8EDB53FF34C80931" +
+            "88A0512867DA2CCA04897759E587C244010DAF8664D59E8083D16C164789301F67A9F078" +
+            "060D834A2ADBD367575B68A8A842C2B02A89B3F31FCCEC8A22FE395795C5C6C7422B4E5D" +
+            "74A1E9A8F30E7759B9FC2D639C1F15673E84E93A5EF1506F4315383C38D45CBD1B14048F" +
+            "4721DC82326102818100D8114593AF415FB612DBF1923710D54D07486205A76A3B431949" +
+            "68C0DFF1F11EF0F61A4A337D5FD3741BBC9640E447B8B6B6C47C3AC1204357D3B0C55BA9" +
+            "286BDA73F629296F5FA9146D8976357D3C751E75148696A40B74685C82CE30902D639D72" +
+            "4FF24D5E2E9407EE34EDED2E3B4DF65AA9BCFEB6DF28D07BA6903F165768");
+
+
+    private static byte[] hexToBytes(String s) {
+        int len = s.length();
+        byte[] data = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(
+                    s.charAt(i + 1), 16));
+        }
+        return data;
+    }
+
     @Override
     protected void setUp() throws Exception {
         mKeyStore = KeyStore.getInstance();
         if (mKeyStore.state() != KeyStore.State.UNINITIALIZED) {
             mKeyStore.reset();
         }
-        assertEquals(KeyStore.State.UNINITIALIZED, mKeyStore.state());
+        assertEquals("KeyStore should be in an uninitialized state",
+                KeyStore.State.UNINITIALIZED, mKeyStore.state());
         super.setUp();
     }
 
@@ -164,4 +218,183 @@
         mKeyStore.reset();
         assertTrue(mKeyStore.isEmpty());
     }
+
+    public void testGenerate_NotInitialized_Fail() throws Exception {
+        assertFalse("Should fail when keystore is not initialized",
+                mKeyStore.generate(TEST_KEYNAME));
+    }
+
+    public void testGenerate_Locked_Fail() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.lock();
+        assertFalse("Should fail when keystore is locked", mKeyStore.generate(TEST_KEYNAME));
+    }
+
+    public void testGenerate_Success() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+
+        assertTrue("Should be able to generate key when unlocked",
+                mKeyStore.generate(TEST_KEYNAME));
+    }
+
+    public void testImport_Success() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+
+        assertTrue("Should be able to import key when unlocked",
+                mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+    }
+
+    public void testImport_Failure_BadEncoding() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+
+        assertFalse("Invalid DER-encoded key should not be imported",
+                mKeyStore.importKey(TEST_KEYNAME, TEST_DATA));
+    }
+
+    public void testSign_Success() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME));
+        final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
+
+        assertNotNull("Signature should not be null", signature);
+    }
+
+    public void testVerify_Success() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME));
+        final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
+
+        assertNotNull("Signature should not be null", signature);
+
+        assertTrue("Signature should verify with same data",
+                mKeyStore.verify(TEST_KEYNAME, TEST_DATA, signature));
+    }
+
+    public void testSign_NotInitialized_Failure() throws Exception {
+        assertNull("Should not be able to sign without first initializing the keystore",
+                mKeyStore.sign(TEST_KEYNAME, TEST_DATA));
+    }
+
+    public void testSign_NotGenerated_Failure() throws Exception {
+        mKeyStore.password(TEST_PASSWD);
+
+        assertNull("Should not be able to sign without first generating keys",
+                mKeyStore.sign(TEST_KEYNAME, TEST_DATA));
+    }
+
+    public void testGrant_Generated_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testGrant_Imported_Success() throws Exception {
+        assertTrue("Password should work for keystore", mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to import key for testcase",
+                mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+
+        assertTrue("Should be able to grant key to other user", mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testGrant_NoKey_Failure() throws Exception {
+        assertTrue("Should be able to unlock keystore for test",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertFalse("Should not be able to grant without first initializing the keystore",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testGrant_NotInitialized_Failure() throws Exception {
+        assertFalse("Should not be able to grant without first initializing the keystore",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_Generated_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_Imported_Success() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to import key for testcase",
+                mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_NotInitialized_Failure() throws Exception {
+        assertFalse("Should fail to ungrant key when keystore not initialized",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_NoGrant_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME));
+
+        assertFalse("Should not be able to revoke not existent grant",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_DoubleUngrant_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+
+        assertFalse("Should fail to ungrant key to other user second time",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
+
+    public void testUngrant_DoubleGrantUngrant_Failure() throws Exception {
+        assertTrue("Password should work for keystore",
+                mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key for testcase",
+                mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue("Should be able to grant key to other user",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to grant key to other user a second time",
+                mKeyStore.grant(TEST_KEYNAME, 0));
+
+        assertTrue("Should be able to ungrant key to other user",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+
+        assertFalse("Should fail to ungrant key to other user second time",
+                mKeyStore.ungrant(TEST_KEYNAME, 0));
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 5dec269..a9dbd10 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -25,6 +25,7 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.ProxyProperties;
 import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration.EnterpriseField;
 import android.net.wifi.WifiConfiguration.IpAssignment;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.ProxySettings;
@@ -1140,7 +1141,7 @@
                 String varName = field.varName();
                 String value = field.value();
                 if (value != null) {
-                    if (field != config.eap) {
+                    if (field != config.eap && field != config.engine) {
                         value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
                     }
                     if (!mWifiNative.setNetworkVariable(
@@ -1449,10 +1450,68 @@
             value = mWifiNative.getNetworkVariable(netId,
                     field.varName());
             if (!TextUtils.isEmpty(value)) {
-                if (field != config.eap) value = removeDoubleQuotes(value);
+                if (field != config.eap && field != config.engine) {
+                    value = removeDoubleQuotes(value);
+                }
                 field.setValue(value);
             }
         }
+
+        migrateOldEapTlsIfNecessary(config, netId);
+    }
+
+    /**
+     * Migration code for old EAP-TLS configurations. This should only be used
+     * when restoring an old wpa_supplicant.conf or upgrading from a previous
+     * platform version.
+     *
+     * @param config the configuration to be migrated
+     * @param netId the wpa_supplicant's net ID
+     * @param value the old private_key value
+     */
+    private void migrateOldEapTlsIfNecessary(WifiConfiguration config, int netId) {
+        String value = mWifiNative.getNetworkVariable(netId,
+                WifiConfiguration.OLD_PRIVATE_KEY_NAME);
+        /*
+         * If the old configuration value is not present, then there is nothing
+         * to do.
+         */
+        if (TextUtils.isEmpty(value)) {
+            return;
+        } else {
+            // Also ignore it if it's empty quotes.
+            value = removeDoubleQuotes(value);
+            if (TextUtils.isEmpty(value)) {
+                return;
+            }
+        }
+
+        config.engine.setValue(WifiConfiguration.ENGINE_ENABLE);
+        config.engine_id.setValue(convertToQuotedString(WifiConfiguration.KEYSTORE_ENGINE_ID));
+
+        /*
+         * The old key started with the keystore:// URI prefix, but we don't
+         * need that anymore. Trim it off if it exists.
+         */
+        final String keyName;
+        if (value.startsWith(WifiConfiguration.KEYSTORE_URI)) {
+            keyName = new String(value.substring(WifiConfiguration.KEYSTORE_URI.length()));
+        } else {
+            keyName = value;
+        }
+        config.key_id.setValue(convertToQuotedString(keyName));
+
+        // Now tell the wpa_supplicant the new configuration values.
+        final EnterpriseField needsUpdate[] = { config.engine, config.engine_id, config.key_id };
+        for (EnterpriseField field : needsUpdate) {
+            mWifiNative.setNetworkVariable(netId, field.varName(), field.value());
+        }
+
+        // Remove old private_key string so we don't run this again.
+        mWifiNative.setNetworkVariable(netId, WifiConfiguration.OLD_PRIVATE_KEY_NAME,
+                convertToQuotedString(""));
+
+        saveConfig();
     }
 
     private String removeDoubleQuotes(String string) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 85a6f27..dfc1b18 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,6 +29,33 @@
  */
 public class WifiConfiguration implements Parcelable {
 
+    /**
+     * In old configurations, the "private_key" field was used. However, newer
+     * configurations use the key_id field with the engine_id set to "keystore".
+     * If this field is found in the configuration, the migration code is
+     * triggered.
+     * @hide
+     */
+    public static final String OLD_PRIVATE_KEY_NAME = "private_key";
+
+    /**
+     * String representing the keystore OpenSSL ENGINE's ID.
+     * @hide
+     */
+    public static final String KEYSTORE_ENGINE_ID = "keystore";
+
+    /**
+     * String representing the keystore URI used for wpa_supplicant.
+     * @hide
+     */
+    public static final String KEYSTORE_URI = "keystore://";
+
+    /**
+     * String to set the engine value to when it should be enabled.
+     * @hide
+     */
+    public static final String ENGINE_ENABLE = "1";
+
     /** {@hide} */
     public static final String ssidVarName = "ssid";
     /** {@hide} */
@@ -82,14 +109,18 @@
     /** {@hide} */
     public EnterpriseField client_cert = new EnterpriseField("client_cert");
     /** {@hide} */
-    public EnterpriseField private_key = new EnterpriseField("private_key");
+    public EnterpriseField engine = new EnterpriseField("engine");
+    /** {@hide} */
+    public EnterpriseField engine_id = new EnterpriseField("engine_id");
+    /** {@hide} */
+    public EnterpriseField key_id = new EnterpriseField("key_id");
     /** {@hide} */
     public EnterpriseField ca_cert = new EnterpriseField("ca_cert");
 
     /** {@hide} */
     public EnterpriseField[] enterpriseFields = {
             eap, phase2, identity, anonymous_identity, password, client_cert,
-            private_key, ca_cert };
+            engine, engine_id, key_id, ca_cert };
 
     /**
      * Recognized key management schemes.