diff options
-rw-r--r-- | service/java/com/android/server/wifi/WifiKeyStore.java | 129 | ||||
-rw-r--r-- | tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java | 120 |
2 files changed, 214 insertions, 35 deletions
diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java index 70daa0d55..c248d2269 100644 --- a/service/java/com/android/server/wifi/WifiKeyStore.java +++ b/service/java/com/android/server/wifi/WifiKeyStore.java @@ -30,6 +30,7 @@ import com.android.server.wifi.util.ArrayUtils; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -276,52 +277,110 @@ public class WifiKeyStore { // For WPA3-Enterprise 192-bit networks, set the SuiteBCipher field based on the // CA certificate type. Suite-B requires SHA384, reject other certs. if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) { - // Read the first CA certificate, and initialize - Certificate caCert = null; + // Read the CA certificates, and initialize + String[] caAliases = config.enterpriseConfig.getCaCertificateAliases(); + + if (caAliases == null || caAliases.length == 0) { + Log.e(TAG, "No CA aliases in profile"); + return false; + } + + int caCertType = -1; + int prevCaCertType = -1; + for (String caAlias : caAliases) { + Certificate caCert = null; + try { + caCert = mKeyStore.getCertificate(caAlias); + } catch (KeyStoreException e) { + Log.e(TAG, "Failed to get Suite-B certificate", e); + } + if (caCert == null || !(caCert instanceof X509Certificate)) { + Log.e(TAG, "Failed reading CA certificate for Suite-B"); + return false; + } + + // Confirm that the CA certificate is compatible with Suite-B requirements + caCertType = getSuiteBCipherFromCert((X509Certificate) caCert); + if (caCertType < 0) { + return false; + } + if (prevCaCertType != -1) { + if (prevCaCertType != caCertType) { + Log.e(TAG, "Incompatible CA certificates"); + return false; + } + } + prevCaCertType = caCertType; + } + + Certificate clientCert = null; try { - caCert = mKeyStore.getCertificate(config.enterpriseConfig.getCaCertificateAlias()); + clientCert = mKeyStore.getCertificate(config.enterpriseConfig + .getClientCertificateAlias()); } catch (KeyStoreException e) { - Log.e(TAG, "Failed to get Suite-B certificate", e); + Log.e(TAG, "Failed to get Suite-B client certificate", e); } - if (caCert == null || !(caCert instanceof X509Certificate)) { - Log.e(TAG, "Failed reading CA certificate for Suite-B"); + if (clientCert == null || !(clientCert instanceof X509Certificate)) { + Log.e(TAG, "Failed reading client certificate for Suite-B"); return false; } - X509Certificate x509CaCert = (X509Certificate) caCert; - String sigAlgOid = x509CaCert.getSigAlgOID(); - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Signature algorithm: " + sigAlgOid); + + int clientCertType = getSuiteBCipherFromCert((X509Certificate) clientCert); + if (clientCertType < 0) { + return false; } - config.allowedSuiteBCiphers.clear(); - // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates - // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192 - // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term - // Suite-B was already coined in the IEEE 802.11-2016 specification for - // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates - // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally - // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments, - // we are supporting both types here. - if (sigAlgOid.equals("1.2.840.113549.1.1.12")) { - // sha384WithRSAEncryption - config.allowedSuiteBCiphers.set( - WifiConfiguration.SuiteBCipher.ECDHE_RSA); - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Selecting Suite-B RSA"); - } - } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) { - // ecdsa-with-SHA384 - config.allowedSuiteBCiphers.set( - WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); - if (mVerboseLoggingEnabled) { - Log.d(TAG, "Selecting Suite-B ECDSA"); - } + if (clientCertType == caCertType) { + config.allowedSuiteBCiphers.clear(); + config.allowedSuiteBCiphers.set(clientCertType); } else { - Log.e(TAG, "Invalid CA certificate type for Suite-B: " - + sigAlgOid); + Log.e(TAG, "Client certificate for Suite-B is incompatible with the CA " + + "certificate"); return false; } } return true; } + + /** + * Get the Suite-B cipher from the certificate + * + * @param x509Certificate Certificate to process + * @return WifiConfiguration.SuiteBCipher.ECDHE_RSA if the certificate OID matches the Suite-B + * requirements for RSA certificates, WifiConfiguration.SuiteBCipher.ECDHE_ECDSA if the + * certificate OID matches the Suite-B requirements for ECDSA certificates, or -1 otherwise. + */ + private int getSuiteBCipherFromCert(X509Certificate x509Certificate) { + String sigAlgOid = x509Certificate.getSigAlgOID(); + if (mVerboseLoggingEnabled) { + Principal p = x509Certificate.getSubjectX500Principal(); + if (p != null && !TextUtils.isEmpty(p.getName())) { + Log.d(TAG, "Checking cert " + p.getName()); + } + } + + // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates + // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192 + // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term + // Suite-B was already coined in the IEEE 802.11-2016 specification for + // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates + // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally + // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments, + // we are supporting both types here. + if (sigAlgOid.equals("1.2.840.113549.1.1.12")) { + // sha384WithRSAEncryption + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Found Suite-B RSA certificate"); + } + return WifiConfiguration.SuiteBCipher.ECDHE_RSA; + } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) { + // ecdsa-with-SHA384 + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Found Suite-B ECDSA certificate"); + } + return WifiConfiguration.SuiteBCipher.ECDHE_ECDSA; + } + Log.e(TAG, "Invalid certificate type for Suite-B: " + sigAlgOid); + return -1; + } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java index 8eef7e7d0..f6cae66db 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiKeyStoreTest.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalMatchers.aryEq; @@ -223,6 +224,8 @@ public class WifiKeyStoreTest extends WifiBaseTest { */ @Test public void testConfigureSuiteBRsa3072() throws Exception { + when(mWifiEnterpriseConfig.getCaCertificateAliases()) + .thenReturn(new String[]{USER_CA_CERT_ALIAS}); when(mWifiEnterpriseConfig.getClientPrivateKey()) .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY); when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( @@ -249,6 +252,8 @@ public class WifiKeyStoreTest extends WifiBaseTest { */ @Test public void testConfigureSuiteBEcdsa() throws Exception { + when(mWifiEnterpriseConfig.getCaCertificateAliases()) + .thenReturn(new String[]{USER_CA_CERT_ALIAS}); when(mWifiEnterpriseConfig.getClientPrivateKey()) .thenReturn(FakeKeys.CLIENT_SUITE_B_ECC_KEY); when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( @@ -269,4 +274,119 @@ public class WifiKeyStoreTest extends WifiBaseTest { assertTrue( savedNetwork.allowedSuiteBCiphers.get(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA)); } + + /** + * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when CA and client + * certificates are not of the same type. + */ + @Test + public void testConfigurationFailureSuiteB() throws Exception { + // Create a configuration with RSA client cert and ECDSA CA cert + when(mWifiEnterpriseConfig.getClientPrivateKey()) + .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY); + when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_SUITE_B_ECDSA_CERT); + when(mWifiEnterpriseConfig.getClientCertificateChain()) + .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT}); + when(mWifiEnterpriseConfig.getCaCertificates()) + .thenReturn(new X509Certificate[]{FakeKeys.CA_SUITE_B_ECDSA_CERT}); + when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn( + FakeKeys.CA_SUITE_B_ECDSA_CERT); + WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork( + WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); + savedNetwork.enterpriseConfig = mWifiEnterpriseConfig; + assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null)); + } + + /** + * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when CA is RSA but not + * with the required security + */ + @Test + public void testConfigurationFailureSuiteBNon3072Rsa() throws Exception { + // Create a configuration with RSA client cert and weak RSA CA cert + when(mWifiEnterpriseConfig.getClientPrivateKey()) + .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY); + when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_CERT0); + when(mWifiEnterpriseConfig.getClientCertificateChain()) + .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT}); + when(mWifiEnterpriseConfig.getCaCertificates()) + .thenReturn(new X509Certificate[]{FakeKeys.CA_CERT0}); + when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn( + FakeKeys.CA_CERT0); + WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork( + WifiConfiguration.SuiteBCipher.ECDHE_RSA); + savedNetwork.enterpriseConfig = mWifiEnterpriseConfig; + assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null)); + } + + /** + * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when one CA in the list + * is RSA but not with the required security + */ + @Test + public void testConfigurationFailureSuiteBNon3072RsaInList() throws Exception { + // Create a configuration with RSA client cert and weak RSA CA cert + when(mWifiEnterpriseConfig.getClientPrivateKey()) + .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY); + when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_SUITE_B_RSA3072_CERT); + when(mWifiEnterpriseConfig.getClientCertificateChain()) + .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT}); + when(mWifiEnterpriseConfig.getCaCertificates()) + .thenReturn( + new X509Certificate[]{FakeKeys.CA_SUITE_B_RSA3072_CERT, FakeKeys.CA_CERT0}); + when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn( + FakeKeys.CA_SUITE_B_RSA3072_CERT); + when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[1]))).thenReturn( + FakeKeys.CA_CERT0); + when(mWifiEnterpriseConfig.getCaCertificateAliases()) + .thenReturn(USER_CA_CERT_ALIASES); + WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork( + WifiConfiguration.SuiteBCipher.ECDHE_RSA); + savedNetwork.enterpriseConfig = mWifiEnterpriseConfig; + assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null)); + } + + /** + * Test configuring WPA3-Enterprise in 192-bit mode for RSA 3072 fails when one CA in the list + * is RSA and the other is ECDSA + */ + @Test + public void testConfigurationFailureSuiteBRsaAndEcdsaInList() throws Exception { + // Create a configuration with RSA client cert and weak RSA CA cert + when(mWifiEnterpriseConfig.getClientPrivateKey()) + .thenReturn(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY); + when(mWifiEnterpriseConfig.getClientCertificate()).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mWifiEnterpriseConfig.getCaCertificate()).thenReturn(FakeKeys.CA_SUITE_B_RSA3072_CERT); + when(mWifiEnterpriseConfig.getClientCertificateChain()) + .thenReturn(new X509Certificate[]{FakeKeys.CLIENT_SUITE_B_RSA3072_CERT}); + when(mWifiEnterpriseConfig.getCaCertificates()) + .thenReturn( + new X509Certificate[]{FakeKeys.CA_SUITE_B_RSA3072_CERT, + FakeKeys.CA_SUITE_B_ECDSA_CERT}); + when(mKeyStore.getCertificate(eq(USER_CERT_ALIAS))).thenReturn( + FakeKeys.CLIENT_SUITE_B_RSA3072_CERT); + when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[0]))).thenReturn( + FakeKeys.CA_SUITE_B_RSA3072_CERT); + when(mKeyStore.getCertificate(eq(USER_CA_CERT_ALIASES[1]))).thenReturn( + FakeKeys.CA_SUITE_B_ECDSA_CERT); + when(mWifiEnterpriseConfig.getCaCertificateAliases()) + .thenReturn(USER_CA_CERT_ALIASES); + WifiConfiguration savedNetwork = WifiConfigurationTestUtil.createEapSuiteBNetwork( + WifiConfiguration.SuiteBCipher.ECDHE_RSA); + savedNetwork.enterpriseConfig = mWifiEnterpriseConfig; + assertFalse(mWifiKeyStore.updateNetworkKeys(savedNetwork, null)); + } } |