Merge "API to set multiple CA certificates for WifiEnterpriseConfig" into mm-wireless-dev

This commit is contained in:
Rubin Xu
2016-01-27 00:26:58 +00:00
committed by Android Partner Code Review
3 changed files with 178 additions and 10 deletions

View File

@@ -19218,6 +19218,7 @@ package android.net.wifi {
method public java.lang.String getAltSubjectMatch();
method public java.lang.String getAnonymousIdentity();
method public java.security.cert.X509Certificate getCaCertificate();
method public java.security.cert.X509Certificate[] getCaCertificates();
method public java.security.cert.X509Certificate getClientCertificate();
method public java.lang.String getDomainSuffixMatch();
method public int getEapMethod();
@@ -19230,6 +19231,7 @@ package android.net.wifi {
method public void setAltSubjectMatch(java.lang.String);
method public void setAnonymousIdentity(java.lang.String);
method public void setCaCertificate(java.security.cert.X509Certificate);
method public void setCaCertificates(java.security.cert.X509Certificate[]);
method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
method public void setDomainSuffixMatch(java.lang.String);
method public void setEapMethod(int);

View File

@@ -20994,6 +20994,7 @@ package android.net.wifi {
method public java.lang.String getAltSubjectMatch();
method public java.lang.String getAnonymousIdentity();
method public java.security.cert.X509Certificate getCaCertificate();
method public java.security.cert.X509Certificate[] getCaCertificates();
method public java.security.cert.X509Certificate getClientCertificate();
method public java.lang.String getDomainSuffixMatch();
method public int getEapMethod();
@@ -21006,6 +21007,7 @@ package android.net.wifi {
method public void setAltSubjectMatch(java.lang.String);
method public void setAnonymousIdentity(java.lang.String);
method public void setCaCertificate(java.security.cert.X509Certificate);
method public void setCaCertificates(java.security.cert.X509Certificate[]);
method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
method public void setDomainSuffixMatch(java.lang.String);
method public void setEapMethod(int);

View File

@@ -15,12 +15,14 @@
*/
package android.net.wifi;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.security.Credentials;
import android.text.TextUtils;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
@@ -71,6 +73,13 @@ public class WifiEnterpriseConfig implements Parcelable {
*/
public static final String KEYSTORE_URI = "keystore://";
/**
* String representing the keystore URI used for wpa_supplicant,
* Unlike #KEYSTORE_URI, this supports a list of space-delimited aliases
* @hide
*/
public static final String KEYSTORES_URI = "keystores://";
/**
* String to set the engine value to when it should be enabled.
* @hide
@@ -103,6 +112,8 @@ public class WifiEnterpriseConfig implements Parcelable {
public static final String PLMN_KEY = "plmn";
/** @hide */
public static final String PHASE1_KEY = "phase1";
/** @hide */
public static final String CA_CERT_ALIAS_DELIMITER = " ";
/** {@hide} */
public static final String ENABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=0\"";
@@ -113,7 +124,7 @@ public class WifiEnterpriseConfig implements Parcelable {
//By default, we enable TLS1.2. However, due to a known bug on some radius, we may disable it to
// fall back to TLS 1.1.
private boolean mTls12Enable = true;
private X509Certificate mCaCert;
private X509Certificate[] mCaCerts;
private PrivateKey mClientPrivateKey;
private X509Certificate mClientCertificate;
@@ -145,7 +156,7 @@ public class WifiEnterpriseConfig implements Parcelable {
dest.writeString(entry.getValue());
}
writeCertificate(dest, mCaCert);
writeCertificates(dest, mCaCerts);
if (mClientPrivateKey != null) {
String algorithm = mClientPrivateKey.getAlgorithm();
@@ -161,6 +172,17 @@ public class WifiEnterpriseConfig implements Parcelable {
dest.writeInt(mTls12Enable ? 1: 0);
}
private void writeCertificates(Parcel dest, X509Certificate[] cert) {
if (cert != null && cert.length != 0) {
dest.writeInt(cert.length);
for (int i = 0; i < cert.length; i++) {
writeCertificate(dest, cert[i]);
}
} else {
dest.writeInt(0);
}
}
private void writeCertificate(Parcel dest, X509Certificate cert) {
if (cert != null) {
try {
@@ -186,7 +208,7 @@ public class WifiEnterpriseConfig implements Parcelable {
enterpriseConfig.mFields.put(key, value);
}
enterpriseConfig.mCaCert = readCertificate(in);
enterpriseConfig.mCaCerts = readCertificates(in);
PrivateKey userKey = null;
int len = in.readInt();
@@ -210,6 +232,18 @@ public class WifiEnterpriseConfig implements Parcelable {
return enterpriseConfig;
}
private X509Certificate[] readCertificates(Parcel in) {
X509Certificate[] certs = null;
int len = in.readInt();
if (len > 0) {
certs = new X509Certificate[len];
for (int i = 0; i < len; i++) {
certs[i] = readCertificate(in);
}
}
return certs;
}
private X509Certificate readCertificate(Parcel in) {
X509Certificate cert = null;
int len = in.readInt();
@@ -429,6 +463,36 @@ public class WifiEnterpriseConfig implements Parcelable {
return getFieldValue(PASSWORD_KEY, "");
}
/**
* Encode a CA certificate alias so it does not contain illegal character.
* @hide
*/
public static String encodeCaCertificateAlias(String alias) {
byte[] bytes = alias.getBytes(StandardCharsets.UTF_8);
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte o : bytes) {
sb.append(String.format("%02x", o & 0xFF));
}
return sb.toString();
}
/**
* Decode a previously-encoded CA certificate alias.
* @hide
*/
public static String decodeCaCertificateAlias(String alias) {
byte[] data = new byte[alias.length() >> 1];
for (int n = 0, position = 0; n < alias.length(); n += 2, position++) {
data[position] = (byte) Integer.parseInt(alias.substring(n, n + 2), 16);
}
try {
return new String(data, StandardCharsets.UTF_8);
} catch (NumberFormatException e) {
e.printStackTrace();
return alias;
}
}
/**
* Set CA certificate alias.
*
@@ -442,6 +506,35 @@ public class WifiEnterpriseConfig implements Parcelable {
setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX);
}
/**
* Set CA certificate aliases. When creating installing the corresponding certificate to
* the keystore, please use alias encoded by {@link #encodeCaCertificateAlias(String)}.
*
* <p> See the {@link android.security.KeyChain} for details on installing or choosing
* a certificate.
* </p>
* @param aliases identifies the certificate
* @hide
*/
public void setCaCertificateAliases(@Nullable String[] aliases) {
if (aliases == null) {
setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX);
} else if (aliases.length == 1) {
// Backwards compatibility: use the original cert prefix if setting only one alias.
setCaCertificateAlias(aliases[0]);
} else {
// Use KEYSTORES_URI which supports multiple aliases.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < aliases.length; i++) {
if (i > 0) {
sb.append(CA_CERT_ALIAS_DELIMITER);
}
sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i]));
}
setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI);
}
}
/**
* Get CA certificate alias
* @return alias to the CA certificate
@@ -451,6 +544,32 @@ public class WifiEnterpriseConfig implements Parcelable {
return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
}
/**
* Get CA certificate aliases
* @return alias to the CA certificate
* @hide
*/
@Nullable public String[] getCaCertificateAliases() {
String value = getFieldValue(CA_CERT_KEY, "");
if (value.startsWith(CA_CERT_PREFIX)) {
// Backwards compatibility: parse the original alias prefix.
return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)};
} else if (value.startsWith(KEYSTORES_URI)) {
String values = value.substring(KEYSTORES_URI.length());
String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER);
for (int i = 0; i < aliases.length; i++) {
aliases[i] = decodeCaCertificateAlias(aliases[i]);
if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) {
aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length());
}
}
return aliases.length != 0 ? aliases : null;
} else {
return TextUtils.isEmpty(value) ? null : new String[] {value};
}
}
/**
* Specify a X.509 certificate that identifies the server.
*
@@ -462,31 +581,76 @@ public class WifiEnterpriseConfig implements Parcelable {
* @param cert X.509 CA certificate
* @throws IllegalArgumentException if not a CA certificate
*/
public void setCaCertificate(X509Certificate cert) {
public void setCaCertificate(@Nullable X509Certificate cert) {
if (cert != null) {
if (cert.getBasicConstraints() >= 0) {
mCaCert = cert;
mCaCerts = new X509Certificate[] {cert};
} else {
throw new IllegalArgumentException("Not a CA certificate");
}
} else {
mCaCert = null;
mCaCerts = null;
}
}
/**
* Get CA certificate
* Get CA certificate. If multiple CA certificates are configured previously,
* return the first one.
* @return X.509 CA certificate
*/
public X509Certificate getCaCertificate() {
return mCaCert;
@Nullable public X509Certificate getCaCertificate() {
if (mCaCerts != null && mCaCerts.length > 0) {
return mCaCerts[0];
} else {
return null;
}
}
/**
* Specify a list of X.509 certificates that identifies the server. The validation
* passes if the CA of server certificate matches one of the given certificates.
* <p>Default names are automatically assigned to the certificates and used
* with this configuration. The framework takes care of installing the
* certificates when the config is saved and removing the certificates when
* the config is removed.
*
* @param certs X.509 CA certificates
* @throws IllegalArgumentException if any of the provided certificates is
* not a CA certificate
*/
public void setCaCertificates(@Nullable X509Certificate[] certs) {
if (certs != null) {
X509Certificate[] newCerts = new X509Certificate[certs.length];
for (int i = 0; i < certs.length; i++) {
if (certs[i].getBasicConstraints() >= 0) {
newCerts[i] = certs[i];
} else {
throw new IllegalArgumentException("Not a CA certificate");
}
}
mCaCerts = newCerts;
} else {
mCaCerts = null;
}
}
/**
* Get CA certificates.
*/
@Nullable public X509Certificate[] getCaCertificates() {
if (mCaCerts != null || mCaCerts.length > 0) {
return mCaCerts;
} else {
return null;
}
}
/**
* @hide
*/
public void resetCaCertificate() {
mCaCert = null;
mCaCerts = null;
}
/** Set Client certificate alias.