diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 9deacde23b372..f119a4b45b216 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -36,6 +36,7 @@ import android.os.Message;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UserHandle;
+import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
@@ -143,6 +144,7 @@ class WifiConfigStore {
private static final String EOS = "eos";
private WifiNative mWifiNative;
+ private final KeyStore mKeyStore = KeyStore.getInstance();
WifiConfigStore(Context c, WifiNative wn) {
mContext = c;
@@ -294,16 +296,7 @@ class WifiConfigStore {
boolean forgetNetwork(int netId) {
if (mWifiNative.removeNetwork(netId)) {
mWifiNative.saveConfig();
- WifiConfiguration target = null;
- WifiConfiguration config = mConfiguredNetworks.get(netId);
- if (config != null) {
- target = mConfiguredNetworks.remove(netId);
- mNetworkIds.remove(configKey(config));
- }
- if (target != null) {
- writeIpAndProxyConfigurations();
- sendConfiguredNetworksChangedBroadcast(target, WifiManager.CHANGE_REASON_REMOVED);
- }
+ removeConfigAndSendBroadcastIfNeeded(netId);
return true;
} else {
loge("Failed to remove network " + netId);
@@ -341,20 +334,27 @@ class WifiConfigStore {
*/
boolean removeNetwork(int netId) {
boolean ret = mWifiNative.removeNetwork(netId);
- WifiConfiguration config = null;
if (ret) {
- config = mConfiguredNetworks.get(netId);
- if (config != null) {
- config = mConfiguredNetworks.remove(netId);
- mNetworkIds.remove(configKey(config));
- }
- }
- if (config != null) {
- sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
+ removeConfigAndSendBroadcastIfNeeded(netId);
}
return ret;
}
+ private void removeConfigAndSendBroadcastIfNeeded(int netId) {
+ WifiConfiguration config = mConfiguredNetworks.get(netId);
+ if (config != null) {
+ // Remove any associated keys
+ if (config.enterpriseConfig != null) {
+ config.enterpriseConfig.removeKeys(mKeyStore);
+ }
+ mConfiguredNetworks.remove(netId);
+ mNetworkIds.remove(configKey(config));
+
+ writeIpAndProxyConfigurations();
+ sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
+ }
+ }
+
/**
* Enable a network. Note that there is no saveConfig operation.
* This function is retained for compatibility with the public
@@ -1122,13 +1122,48 @@ class WifiConfigStore {
}
if (config.enterpriseConfig != null) {
- HashMap
A default name is automatically assigned to the certificate and used + * with this configuration. + * @param cert X.509 CA certificate + * @throws IllegalArgumentException if not a CA certificate + */ + public void setCaCertificate(X509Certificate cert) { + if (cert.getBasicConstraints() >= 0) { + mCaCert = cert; + } else { + throw new IllegalArgumentException("Not a CA certificate"); + } + } + /** * Set Client certificate alias. * @@ -291,8 +397,9 @@ public class WifiEnterpriseConfig implements Parcelable { * a certificate *
* @param alias identifies the certificate + * @hide */ - public void setClientCertificate(String alias) { + public void setClientCertificateAlias(String alias) { setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); // Also, set engine parameters @@ -308,11 +415,117 @@ public class WifiEnterpriseConfig implements Parcelable { /** * Get client certificate alias * @return alias to the client certificate + * @hide */ - public String getClientCertificate() { + public String getClientCertificateAlias() { return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); } + /** + * Specify a private key and client certificate for client authorization. + * + *A default name is automatically assigned to the key entry and used + * with this configuration. + * @param privateKey + * @param clientCertificate + */ + public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { + if (clientCertificate != null) { + if (clientCertificate.getBasicConstraints() != -1) { + throw new IllegalArgumentException("Cannot be a CA certificate"); + } + if (privateKey == null) { + throw new IllegalArgumentException("Client cert without a private key"); + } + if (privateKey.getEncoded() == null) { + throw new IllegalArgumentException("Private key cannot be encoded"); + } + } + + mClientPrivateKey = privateKey; + mClientCertificate = clientCertificate; + } + + boolean needsKeyStore() { + // Has no keys to be installed + if (mClientCertificate == null && mCaCert == null) return false; + return true; + } + + 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(); + ret = keyStore.importKey(privKeyName, privKeyData); + if (ret == false) { + return ret; + } + + ret = putCertInKeyStore(keyStore, userCertName, mClientCertificate); + if (ret == false) { + // Remove private key installed + keyStore.delKey(privKeyName); + return ret; + } + } + + if (mCaCert != null) { + ret = putCertInKeyStore(keyStore, caCertName, mCaCert); + if (ret == false) { + if (mClientCertificate != null) { + // Remove client key+cert + keyStore.delKey(privKeyName); + keyStore.delete(userCertName); + } + 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); + return keyStore.put(name, certData); + } catch (IOException e1) { + return false; + } catch (CertificateException e2) { + return false; + } + } + + void removeKeys(android.security.KeyStore keyStore) { + String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); + // a valid client certificate is configured + if (!TextUtils.isEmpty(client)) { + keyStore.delKey(Credentials.USER_PRIVATE_KEY + client); + keyStore.delete(Credentials.USER_CERTIFICATE + client); + } + + String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); + // a valid ca certificate is configured + if (!TextUtils.isEmpty(ca)) { + keyStore.delete(Credentials.CA_CERTIFICATE + ca); + } + } + /** * Set subject match. This is the substring to be matched against the subject of the * authentication server certificate. @@ -330,13 +543,28 @@ public class WifiEnterpriseConfig implements Parcelable { return getFieldValue(SUBJECT_MATCH_KEY, ""); } + /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ + String getKeyId(WifiEnterpriseConfig current) { + String eap = mFields.get(EAP_KEY); + String phase2 = mFields.get(PHASE2_KEY); + + // If either eap or phase2 are not initialized, use current config details + if (TextUtils.isEmpty((eap))) { + eap = current.mFields.get(EAP_KEY); + } + if (TextUtils.isEmpty(phase2)) { + phase2 = current.mFields.get(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 */ - public boolean migrateOldEapTlsNative(WifiNative wifiNative, int netId) { + 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 @@ -395,6 +623,7 @@ public class WifiEnterpriseConfig implements Parcelable { * @return the index into array */ private int getStringIndex(String arr[], String toBeFound, int defaultIndex) { + if (TextUtils.isEmpty(toBeFound)) return defaultIndex; for (int i = 0; i < arr.length; i++) { if (toBeFound.equals(arr[i])) return i; } @@ -408,7 +637,8 @@ public class WifiEnterpriseConfig implements Parcelable { */ private String getFieldValue(String key, String prefix) { String value = mFields.get(key); - if (EMPTY_VALUE.equals(value)) return ""; + // Uninitialized or known to be empty after reading from supplicant + if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return ""; return removeDoubleQuotes(value).substring(prefix.length()); }