diff options
author | Vinit Deshapnde <vinitd@google.com> | 2013-11-19 18:47:37 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-11-19 18:47:37 +0000 |
commit | 86513aafee5f44c680fbd5d6dbed9ce6ae28025f (patch) | |
tree | e6a6b5d6260080e9f91064f72e665fe116d613d1 /wifi | |
parent | 15f7b19313af24dc4cf4a001794404c13fa2a1d1 (diff) | |
parent | ade8de3c4a021d6b0e753d77be148236af5f39ef (diff) | |
download | frameworks_base-86513aafee5f44c680fbd5d6dbed9ce6ae28025f.zip frameworks_base-86513aafee5f44c680fbd5d6dbed9ce6ae28025f.tar.gz frameworks_base-86513aafee5f44c680fbd5d6dbed9ce6ae28025f.tar.bz2 |
Merge "Refactor WifiEnterpriseConfig and move service part in WifiConfigStore"
Diffstat (limited to 'wifi')
-rw-r--r-- | wifi/java/android/net/wifi/WifiConfigStore.java | 320 | ||||
-rw-r--r-- | wifi/java/android/net/wifi/WifiEnterpriseConfig.java | 394 |
2 files changed, 376 insertions, 338 deletions
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java index a6ae215..ee38464 100644 --- a/wifi/java/android/net/wifi/WifiConfigStore.java +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -30,12 +30,15 @@ import android.net.wifi.WifiConfiguration.ProxySettings; import android.net.wifi.WifiConfiguration.Status; import android.net.wifi.NetworkUpdateResult; import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; + import android.os.Environment; import android.os.FileObserver; -import android.os.Message; import android.os.Handler; import android.os.HandlerThread; +import android.os.Process; import android.os.UserHandle; +import android.security.Credentials; +import android.security.KeyChain; import android.security.KeyStore; import android.text.TextUtils; import android.util.LocalLog; @@ -57,7 +60,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.UnknownHostException; +import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; @@ -155,6 +161,61 @@ class WifiConfigStore { private static final String EXCLUSION_LIST_KEY = "exclusionList"; private static final String EOS = "eos"; + + /* Enterprise configuration keys */ + /** + * 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. + */ + public static final String OLD_PRIVATE_KEY_NAME = "private_key"; + + /** + * String representing the keystore OpenSSL ENGINE's ID. + */ + public static final String ENGINE_ID_KEYSTORE = "keystore"; + + /** + * String representing the keystore URI used for wpa_supplicant. + */ + public static final String KEYSTORE_URI = "keystore://"; + + /** + * String to set the engine value to when it should be enabled. + */ + public static final String ENGINE_ENABLE = "1"; + + /** + * String to set the engine value to when it should be disabled. + */ + public static final String ENGINE_DISABLE = "0"; + + public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; + public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE; + public static final String EAP_KEY = "eap"; + public static final String PHASE2_KEY = "phase2"; + public static final String IDENTITY_KEY = "identity"; + public static final String ANON_IDENTITY_KEY = "anonymous_identity"; + public static final String PASSWORD_KEY = "password"; + public static final String CLIENT_CERT_KEY = "client_cert"; + public static final String CA_CERT_KEY = "ca_cert"; + public static final String SUBJECT_MATCH_KEY = "subject_match"; + public static final String ENGINE_KEY = "engine"; + public static final String ENGINE_ID_KEY = "engine_id"; + public static final String PRIVATE_KEY_ID_KEY = "key_id"; + public static final String OPP_KEY_CACHING = "proactive_key_caching"; + + /** This represents an empty value of an enterprise field. + * NULL is used at wpa_supplicant to indicate an empty value + */ + static final String EMPTY_VALUE = "NULL"; + + /** Internal use only */ + private static final String[] ENTERPRISE_CONFIG_SUPPLICANT_KEYS = new String[] { EAP_KEY, + PHASE2_KEY, IDENTITY_KEY, ANON_IDENTITY_KEY, PASSWORD_KEY, CLIENT_CERT_KEY, + CA_CERT_KEY, SUBJECT_MATCH_KEY, ENGINE_KEY, ENGINE_ID_KEY, PRIVATE_KEY_ID_KEY }; + private final LocalLog mLocalLog; private final WpaConfigFileObserver mFileObserver; @@ -390,7 +451,7 @@ class WifiConfigStore { if (config != null) { // Remove any associated keys if (config.enterpriseConfig != null) { - config.enterpriseConfig.removeKeys(mKeyStore); + removeKeys(config.enterpriseConfig); } mConfiguredNetworks.remove(netId); mNetworkIds.remove(configKey(config)); @@ -754,7 +815,7 @@ class WifiConfigStore { if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { - if (config.enterpriseConfig.needsSoftwareBackedKeyStore()) { + if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) { return true; } } @@ -1239,7 +1300,7 @@ class WifiConfigStore { WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; - if (enterpriseConfig.needsKeyStore()) { + if (needsKeyStore(enterpriseConfig)) { /** * Keyguard settings may eventually be controlled by device policy. * We check here if keystore is unlocked before installing @@ -1259,7 +1320,7 @@ class WifiConfigStore { WifiConfiguration currentConfig = mConfiguredNetworks.get(netId); String keyId = config.getKeyIdForCredentials(currentConfig); - if (!enterpriseConfig.installKeys(mKeyStore, keyId)) { + if (!installKeys(enterpriseConfig, keyId)) { loge(config.SSID + ": failed to install keys"); break setVariables; } @@ -1276,7 +1337,7 @@ class WifiConfigStore { netId, key, value)) { - enterpriseConfig.removeKeys(mKeyStore); + removeKeys(enterpriseConfig); loge(config.SSID + ": failed to set " + key + ": " + value); break setVariables; @@ -1589,21 +1650,21 @@ class WifiConfigStore { config.enterpriseConfig = new WifiEnterpriseConfig(); } HashMap<String, String> enterpriseFields = config.enterpriseConfig.getFields(); - for (String key : WifiEnterpriseConfig.getSupplicantKeys()) { + for (String key : ENTERPRISE_CONFIG_SUPPLICANT_KEYS) { value = mWifiNative.getNetworkVariable(netId, key); if (!TextUtils.isEmpty(value)) { enterpriseFields.put(key, removeDoubleQuotes(value)); } else { - enterpriseFields.put(key, WifiEnterpriseConfig.EMPTY_VALUE); + enterpriseFields.put(key, EMPTY_VALUE); } } - if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) { + if (migrateOldEapTlsNative(config.enterpriseConfig, netId)) { saveConfig(); } - config.enterpriseConfig.migrateCerts(mKeyStore); - config.enterpriseConfig.initializeSoftwareKeystoreFlag(mKeyStore); + migrateCerts(config.enterpriseConfig); + // initializeSoftwareKeystoreFlag(config.enterpriseConfig, mKeyStore); } private String removeDoubleQuotes(String string) { @@ -1725,4 +1786,241 @@ class WifiConfigStore { } } + // Certificate and privake key management for EnterpriseConfig + boolean needsKeyStore(WifiEnterpriseConfig config) { + // Has no keys to be installed + if (config.getClientCertificate() == null && config.getCaCertificate() == null) + return false; + return true; + } + + static boolean isHardwareBackedKey(PrivateKey key) { + return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); + } + + static boolean hasHardwareBackedKey(Certificate certificate) { + return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); + } + + boolean needsSoftwareBackedKeyStore(WifiEnterpriseConfig config) { + String client = config.getClientCertificateAlias(); + if (!TextUtils.isEmpty(client)) { + // a valid client certificate is configured + + // BUGBUG: keyStore.get() never returns certBytes; because it is not + // taking WIFI_UID as a parameter. It always looks for certificate + // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that + // all certificates need software keystore until we get the get() API + // fixed. + + return true; + } + + /* + try { + + if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials + .USER_CERTIFICATE + client); + + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + if (factory == null) { + Slog.e(TAG, "Error getting certificate factory"); + return; + } + + byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); + if (certBytes != null) { + Certificate cert = (X509Certificate) factory.generateCertificate( + new ByteArrayInputStream(certBytes)); + + if (cert != null) { + mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); + + if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials + .USER_CERTIFICATE + client); + if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : + "does not need" ) + " software key store"); + } else { + Slog.d(TAG, "could not generate certificate"); + } + } else { + Slog.e(TAG, "Could not load client certificate " + Credentials + .USER_CERTIFICATE + client); + mNeedsSoftwareKeystore = true; + } + + } catch(CertificateException e) { + Slog.e(TAG, "Could not read certificates"); + mCaCert = null; + mClientCertificate = null; + } + */ + + return false; + } + + boolean installKeys(WifiEnterpriseConfig config, String name) { + boolean ret = true; + String privKeyName = Credentials.USER_PRIVATE_KEY + name; + String userCertName = Credentials.USER_CERTIFICATE + name; + String caCertName = Credentials.CA_CERTIFICATE + name; + if (config.getClientCertificate() != null) { + byte[] privKeyData = config.getClientPrivateKey().getEncoded(); + if (isHardwareBackedKey(config.getClientPrivateKey())) { + // Hardware backed key store is secure enough to store keys un-encrypted, this + // removes the need for user to punch a PIN to get access to these keys + if (DBG) Log.d(TAG, "importing keys " + name + " in hardware backed store"); + ret = mKeyStore.importKey(privKeyName, privKeyData, android.os.Process.WIFI_UID, + KeyStore.FLAG_NONE); + } else { + // Software backed key store is NOT secure enough to store keys un-encrypted. + // Save keys encrypted so they are protected with user's PIN. User will + // have to unlock phone before being able to use these keys and connect to + // networks. + if (DBG) Log.d(TAG, "importing keys " + name + " in software backed store"); + ret = mKeyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, + KeyStore.FLAG_ENCRYPTED); + } + if (ret == false) { + return ret; + } + + ret = putCertInKeyStore(userCertName, config.getClientCertificate()); + if (ret == false) { + // Remove private key installed + mKeyStore.delKey(privKeyName, Process.WIFI_UID); + return ret; + } + } + + if (config.getCaCertificate() != null) { + ret = putCertInKeyStore(caCertName, config.getCaCertificate()); + if (ret == false) { + if (config.getClientCertificate() != null) { + // Remove client key+cert + mKeyStore.delKey(privKeyName, Process.WIFI_UID); + mKeyStore.delete(userCertName, Process.WIFI_UID); + } + return ret; + } + } + + // Set alias names + if (config.getClientCertificate() != null) { + config.setClientCertificateAlias(name); + config.resetClientKeyEntry(); + } + + if (config.getCaCertificate() != null) { + config.setCaCertificateAlias(name); + config.resetCaCertificate(); + } + + return ret; + } + + private boolean putCertInKeyStore(String name, Certificate cert) { + try { + byte[] certData = Credentials.convertToPem(cert); + if (DBG) Log.d(TAG, "putting certificate " + name + " in keystore"); + return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); + + } catch (IOException e1) { + return false; + } catch (CertificateException e2) { + return false; + } + } + + void removeKeys(WifiEnterpriseConfig config) { + String client = config.getClientCertificateAlias(); + // a valid client certificate is configured + if (!TextUtils.isEmpty(client)) { + if (DBG) Log.d(TAG, "removing client private key and user cert"); + mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); + mKeyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); + } + + String ca = config.getCaCertificateAlias(); + // a valid ca certificate is configured + if (!TextUtils.isEmpty(ca)) { + if (DBG) Log.d(TAG, "removing CA cert"); + mKeyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); + } + } + + + /** Migrates the old style TLS config to the new config style. This should only be used + * when restoring an old wpa_supplicant.conf or upgrading from a previous + * platform version. + * @return true if the config was updated + * @hide + */ + boolean migrateOldEapTlsNative(WifiEnterpriseConfig config, int netId) { + String oldPrivateKey = mWifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); + /* + * If the old configuration value is not present, then there is nothing + * to do. + */ + if (TextUtils.isEmpty(oldPrivateKey)) { + return false; + } else { + // Also ignore it if it's empty quotes. + oldPrivateKey = removeDoubleQuotes(oldPrivateKey); + if (TextUtils.isEmpty(oldPrivateKey)) { + return false; + } + } + + config.setFieldValue(ENGINE_KEY, ENGINE_ENABLE); + config.setFieldValue(ENGINE_ID_KEY, ENGINE_ID_KEYSTORE); + + /* + * 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 (oldPrivateKey.startsWith(KEYSTORE_URI)) { + keyName = new String(oldPrivateKey.substring(KEYSTORE_URI.length())); + } else { + keyName = oldPrivateKey; + } + config.setFieldValue(PRIVATE_KEY_ID_KEY, keyName); + + mWifiNative.setNetworkVariable(netId, ENGINE_KEY, config.getFieldValue(ENGINE_KEY, "")); + + mWifiNative.setNetworkVariable(netId, ENGINE_ID_KEY, + config.getFieldValue(ENGINE_ID_KEY, "")); + + mWifiNative.setNetworkVariable(netId, PRIVATE_KEY_ID_KEY, + config.getFieldValue(PRIVATE_KEY_ID_KEY, "")); + + // Remove old private_key string so we don't run this again. + mWifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); + + return true; + } + + /** Migrate certs from global pool to wifi UID if not already done */ + void migrateCerts(WifiEnterpriseConfig config) { + String client = config.getClientCertificateAlias(); + // a valid client certificate is configured + if (!TextUtils.isEmpty(client)) { + if (!mKeyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { + mKeyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, + Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); + mKeyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, + Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); + } + } + + String ca = config.getCaCertificateAlias(); + // a valid ca certificate is configured + if (!TextUtils.isEmpty(ca)) { + if (!mKeyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { + mKeyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, + Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); + } + } + } } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index c7ebecb..452d84b 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -17,19 +17,13 @@ package android.net.wifi; import android.os.Parcel; import android.os.Parcelable; -import android.os.Process; import android.security.Credentials; -import android.security.KeyChain; -import android.security.KeyStore; import android.text.TextUtils; -import android.util.Slog; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -44,62 +38,11 @@ import java.util.Map; * and any associated credentials. */ public class WifiEnterpriseConfig implements Parcelable { - private static final String TAG = "WifiEnterpriseConfig"; - private static final boolean DBG = false; - /** - * 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. - */ - private static final String OLD_PRIVATE_KEY_NAME = "private_key"; - - /** - * String representing the keystore OpenSSL ENGINE's ID. - */ - private static final String ENGINE_ID_KEYSTORE = "keystore"; - - /** - * String representing the keystore URI used for wpa_supplicant. - */ - private static final String KEYSTORE_URI = "keystore://"; - - /** - * String to set the engine value to when it should be enabled. - */ - private static final String ENGINE_ENABLE = "1"; - - /** - * String to set the engine value to when it should be disabled. - */ - private static final String ENGINE_DISABLE = "0"; - - private static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; - private static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE; - - private static final String EAP_KEY = "eap"; - private static final String PHASE2_KEY = "phase2"; - private static final String IDENTITY_KEY = "identity"; - private static final String ANON_IDENTITY_KEY = "anonymous_identity"; - private static final String PASSWORD_KEY = "password"; - private static final String CLIENT_CERT_KEY = "client_cert"; - private static final String CA_CERT_KEY = "ca_cert"; - private static final String SUBJECT_MATCH_KEY = "subject_match"; - private static final String ENGINE_KEY = "engine"; - private static final String ENGINE_ID_KEY = "engine_id"; - private static final String PRIVATE_KEY_ID_KEY = "key_id"; - private static final String OPP_KEY_CACHING = "proactive_key_caching"; private HashMap<String, String> mFields = new HashMap<String, String>(); private X509Certificate mCaCert; private PrivateKey mClientPrivateKey; private X509Certificate mClientCertificate; - private boolean mNeedsSoftwareKeystore = false; - - /** This represents an empty value of an enterprise field. - * NULL is used at wpa_supplicant to indicate an empty value - */ - static final String EMPTY_VALUE = "NULL"; public WifiEnterpriseConfig() { // Do not set defaults so that the enterprise fields that are not changed @@ -246,7 +189,8 @@ public class WifiEnterpriseConfig implements Parcelable { public static final int GTC = 4; private static final String PREFIX = "auth="; /** @hide */ - public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", "MSCHAPV2", "GTC" }; + public static final String[] strings = {WifiConfigStore.EMPTY_VALUE, "PAP", "MSCHAP", + "MSCHAPV2", "GTC" }; /** Prevent initialization */ private Phase2() {} @@ -257,13 +201,6 @@ public class WifiEnterpriseConfig implements Parcelable { return mFields; } - /** Internal use only */ - static String[] getSupplicantKeys() { - return new String[] { EAP_KEY, PHASE2_KEY, IDENTITY_KEY, ANON_IDENTITY_KEY, PASSWORD_KEY, - CLIENT_CERT_KEY, CA_CERT_KEY, SUBJECT_MATCH_KEY, ENGINE_KEY, ENGINE_ID_KEY, - PRIVATE_KEY_ID_KEY }; - } - /** * Set the EAP authentication method. * @param eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or @@ -277,8 +214,8 @@ public class WifiEnterpriseConfig implements Parcelable { case Eap.PWD: case Eap.TLS: case Eap.TTLS: - mFields.put(EAP_KEY, Eap.strings[eapMethod]); - mFields.put(OPP_KEY_CACHING, "1"); + mFields.put(WifiConfigStore.EAP_KEY, Eap.strings[eapMethod]); + mFields.put(WifiConfigStore.OPP_KEY_CACHING, "1"); break; default: throw new IllegalArgumentException("Unknown EAP method"); @@ -290,7 +227,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return eap method configured */ public int getEapMethod() { - String eapMethod = mFields.get(EAP_KEY); + String eapMethod = mFields.get(WifiConfigStore.EAP_KEY); return getStringIndex(Eap.strings, eapMethod, Eap.NONE); } @@ -306,14 +243,14 @@ public class WifiEnterpriseConfig implements Parcelable { public void setPhase2Method(int phase2Method) { switch (phase2Method) { case Phase2.NONE: - mFields.put(PHASE2_KEY, EMPTY_VALUE); + mFields.put(WifiConfigStore.PHASE2_KEY, WifiConfigStore.EMPTY_VALUE); break; /** Valid methods */ case Phase2.PAP: case Phase2.MSCHAP: case Phase2.MSCHAPV2: case Phase2.GTC: - mFields.put(PHASE2_KEY, convertToQuotedString( + mFields.put(WifiConfigStore.PHASE2_KEY, convertToQuotedString( Phase2.PREFIX + Phase2.strings[phase2Method])); break; default: @@ -326,7 +263,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return a phase 2 method defined at {@link Phase2} * */ public int getPhase2Method() { - String phase2Method = removeDoubleQuotes(mFields.get(PHASE2_KEY)); + String phase2Method = removeDoubleQuotes(mFields.get(WifiConfigStore.PHASE2_KEY)); // Remove auth= prefix if (phase2Method.startsWith(Phase2.PREFIX)) { phase2Method = phase2Method.substring(Phase2.PREFIX.length()); @@ -339,7 +276,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param identity */ public void setIdentity(String identity) { - setFieldValue(IDENTITY_KEY, identity, ""); + setFieldValue(WifiConfigStore.IDENTITY_KEY, identity, ""); } /** @@ -347,7 +284,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the identity */ public String getIdentity() { - return getFieldValue(IDENTITY_KEY, ""); + return getFieldValue(WifiConfigStore.IDENTITY_KEY, ""); } /** @@ -356,14 +293,14 @@ public class WifiEnterpriseConfig implements Parcelable { * @param anonymousIdentity the anonymous identity */ public void setAnonymousIdentity(String anonymousIdentity) { - setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, ""); + setFieldValue(WifiConfigStore.ANON_IDENTITY_KEY, anonymousIdentity, ""); } /** Get the anonymous identity * @return anonymous identity */ public String getAnonymousIdentity() { - return getFieldValue(ANON_IDENTITY_KEY, ""); + return getFieldValue(WifiConfigStore.ANON_IDENTITY_KEY, ""); } /** @@ -371,7 +308,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param password the password */ public void setPassword(String password) { - setFieldValue(PASSWORD_KEY, password, ""); + setFieldValue(WifiConfigStore.PASSWORD_KEY, password, ""); } /** @@ -381,7 +318,7 @@ public class WifiEnterpriseConfig implements Parcelable { * framework, returns "*". */ public String getPassword() { - return getFieldValue(PASSWORD_KEY, ""); + return getFieldValue(WifiConfigStore.PASSWORD_KEY, ""); } /** @@ -394,7 +331,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @hide */ public void setCaCertificateAlias(String alias) { - setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); + setFieldValue(WifiConfigStore.CA_CERT_KEY, alias, WifiConfigStore.CA_CERT_PREFIX); } /** @@ -403,7 +340,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @hide */ public String getCaCertificateAlias() { - return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); + return getFieldValue(WifiConfigStore.CA_CERT_KEY, WifiConfigStore.CA_CERT_PREFIX); } /** @@ -431,7 +368,6 @@ public class WifiEnterpriseConfig implements Parcelable { /** * Get CA certificate - * * @return X.509 CA certificate */ public X509Certificate getCaCertificate() { @@ -439,6 +375,13 @@ public class WifiEnterpriseConfig implements Parcelable { } /** + * @hide + */ + public void resetCaCertificate() { + mCaCert = null; + } + + /** * Set Client certificate alias. * * <p> See the {@link android.security.KeyChain} for details on installing or choosing @@ -448,15 +391,16 @@ public class WifiEnterpriseConfig implements Parcelable { * @hide */ public void setClientCertificateAlias(String alias) { - setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); - setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); + setFieldValue(WifiConfigStore.CLIENT_CERT_KEY, alias, WifiConfigStore.CLIENT_CERT_PREFIX); + setFieldValue(WifiConfigStore.PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); // Also, set engine parameters if (TextUtils.isEmpty(alias)) { - mFields.put(ENGINE_KEY, ENGINE_DISABLE); - mFields.put(ENGINE_ID_KEY, EMPTY_VALUE); + mFields.put(WifiConfigStore.ENGINE_KEY, WifiConfigStore.ENGINE_DISABLE); + mFields.put(WifiConfigStore.ENGINE_ID_KEY, WifiConfigStore.EMPTY_VALUE); } else { - mFields.put(ENGINE_KEY, ENGINE_ENABLE); - mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); + mFields.put(WifiConfigStore.ENGINE_KEY, WifiConfigStore.ENGINE_ENABLE); + mFields.put(WifiConfigStore.ENGINE_ID_KEY, + convertToQuotedString(WifiConfigStore.ENGINE_ID_KEYSTORE)); } } @@ -466,7 +410,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @hide */ public String getClientCertificateAlias() { - return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); + return getFieldValue(WifiConfigStore.CLIENT_CERT_KEY, WifiConfigStore.CLIENT_CERT_PREFIX); } /** @@ -507,116 +451,19 @@ public class WifiEnterpriseConfig implements Parcelable { return mClientCertificate; } - boolean needsKeyStore() { - // Has no keys to be installed - if (mClientCertificate == null && mCaCert == null) return false; - return true; - } - - static boolean isHardwareBackedKey(PrivateKey key) { - return KeyChain.isBoundKeyAlgorithm(key.getAlgorithm()); - } - - static boolean hasHardwareBackedKey(Certificate certificate) { - return KeyChain.isBoundKeyAlgorithm(certificate.getPublicKey().getAlgorithm()); - } - - boolean needsSoftwareBackedKeyStore() { - return mNeedsSoftwareKeystore; - } - - boolean installKeys(android.security.KeyStore keyStore, String name) { - boolean ret = true; - String privKeyName = Credentials.USER_PRIVATE_KEY + name; - String userCertName = Credentials.USER_CERTIFICATE + name; - String caCertName = Credentials.CA_CERTIFICATE + name; - if (mClientCertificate != null) { - byte[] privKeyData = mClientPrivateKey.getEncoded(); - if (isHardwareBackedKey(mClientPrivateKey)) { - // Hardware backed key store is secure enough to store keys un-encrypted, this - // removes the need for user to punch a PIN to get access to these keys - if (DBG) Slog.d(TAG, "importing keys " + name + " in hardware backed " + - "store"); - ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, - KeyStore.FLAG_NONE); - } else { - // Software backed key store is NOT secure enough to store keys un-encrypted. - // Save keys encrypted so they are protected with user's PIN. User will - // have to unlock phone before being able to use these keys and connect to - // networks. - if (DBG) Slog.d(TAG, "importing keys " + name + " in software backed store"); - ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID, - KeyStore.FLAG_ENCRYPTED); - mNeedsSoftwareKeystore = true; - } - if (ret == false) { - return ret; - } - - ret = putCertInKeyStore(keyStore, userCertName, mClientCertificate); - if (ret == false) { - // Remove private key installed - keyStore.delKey(privKeyName, Process.WIFI_UID); - return ret; - } - } - - if (mCaCert != null) { - ret = putCertInKeyStore(keyStore, caCertName, mCaCert); - if (ret == false) { - if (mClientCertificate != null) { - // Remove client key+cert - keyStore.delKey(privKeyName, Process.WIFI_UID); - keyStore.delete(userCertName, Process.WIFI_UID); - } - return ret; - } - } - - // Set alias names - if (mClientCertificate != null) { - setClientCertificateAlias(name); - mClientPrivateKey = null; - mClientCertificate = null; - } - - if (mCaCert != null) { - setCaCertificateAlias(name); - mCaCert = null; - } - - return ret; - } - - private boolean putCertInKeyStore(android.security.KeyStore keyStore, String name, - Certificate cert) { - try { - byte[] certData = Credentials.convertToPem(cert); - if (DBG) Slog.d(TAG, "putting certificate " + name + " in keystore"); - return keyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE); - - } catch (IOException e1) { - return false; - } catch (CertificateException e2) { - return false; - } + /** + * @hide + */ + public void resetClientKeyEntry() { + mClientPrivateKey = null; + mClientCertificate = null; } - void removeKeys(KeyStore keyStore) { - String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); - // a valid client certificate is configured - if (!TextUtils.isEmpty(client)) { - if (DBG) Slog.d(TAG, "removing client private key and user cert"); - keyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); - keyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); - } - - String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); - // a valid ca certificate is configured - if (!TextUtils.isEmpty(ca)) { - if (DBG) Slog.d(TAG, "removing CA cert"); - keyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); - } + /** + * @hide + */ + public PrivateKey getClientPrivateKey() { + return mClientPrivateKey; } /** @@ -625,7 +472,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @param subjectMatch substring to be matched */ public void setSubjectMatch(String subjectMatch) { - setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, ""); + setFieldValue(WifiConfigStore.SUBJECT_MATCH_KEY, subjectMatch, ""); } /** @@ -633,147 +480,24 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the subject match string */ public String getSubjectMatch() { - return getFieldValue(SUBJECT_MATCH_KEY, ""); + return getFieldValue(WifiConfigStore.SUBJECT_MATCH_KEY, ""); } /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ String getKeyId(WifiEnterpriseConfig current) { - String eap = mFields.get(EAP_KEY); - String phase2 = mFields.get(PHASE2_KEY); + String eap = mFields.get(WifiConfigStore.EAP_KEY); + String phase2 = mFields.get(WifiConfigStore.PHASE2_KEY); // If either eap or phase2 are not initialized, use current config details if (TextUtils.isEmpty((eap))) { - eap = current.mFields.get(EAP_KEY); + eap = current.mFields.get(WifiConfigStore.EAP_KEY); } if (TextUtils.isEmpty(phase2)) { - phase2 = current.mFields.get(PHASE2_KEY); + phase2 = current.mFields.get(WifiConfigStore.PHASE2_KEY); } return eap + "_" + phase2; } - /** Migrates the old style TLS config to the new config style. This should only be used - * when restoring an old wpa_supplicant.conf or upgrading from a previous - * platform version. - * @return true if the config was updated - * @hide - */ - boolean migrateOldEapTlsNative(WifiNative wifiNative, int netId) { - String oldPrivateKey = wifiNative.getNetworkVariable(netId, OLD_PRIVATE_KEY_NAME); - /* - * If the old configuration value is not present, then there is nothing - * to do. - */ - if (TextUtils.isEmpty(oldPrivateKey)) { - return false; - } else { - // Also ignore it if it's empty quotes. - oldPrivateKey = removeDoubleQuotes(oldPrivateKey); - if (TextUtils.isEmpty(oldPrivateKey)) { - return false; - } - } - - mFields.put(ENGINE_KEY, ENGINE_ENABLE); - mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); - - /* - * 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 (oldPrivateKey.startsWith(KEYSTORE_URI)) { - keyName = new String(oldPrivateKey.substring(KEYSTORE_URI.length())); - } else { - keyName = oldPrivateKey; - } - mFields.put(PRIVATE_KEY_ID_KEY, convertToQuotedString(keyName)); - - wifiNative.setNetworkVariable(netId, ENGINE_KEY, mFields.get(ENGINE_KEY)); - wifiNative.setNetworkVariable(netId, ENGINE_ID_KEY, mFields.get(ENGINE_ID_KEY)); - wifiNative.setNetworkVariable(netId, PRIVATE_KEY_ID_KEY, mFields.get(PRIVATE_KEY_ID_KEY)); - // Remove old private_key string so we don't run this again. - wifiNative.setNetworkVariable(netId, OLD_PRIVATE_KEY_NAME, EMPTY_VALUE); - return true; - } - - /** Migrate certs from global pool to wifi UID if not already done */ - void migrateCerts(android.security.KeyStore keyStore) { - String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); - // a valid client certificate is configured - if (!TextUtils.isEmpty(client)) { - if (!keyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { - keyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, - Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); - keyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, - Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); - } - } - - String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); - // a valid ca certificate is configured - if (!TextUtils.isEmpty(ca)) { - if (!keyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { - keyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, - Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); - } - } - } - - void initializeSoftwareKeystoreFlag(android.security.KeyStore keyStore) { - String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); - if (!TextUtils.isEmpty(client)) { - // a valid client certificate is configured - - // BUGBUG: keyStore.get() never returns certBytes; because it is not - // taking WIFI_UID as a parameter. It always looks for certificate - // with SYSTEM_UID, and never finds any Wifi certificates. Assuming that - // all certificates need software keystore until we get the get() API - // fixed. - - mNeedsSoftwareKeystore = true; - - /* - try { - - if (DBG) Slog.d(TAG, "Loading client certificate " + Credentials - .USER_CERTIFICATE + client); - - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - if (factory == null) { - Slog.e(TAG, "Error getting certificate factory"); - return; - } - - byte[] certBytes = keyStore.get(Credentials.USER_CERTIFICATE + client); - if (certBytes != null) { - Certificate cert = (X509Certificate) factory.generateCertificate( - new ByteArrayInputStream(certBytes)); - - if (cert != null) { - mNeedsSoftwareKeystore = hasHardwareBackedKey(cert); - - if (DBG) Slog.d(TAG, "Loaded client certificate " + Credentials - .USER_CERTIFICATE + client); - if (DBG) Slog.d(TAG, "It " + (mNeedsSoftwareKeystore ? "needs" : - "does not need" ) + " software key store"); - } else { - Slog.d(TAG, "could not generate certificate"); - } - } else { - Slog.e(TAG, "Could not load client certificate " + Credentials - .USER_CERTIFICATE + client); - mNeedsSoftwareKeystore = true; - } - - } catch(CertificateException e) { - Slog.e(TAG, "Could not read certificates"); - mCaCert = null; - mClientCertificate = null; - } - */ - } - } - private String removeDoubleQuotes(String string) { if (TextUtils.isEmpty(string)) return ""; int length = string.length(); @@ -806,11 +530,12 @@ public class WifiEnterpriseConfig implements Parcelable { * @param key into the hash * @param prefix is the prefix that the value may have * @return value + * @hide */ - private String getFieldValue(String key, String prefix) { + String getFieldValue(String key, String prefix) { String value = mFields.get(key); // Uninitialized or known to be empty after reading from supplicant - if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return ""; + if (TextUtils.isEmpty(value) || WifiConfigStore.EMPTY_VALUE.equals(value)) return ""; value = removeDoubleQuotes(value); if (value.startsWith(prefix)) { @@ -827,12 +552,27 @@ public class WifiEnterpriseConfig implements Parcelable { */ private void setFieldValue(String key, String value, String prefix) { if (TextUtils.isEmpty(value)) { - mFields.put(key, EMPTY_VALUE); + mFields.put(key, WifiConfigStore.EMPTY_VALUE); } else { mFields.put(key, convertToQuotedString(prefix + value)); } } + + /** Set a value with an optional prefix at key + * @param key into the hash + * @param value to be set + * @param prefix an optional value to be prefixed to actual value + * @hide + */ + public void setFieldValue(String key, String value) { + if (TextUtils.isEmpty(value)) { + mFields.put(key, WifiConfigStore.EMPTY_VALUE); + } else { + mFields.put(key, convertToQuotedString(value)); + } + } + @Override public String toString() { StringBuffer sb = new StringBuffer(); |