1a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistpackage com.android.server.wifi.configparse; 2a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 3a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport android.content.Context; 4a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport android.net.Uri; 5a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport android.net.wifi.WifiConfiguration; 6a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport android.net.wifi.WifiEnterpriseConfig; 7de509a74735519a9efaa5e5d8d86728c084b804dJan Nordqvistimport android.provider.DocumentsContract; 8a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport android.util.Base64; 9a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport android.util.Log; 10a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 1107f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvistimport com.android.server.wifi.IMSIParameter; 12a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport com.android.server.wifi.anqp.eap.AuthParam; 13a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport com.android.server.wifi.anqp.eap.EAP; 14a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport com.android.server.wifi.anqp.eap.EAPMethod; 15a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport com.android.server.wifi.anqp.eap.NonEAPInnerAuth; 162e814680f4dd27a5f825afab189843582235cedcJan Nordqvistimport com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager; 17a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport com.android.server.wifi.hotspot2.pps.Credential; 18a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport com.android.server.wifi.hotspot2.pps.HomeSP; 19a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 20a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport org.xml.sax.SAXException; 21a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 22a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.io.ByteArrayInputStream; 23a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.io.IOException; 24a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.io.InputStreamReader; 25a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.io.LineNumberReader; 26a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.nio.charset.StandardCharsets; 27a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.GeneralSecurityException; 28a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.KeyStore; 29a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.MessageDigest; 30a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.PrivateKey; 31a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.cert.Certificate; 32a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.cert.CertificateFactory; 33a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.security.cert.X509Certificate; 34a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.util.ArrayList; 35a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.util.Arrays; 36a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.util.Enumeration; 37c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpandeimport java.util.HashSet; 38a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistimport java.util.List; 39a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 40a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvistpublic class ConfigBuilder { 414e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist public static final String WifiConfigType = "application/x-wifi-config"; 42a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static final String ProfileTag = "application/x-passpoint-profile"; 43a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static final String KeyTag = "application/x-pkcs12"; 44a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static final String CATag = "application/x-x509-ca-cert"; 45a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 467a94f84d2c4c7c34baaba9b87d9565f74b1eebfdJan Nordqvist private static final String X509 = "X.509"; 47e026d53099b5220c7184cb4bcee3af0542ffc6eeJan Nordqvist 48a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static final String TAG = "WCFG"; 49a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 50a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist public static WifiConfiguration buildConfig(String uriString, byte[] data, Context context) 51a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throws IOException, GeneralSecurityException, SAXException { 52a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "Content: " + (data != null ? data.length : -1)); 53a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 54a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist byte[] b64 = Base64.decode(new String(data, StandardCharsets.ISO_8859_1), Base64.DEFAULT); 55a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "Decoded: " + b64.length + " bytes."); 56a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 57a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist MIMEContainer mimeContainer = new 58a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist MIMEContainer(new LineNumberReader( 59a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist new InputStreamReader(new ByteArrayInputStream(b64), StandardCharsets.ISO_8859_1)), 60a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist null); 61a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (!mimeContainer.isBase64()) { 62a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Encoding for " + 63a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist mimeContainer.getContentType() + " is not base64"); 64a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 654e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist MIMEContainer inner; 664e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist if (mimeContainer.getContentType().equals(WifiConfigType)) { 674e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist byte[] wrappedContent = Base64.decode(mimeContainer.getText(), Base64.DEFAULT); 680600b63042d269dbf99197e188000a134511c978Jan Nordqvist Log.d(TAG, "Building container from '" + 690600b63042d269dbf99197e188000a134511c978Jan Nordqvist new String(wrappedContent, StandardCharsets.ISO_8859_1) + "'"); 704e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist inner = new MIMEContainer(new LineNumberReader( 714e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist new InputStreamReader(new ByteArrayInputStream(wrappedContent), 724e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist StandardCharsets.ISO_8859_1)), null); 734e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist } 744e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist else { 754e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist inner = mimeContainer; 764e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist } 771d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist return parse(inner); 78a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 79a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 801d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist private static WifiConfiguration parse(MIMEContainer root) 81a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throws IOException, GeneralSecurityException, SAXException { 82a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 83a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (root.getMimeContainers() == null) { 84a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Malformed MIME content: not multipart"); 85a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 86a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 87a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist String moText = null; 88a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist X509Certificate caCert = null; 89a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist PrivateKey clientKey = null; 90a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist List<X509Certificate> clientChain = null; 91a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 92a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist for (MIMEContainer subContainer : root.getMimeContainers()) { 934e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist Log.d(TAG, " + Content Type: " + subContainer.getContentType()); 94a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist switch (subContainer.getContentType()) { 95a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case ProfileTag: 96a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (subContainer.isBase64()) { 97a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT); 98a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist moText = new String(octets, StandardCharsets.UTF_8); 99a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } else { 100a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist moText = subContainer.getText(); 101a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 102a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "OMA: " + moText); 103a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist break; 104a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case CATag: { 105a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (!subContainer.isBase64()) { 106a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Can't read non base64 encoded cert"); 107a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 108a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 109a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT); 110e026d53099b5220c7184cb4bcee3af0542ffc6eeJan Nordqvist CertificateFactory factory = CertificateFactory.getInstance(X509); 111a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist caCert = (X509Certificate) factory.generateCertificate( 112a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist new ByteArrayInputStream(octets)); 113a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "Cert subject " + caCert.getSubjectX500Principal()); 1144e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist Log.d(TAG, "Full Cert: " + caCert); 115a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist break; 116a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 117a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case KeyTag: { 118a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (!subContainer.isBase64()) { 119a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Can't read non base64 encoded key"); 120a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 121a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 122a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT); 123a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 124a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist KeyStore ks = KeyStore.getInstance("PKCS12"); 125a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist ByteArrayInputStream in = new ByteArrayInputStream(octets); 126a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist ks.load(in, new char[0]); 127a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist in.close(); 128a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "---- Start PKCS12 info " + octets.length + ", size " + ks.size()); 129a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Enumeration<String> aliases = ks.aliases(); 130a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist while (aliases.hasMoreElements()) { 131a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist String alias = aliases.nextElement(); 132a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist clientKey = (PrivateKey) ks.getKey(alias, null); 133a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "Key: " + clientKey.getFormat()); 134a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Certificate[] chain = ks.getCertificateChain(alias); 135a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (chain != null) { 136a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist clientChain = new ArrayList<>(); 137a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist for (Certificate certificate : chain) { 138a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (!(certificate instanceof X509Certificate)) { 139a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.w(TAG, "Element in cert chain is not an X509Certificate: " + 140a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist certificate.getClass()); 141a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 142a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist clientChain.add((X509Certificate) certificate); 143a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 144a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "Chain: " + clientChain.size()); 145a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 146a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 147a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Log.d(TAG, "---- End PKCS12 info."); 148a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist break; 149a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 150a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 151a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 152a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 153a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (moText == null) { 154a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Missing profile"); 155a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 156a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 1572e814680f4dd27a5f825afab189843582235cedcJan Nordqvist HomeSP homeSP = PasspointManagementObjectManager.buildSP(moText); 1581d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist 1591d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist return buildConfig(homeSP, caCert, clientChain, clientKey); 160a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 161a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 1629cef0b96fd275dede1571b55feed636621717d3bJan Nordqvist private static WifiConfiguration buildConfig(HomeSP homeSP, X509Certificate caCert, 1631d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist List<X509Certificate> clientChain, PrivateKey key) 1641d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist throws IOException, GeneralSecurityException { 165a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 1664e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist WifiConfiguration config; 1674e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist 168a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID(); 169a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist switch (eapMethodID) { 170a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_TTLS: 171a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (key != null || clientChain != null) { 172a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan Log.w(TAG, "Client cert and/or key unnecessarily included with EAP-TTLS "+ 173a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan "profile"); 174a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 175a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan config = buildTTLSConfig(homeSP, caCert); 1764e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist break; 177a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_TLS: 178a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan config = buildTLSConfig(homeSP, clientChain, key, caCert); 1794e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist break; 180a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_AKA: 181a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_AKAPrim: 182a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_SIM: 183a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (key != null || clientChain != null || caCert != null) { 184a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan Log.i(TAG, "Client/CA cert and/or key unnecessarily included with " + 185a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist eapMethodID + " profile"); 186a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 1871d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist config = buildSIMConfig(homeSP); 1884e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist break; 189a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist default: 190a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Unsupported EAP Method: " + eapMethodID); 191a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 19205d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist 1934e940bb499f65b4305dbeb2e01237c43c2b0b42dJan Nordqvist return config; 194a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 195a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 1960600b63042d269dbf99197e188000a134511c978Jan Nordqvist // Retain for debugging purposes 1977a94f84d2c4c7c34baaba9b87d9565f74b1eebfdJan Nordqvist /* 1980600b63042d269dbf99197e188000a134511c978Jan Nordqvist private static void xIterateCerts(KeyStore ks, X509Certificate caCert) 1990600b63042d269dbf99197e188000a134511c978Jan Nordqvist throws GeneralSecurityException { 2000600b63042d269dbf99197e188000a134511c978Jan Nordqvist Enumeration<String> aliases = ks.aliases(); 2010600b63042d269dbf99197e188000a134511c978Jan Nordqvist while (aliases.hasMoreElements()) { 2020600b63042d269dbf99197e188000a134511c978Jan Nordqvist String alias = aliases.nextElement(); 2030600b63042d269dbf99197e188000a134511c978Jan Nordqvist Certificate cert = ks.getCertificate(alias); 2040600b63042d269dbf99197e188000a134511c978Jan Nordqvist Log.d("HS2J", "Checking " + alias); 2050600b63042d269dbf99197e188000a134511c978Jan Nordqvist if (cert instanceof X509Certificate) { 2060600b63042d269dbf99197e188000a134511c978Jan Nordqvist X509Certificate x509Certificate = (X509Certificate) cert; 2070600b63042d269dbf99197e188000a134511c978Jan Nordqvist boolean sm = x509Certificate.getSubjectX500Principal().equals( 2080600b63042d269dbf99197e188000a134511c978Jan Nordqvist caCert.getSubjectX500Principal()); 2090600b63042d269dbf99197e188000a134511c978Jan Nordqvist boolean eq = false; 2100600b63042d269dbf99197e188000a134511c978Jan Nordqvist if (sm) { 2110600b63042d269dbf99197e188000a134511c978Jan Nordqvist eq = Arrays.equals(x509Certificate.getEncoded(), caCert.getEncoded()); 2120600b63042d269dbf99197e188000a134511c978Jan Nordqvist } 2130600b63042d269dbf99197e188000a134511c978Jan Nordqvist Log.d("HS2J", "Subject: " + x509Certificate.getSubjectX500Principal() + 2140600b63042d269dbf99197e188000a134511c978Jan Nordqvist ": " + sm + "/" + eq); 2150600b63042d269dbf99197e188000a134511c978Jan Nordqvist } 2160600b63042d269dbf99197e188000a134511c978Jan Nordqvist } 2170600b63042d269dbf99197e188000a134511c978Jan Nordqvist } 2187a94f84d2c4c7c34baaba9b87d9565f74b1eebfdJan Nordqvist */ 2190600b63042d269dbf99197e188000a134511c978Jan Nordqvist 220a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan private static void setAnonymousIdentityToNaiRealm( 221a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan WifiConfiguration config, Credential credential) { 222a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan /** 223a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so 224a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * that this value will be sent to the EAP server as part of the EAP-Response/ Identity 225a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity 226a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * packet, and revert to using the (real) identity field for subsequent transactions that 227a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * request an identity (e.g. in EAP-TTLS). 228a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * 229a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * This NAI realm value (the portion of the identity after the '@') is used to tell the 230a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a 231a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * placeholder that is not used--it is set to this value by convention. See Section 5.1 of 232a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * RFC3748 for more details. 233a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * 234a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the 235a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to 236a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan * identify the device. 237a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan */ 238a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan config.enterpriseConfig.setAnonymousIdentity("anonymous@" + credential.getRealm()); 239a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan } 240a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan 241a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan private static WifiConfiguration buildTTLSConfig(HomeSP homeSP, X509Certificate caCert) 242a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throws IOException { 243a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Credential credential = homeSP.getCredential(); 244a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 245a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (credential.getUserName() == null || credential.getPassword() == null) { 246a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("EAP-TTLS provisioned without user name or password"); 247a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 248a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 249a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist EAPMethod eapMethod = credential.getEAPMethod(); 250a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 251a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist AuthParam authParam = eapMethod.getAuthParam(); 252a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (authParam == null || 253a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist authParam.getAuthInfoID() != EAP.AuthInfoID.NonEAPInnerAuthType) { 254a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Bad auth parameter for EAP-TTLS: " + authParam); 255a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 256a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 257a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist WifiConfiguration config = buildBaseConfiguration(homeSP); 258a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist NonEAPInnerAuth ttlsParam = (NonEAPInnerAuth) authParam; 25905d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 26005d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist enterpriseConfig.setPhase2Method(remapInnerMethod(ttlsParam.getType())); 26105d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist enterpriseConfig.setIdentity(credential.getUserName()); 26205d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist enterpriseConfig.setPassword(credential.getPassword()); 263a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan enterpriseConfig.setCaCertificate(caCert); 264a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan 265a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan setAnonymousIdentityToNaiRealm(config, credential); 266a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 267a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return config; 268a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 269a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 2700600b63042d269dbf99197e188000a134511c978Jan Nordqvist private static WifiConfiguration buildTLSConfig(HomeSP homeSP, 271a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist List<X509Certificate> clientChain, 272a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan PrivateKey clientKey, 273a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan X509Certificate caCert) 274a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throws IOException, GeneralSecurityException { 275a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 276a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Credential credential = homeSP.getCredential(); 277a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 278a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist X509Certificate clientCertificate = null; 279a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 280a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (clientKey == null || clientChain == null) { 281a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("No key and/or cert passed for EAP-TLS"); 282a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 283a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (credential.getCertType() != Credential.CertType.x509v3) { 284a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Invalid certificate type for TLS: " + 285a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist credential.getCertType()); 286a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 287a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 288a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist byte[] reference = credential.getFingerPrint(); 289a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist MessageDigest digester = MessageDigest.getInstance("SHA-256"); 290a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist for (X509Certificate certificate : clientChain) { 291a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist digester.reset(); 292a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist byte[] fingerprint = digester.digest(certificate.getEncoded()); 293a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (Arrays.equals(reference, fingerprint)) { 294a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist clientCertificate = certificate; 295a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist break; 296a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 297a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 298a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (clientCertificate == null) { 299a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("No certificate in chain matches supplied fingerprint"); 300a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 301a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 302a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist String alias = Base64.encodeToString(reference, Base64.DEFAULT); 303a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 304a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist WifiConfiguration config = buildBaseConfiguration(homeSP); 30505d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 30605d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist enterpriseConfig.setClientCertificateAlias(alias); 30705d2f4e6f26834a94b53187e6121379a16749088Jan Nordqvist enterpriseConfig.setClientKeyEntry(clientKey, clientCertificate); 308a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan enterpriseConfig.setCaCertificate(caCert); 309a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan 310a9b40d7093e260891d543f4fc418e6a7088f2c6cSamuel Tan setAnonymousIdentityToNaiRealm(config, credential); 311a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 312a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return config; 313a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 314a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 3151d5cd3938f9191184cd9aea3059a3b62bf3a0372Jan Nordqvist private static WifiConfiguration buildSIMConfig(HomeSP homeSP) 316a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throws IOException { 317a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 318a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist Credential credential = homeSP.getCredential(); 31907f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist IMSIParameter credImsi = credential.getImsi(); 320a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 32107f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist /* 32207f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist * Uncomment to enforce strict IMSI matching with currently installed SIM cards. 32307f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist * 324a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist TelephonyManager tm = TelephonyManager.from(context); 325a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist SubscriptionManager sub = SubscriptionManager.from(context); 326a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist boolean match = false; 327a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 328a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist for (int subId : sub.getActiveSubscriptionIdList()) { 329a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist String imsi = tm.getSubscriberId(subId); 33007f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist if (credImsi.matches(imsi)) { 331a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist match = true; 332a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist break; 333a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 334a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 335a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist if (!match) { 336a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Supplied IMSI does not match any SIM card"); 337a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 33807f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist */ 339a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 340a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist WifiConfiguration config = buildBaseConfiguration(homeSP); 34107f11f6f2ee7ec17cb08180035dfb5002aaaf5dfJan Nordqvist config.enterpriseConfig.setPlmn(credImsi.toString()); 342a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return config; 343a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 344a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 345a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static WifiConfiguration buildBaseConfiguration(HomeSP homeSP) throws IOException { 346a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID(); 347a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 348a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist WifiConfiguration config = new WifiConfiguration(); 349a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 350a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist config.FQDN = homeSP.getFQDN(); 351c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande 352c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande HashSet<Long> roamingConsortiumIds = homeSP.getRoamingConsortiums(); 353c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande config.roamingConsortiumIds = new long[roamingConsortiumIds.size()]; 354c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande int i = 0; 355c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande for (long id : roamingConsortiumIds) { 356c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande config.roamingConsortiumIds[i] = id; 357c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande i++; 358c0d8226ffa0e51c143271fead90dd3e8c6594d01Vinit Deshpande } 359a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist config.providerFriendlyName = homeSP.getFriendlyName(); 360a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 361a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 362a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); 363a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 364a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 365a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist enterpriseConfig.setEapMethod(remapEAPMethod(eapMethodID)); 366a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist enterpriseConfig.setRealm(homeSP.getCredential().getRealm()); 367a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist config.enterpriseConfig = enterpriseConfig; 3689cef0b96fd275dede1571b55feed636621717d3bJan Nordqvist // The framework based config builder only ever builds r1 configs: 3699cef0b96fd275dede1571b55feed636621717d3bJan Nordqvist config.updateIdentifier = null; 370a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 371a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return config; 372a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 373a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 374a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static int remapEAPMethod(EAP.EAPMethodID eapMethodID) throws IOException { 375a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist switch (eapMethodID) { 376a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_TTLS: 377a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Eap.TTLS; 378a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_TLS: 379a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Eap.TLS; 380a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_SIM: 381a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Eap.SIM; 382a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_AKA: 383a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Eap.AKA; 384a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case EAP_AKAPrim: 385a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Eap.AKA_PRIME; 386a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist default: 387a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Bad EAP method: " + eapMethodID); 388a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 389a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 390a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist 391a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist private static int remapInnerMethod(NonEAPInnerAuth.NonEAPType type) throws IOException { 392a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist switch (type) { 393a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case PAP: 394a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Phase2.PAP; 395a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case MSCHAP: 396a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Phase2.MSCHAP; 397a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case MSCHAPv2: 398a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist return WifiEnterpriseConfig.Phase2.MSCHAPV2; 399a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist case CHAP: 400a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist default: 401a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist throw new IOException("Inner method " + type + " not supported"); 402a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 403a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist } 404a1edc185d46d85e04930a5e12b465de9fea64afeJan Nordqvist} 405