diff options
author | Irfan Sheriff <isheriff@google.com> | 2013-02-05 09:44:12 -0800 |
---|---|---|
committer | Irfan Sheriff <isheriff@google.com> | 2013-02-11 15:46:02 -0800 |
commit | 26d0076f0dbb021c4e5cc1b37b632b2223fd9278 (patch) | |
tree | c8e86780aa80b9c4cc03f8b2c685b62a37133821 /wifi/java/android/net/wifi/WifiEnterpriseConfig.java | |
parent | 080df3f38fc47f15ad321fff00689dcd07e1d075 (diff) | |
download | frameworks_base-26d0076f0dbb021c4e5cc1b37b632b2223fd9278.zip frameworks_base-26d0076f0dbb021c4e5cc1b37b632b2223fd9278.tar.gz frameworks_base-26d0076f0dbb021c4e5cc1b37b632b2223fd9278.tar.bz2 |
Track keys per config and allow cert push from apps
Allow configuring keys for a configuration and update/delete
from wificonfigstore.
Change-Id: I79b43bf7ca58f7efc95f7dcc125fc84d7aa8c795
Diffstat (limited to 'wifi/java/android/net/wifi/WifiEnterpriseConfig.java')
-rw-r--r-- | wifi/java/android/net/wifi/WifiEnterpriseConfig.java | 252 |
1 files changed, 241 insertions, 11 deletions
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 4dca7ac..7313e7e 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -19,7 +19,26 @@ import android.os.Parcel; import android.os.Parcelable; import android.security.Credentials; import android.text.TextUtils; - +import android.util.Log; + +import com.android.org.bouncycastle.asn1.ASN1InputStream; +import com.android.org.bouncycastle.asn1.ASN1Sequence; +import com.android.org.bouncycastle.asn1.DEROctetString; +import com.android.org.bouncycastle.asn1.x509.BasicConstraints; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.KeyFactory; +import java.security.KeyStore; +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; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.HashMap; import java.util.Map; @@ -70,6 +89,9 @@ public class WifiEnterpriseConfig implements Parcelable { private static final String PRIVATE_KEY_ID_KEY = "key_id"; private HashMap<String, String> mFields = new HashMap<String, String>(); + private X509Certificate mCaCert; + private PrivateKey mClientPrivateKey; + private X509Certificate mClientCertificate; /** This represents an empty value of an enterprise field. * NULL is used at wpa_supplicant to indicate an empty value @@ -103,6 +125,34 @@ public class WifiEnterpriseConfig implements Parcelable { dest.writeString(entry.getKey()); dest.writeString(entry.getValue()); } + + writeCertificate(dest, mCaCert); + + if (mClientPrivateKey != null) { + String algorithm = mClientPrivateKey.getAlgorithm(); + byte[] userKeyBytes = mClientPrivateKey.getEncoded(); + dest.writeInt(userKeyBytes.length); + dest.writeByteArray(userKeyBytes); + dest.writeString(algorithm); + } else { + dest.writeInt(0); + } + + writeCertificate(dest, mClientCertificate); + } + + private void writeCertificate(Parcel dest, X509Certificate cert) { + if (cert != null) { + try { + byte[] certBytes = cert.getEncoded(); + dest.writeInt(certBytes.length); + dest.writeByteArray(certBytes); + } catch (CertificateEncodingException e) { + dest.writeInt(0); + } + } else { + dest.writeInt(0); + } } public static final Creator<WifiEnterpriseConfig> CREATOR = @@ -115,9 +165,47 @@ public class WifiEnterpriseConfig implements Parcelable { String value = in.readString(); enterpriseConfig.mFields.put(key, value); } + + enterpriseConfig.mCaCert = readCertificate(in); + + PrivateKey userKey = null; + int len = in.readInt(); + if (len > 0) { + try { + byte[] bytes = new byte[len]; + in.readByteArray(bytes); + String algorithm = in.readString(); + KeyFactory keyFactory = KeyFactory.getInstance(algorithm); + userKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); + } catch (NoSuchAlgorithmException e) { + userKey = null; + } catch (InvalidKeySpecException e) { + userKey = null; + } + } + + enterpriseConfig.mClientPrivateKey = userKey; + enterpriseConfig.mClientCertificate = readCertificate(in); return enterpriseConfig; } + private X509Certificate readCertificate(Parcel in) { + X509Certificate cert = null; + int len = in.readInt(); + if (len > 0) { + try { + byte[] bytes = new byte[len]; + in.readByteArray(bytes); + CertificateFactory cFactory = CertificateFactory.getInstance("X.509"); + cert = (X509Certificate) cFactory + .generateCertificate(new ByteArrayInputStream(bytes)); + } catch (CertificateException e) { + cert = null; + } + } + return cert; + } + public WifiEnterpriseConfig[] newArray(int size) { return new WifiEnterpriseConfig[size]; } @@ -145,13 +233,13 @@ public class WifiEnterpriseConfig implements Parcelable { public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", "MSCHAPV2", "GTC" }; } - /** Internal use only @hide */ - public HashMap<String, String> getFields() { + /** Internal use only */ + HashMap<String, String> getFields() { return mFields; } - /** Internal use only @hide */ - public static String[] getSupplicantKeys() { + /** 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 }; @@ -271,28 +359,47 @@ public class WifiEnterpriseConfig implements Parcelable { * a certificate * </p> * @param alias identifies the certificate + * @hide */ - public void setCaCertificate(String alias) { + public void setCaCertificateAlias(String alias) { setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); } /** * Get CA certificate alias * @return alias to the CA certificate + * @hide */ - public String getCaCertificate() { + public String getCaCertificateAlias() { return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); } /** + * Specify a X.509 certificate that identifies the server. + * + * <p>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. * * <p> See the {@link android.security.KeyChain} for details on installing or choosing * a certificate * </p> * @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,12 +415,118 @@ 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. + * + * <p>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. * @param subjectMatch substring to be matched @@ -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()); } |