diff options
| author | 2017-01-28 01:45:05 +0000 | |
|---|---|---|
| committer | 2017-01-28 01:45:05 +0000 | |
| commit | cd463ff269e2f7e3aafed7aca2eb7d23ff7b8ffc (patch) | |
| tree | 48eec0f24e85fa123de4c7602f5f0c2336028ddd | |
| parent | b5f2519abc540cb4f6adde059ac8826d084260d9 (diff) | |
| parent | 291ddaef788741fe724fec71760b90cbb6edaa2f (diff) | |
Add a client chain to WifiEnterpriseConfig
am: 291ddaef78
Change-Id: Ic8451631732bd3bfce7ff08f9f37b18745cda357
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | api/system-current.txt | 2 | ||||
| -rw-r--r-- | api/test-current.txt | 2 | ||||
| -rw-r--r-- | wifi/java/android/net/wifi/WifiEnterpriseConfig.java | 90 | ||||
| -rw-r--r-- | wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java | 36 |
5 files changed, 121 insertions, 11 deletions
diff --git a/api/current.txt b/api/current.txt index 289916305782..42275de65396 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24696,6 +24696,7 @@ package android.net.wifi { method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); @@ -24709,6 +24710,7 @@ package android.net.wifi { 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 setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); method public void setIdentity(java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index a9a12b42321d..6ebbee2f4e16 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -27069,6 +27069,7 @@ package android.net.wifi { method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); @@ -27082,6 +27083,7 @@ package android.net.wifi { 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 setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); method public void setIdentity(java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 820ed3447910..40085a26ef5d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -24769,6 +24769,7 @@ package android.net.wifi { method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); @@ -24782,6 +24783,7 @@ package android.net.wifi { 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 setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); method public void setIdentity(java.lang.String); diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index e410a9cf917e..5028d47481b0 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -142,7 +142,7 @@ public class WifiEnterpriseConfig implements Parcelable { private HashMap<String, String> mFields = new HashMap<String, String>(); private X509Certificate[] mCaCerts; private PrivateKey mClientPrivateKey; - private X509Certificate mClientCertificate; + private X509Certificate[] mClientCertificateChain; private int mEapMethod = Eap.NONE; private int mPhase2Method = Phase2.NONE; @@ -161,9 +161,19 @@ public class WifiEnterpriseConfig implements Parcelable { for (String key : source.mFields.keySet()) { mFields.put(key, source.mFields.get(key)); } - mCaCerts = source.mCaCerts; + if (source.mCaCerts != null) { + mCaCerts = Arrays.copyOf(source.mCaCerts, source.mCaCerts.length); + } else { + mCaCerts = null; + } mClientPrivateKey = source.mClientPrivateKey; - mClientCertificate = source.mClientCertificate; + if (source.mClientCertificateChain != null) { + mClientCertificateChain = Arrays.copyOf( + source.mClientCertificateChain, + source.mClientCertificateChain.length); + } else { + mClientCertificateChain = null; + } mEapMethod = source.mEapMethod; mPhase2Method = source.mPhase2Method; } @@ -185,7 +195,7 @@ public class WifiEnterpriseConfig implements Parcelable { dest.writeInt(mPhase2Method); ParcelUtil.writeCertificates(dest, mCaCerts); ParcelUtil.writePrivateKey(dest, mClientPrivateKey); - ParcelUtil.writeCertificate(dest, mClientCertificate); + ParcelUtil.writeCertificates(dest, mClientCertificateChain); } public static final Creator<WifiEnterpriseConfig> CREATOR = @@ -204,7 +214,7 @@ public class WifiEnterpriseConfig implements Parcelable { enterpriseConfig.mPhase2Method = in.readInt(); enterpriseConfig.mCaCerts = ParcelUtil.readCertificates(in); enterpriseConfig.mClientPrivateKey = ParcelUtil.readPrivateKey(in); - enterpriseConfig.mClientCertificate = ParcelUtil.readCertificate(in); + enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in); return enterpriseConfig; } @@ -742,10 +752,51 @@ public class WifiEnterpriseConfig implements Parcelable { * @throws IllegalArgumentException for an invalid key or certificate. */ public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { - if (clientCertificate != null) { - if (clientCertificate.getBasicConstraints() != -1) { - throw new IllegalArgumentException("Cannot be a CA certificate"); + setClientKeyEntryWithCertificateChain(privateKey, + new X509Certificate[] {clientCertificate}); + } + + /** + * Specify a private key and client certificate chain for client authorization. + * + * <p>A default name is automatically assigned to the key entry and used + * with this configuration. The framework takes care of installing the + * key entry when the config is saved and removing the key entry when + * the config is removed. + + * @param privateKey + * @param clientCertificateChain + * @throws IllegalArgumentException for an invalid key or certificate. + */ + public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey, + X509Certificate[] clientCertificateChain) { + X509Certificate[] newCerts = null; + if (clientCertificateChain != null && clientCertificateChain.length > 0) { + // We validate that this is a well formed chain that starts + // with an end-certificate and is followed by CA certificates. + // We don't validate that each following certificate verifies + // the previous. https://en.wikipedia.org/wiki/Chain_of_trust + // + // Basic constraints is an X.509 extension type that defines + // whether a given certificate is allowed to sign additional + // certificates and what path length restrictions may exist. + // We use this to judge whether the certificate is an end + // certificate or a CA certificate. + // https://cryptography.io/en/latest/x509/reference/ + if (clientCertificateChain[0].getBasicConstraints() != -1) { + throw new IllegalArgumentException( + "First certificate in the chain must be a client end certificate"); + } + + for (int i = 1; i < clientCertificateChain.length; i++) { + if (clientCertificateChain[i].getBasicConstraints() == -1) { + throw new IllegalArgumentException( + "All certificates following the first must be CA certificates"); + } } + newCerts = Arrays.copyOf(clientCertificateChain, + clientCertificateChain.length); + if (privateKey == null) { throw new IllegalArgumentException("Client cert without a private key"); } @@ -755,7 +806,7 @@ public class WifiEnterpriseConfig implements Parcelable { } mClientPrivateKey = privateKey; - mClientCertificate = clientCertificate; + mClientCertificateChain = newCerts; } /** @@ -764,7 +815,24 @@ public class WifiEnterpriseConfig implements Parcelable { * @return X.509 client certificate */ public X509Certificate getClientCertificate() { - return mClientCertificate; + if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { + return mClientCertificateChain[0]; + } else { + return null; + } + } + + /** + * Get the complete client certificate chain + * + * @return X.509 client certificates + */ + @Nullable public X509Certificate[] getClientCertificateChain() { + if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { + return mClientCertificateChain; + } else { + return null; + } } /** @@ -772,7 +840,7 @@ public class WifiEnterpriseConfig implements Parcelable { */ public void resetClientKeyEntry() { mClientPrivateKey = null; - mClientCertificate = null; + mClientCertificateChain = null; } /** diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java index 0e503d5e7139..5a67a7e5f285 100644 --- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java +++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java @@ -87,6 +87,42 @@ public class WifiEnterpriseConfigTest { } @Test + public void testSetClientCertificateChain() { + PrivateKey clientKey = FakeKeys.RSA_KEY1; + X509Certificate cert0 = FakeKeys.CLIENT_CERT; + X509Certificate cert1 = FakeKeys.CA_CERT1; + X509Certificate[] clientChain = new X509Certificate[] {cert0, cert1}; + mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); + X509Certificate[] result = mEnterpriseConfig.getClientCertificateChain(); + assertEquals(result.length, 2); + assertTrue(result[0] == cert0 && result[1] == cert1); + assertTrue(mEnterpriseConfig.getClientCertificate() == cert0); + } + + private boolean isClientCertificateChainInvalid(X509Certificate[] clientChain) { + boolean exceptionThrown = false; + try { + PrivateKey clientKey = FakeKeys.RSA_KEY1; + mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + return exceptionThrown; + } + + @Test + public void testSetInvalidClientCertificateChain() { + X509Certificate clientCert = FakeKeys.CLIENT_CERT; + X509Certificate caCert = FakeKeys.CA_CERT1; + assertTrue("Invalid client certificate", + isClientCertificateChainInvalid(new X509Certificate[] {caCert, caCert})); + assertTrue("Invalid CA certificate", + isClientCertificateChainInvalid(new X509Certificate[] {clientCert, clientCert})); + assertTrue("Both certificates invalid", + isClientCertificateChainInvalid(new X509Certificate[] {caCert, clientCert})); + } + + @Test public void testSaveSingleCaCertificateAlias() { final String alias = "single_alias 0"; mEnterpriseConfig.setCaCertificateAliases(new String[] {alias}); |