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