1 package com.android.configparse; 2 3 import android.content.Context; 4 import android.net.Uri; 5 import android.net.wifi.WifiConfiguration; 6 import android.net.wifi.WifiEnterpriseConfig; 7 import android.util.Base64; 8 import android.util.Log; 9 10 import com.android.anqp.eap.AuthParam; 11 import com.android.anqp.eap.EAP; 12 import com.android.anqp.eap.EAPMethod; 13 import com.android.anqp.eap.NonEAPInnerAuth; 14 import com.android.hotspot2.IMSIParameter; 15 import com.android.hotspot2.pps.Credential; 16 import com.android.hotspot2.pps.HomeSP; 17 18 import java.io.IOException; 19 import java.security.GeneralSecurityException; 20 import java.security.MessageDigest; 21 import java.security.PrivateKey; 22 import java.security.cert.X509Certificate; 23 import java.util.Arrays; 24 import java.util.HashSet; 25 import java.util.List; 26 27 public class ConfigBuilder { 28 private static final String TAG = "WCFG"; 29 30 private static void dropFile(Uri uri, Context context) { 31 context.getContentResolver().delete(uri, null, null); 32 } 33 34 public static WifiConfiguration buildConfig(HomeSP homeSP, X509Certificate caCert, 35 List<X509Certificate> clientChain, PrivateKey key) 36 throws IOException, GeneralSecurityException { 37 38 Credential credential = homeSP.getCredential(); 39 40 WifiConfiguration config; 41 42 EAP.EAPMethodID eapMethodID = credential.getEAPMethod().getEAPMethodID(); 43 switch (eapMethodID) { 44 case EAP_TTLS: 45 if (key != null || clientChain != null) { 46 Log.w(TAG, "Client cert and/or key included with EAP-TTLS profile"); 47 } 48 config = buildTTLSConfig(homeSP); 49 break; 50 case EAP_TLS: 51 config = buildTLSConfig(homeSP, clientChain, key); 52 break; 53 case EAP_AKA: 54 case EAP_AKAPrim: 55 case EAP_SIM: 56 if (key != null || clientChain != null || caCert != null) { 57 Log.i(TAG, "Client/CA cert and/or key included with " + 58 eapMethodID + " profile"); 59 } 60 config = buildSIMConfig(homeSP); 61 break; 62 default: 63 throw new IOException("Unsupported EAP Method: " + eapMethodID); 64 } 65 66 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 67 68 enterpriseConfig.setCaCertificate(caCert); 69 enterpriseConfig.setAnonymousIdentity("anonymous@" + credential.getRealm()); 70 71 return config; 72 } 73 74 // Retain for debugging purposes 75 /* 76 private static void xIterateCerts(KeyStore ks, X509Certificate caCert) 77 throws GeneralSecurityException { 78 Enumeration<String> aliases = ks.aliases(); 79 while (aliases.hasMoreElements()) { 80 String alias = aliases.nextElement(); 81 Certificate cert = ks.getCertificate(alias); 82 Log.d("HS2J", "Checking " + alias); 83 if (cert instanceof X509Certificate) { 84 X509Certificate x509Certificate = (X509Certificate) cert; 85 boolean sm = x509Certificate.getSubjectX500Principal().equals( 86 caCert.getSubjectX500Principal()); 87 boolean eq = false; 88 if (sm) { 89 eq = Arrays.equals(x509Certificate.getEncoded(), caCert.getEncoded()); 90 } 91 Log.d("HS2J", "Subject: " + x509Certificate.getSubjectX500Principal() + 92 ": " + sm + "/" + eq); 93 } 94 } 95 } 96 */ 97 98 private static WifiConfiguration buildTTLSConfig(HomeSP homeSP) 99 throws IOException { 100 Credential credential = homeSP.getCredential(); 101 102 if (credential.getUserName() == null || credential.getPassword() == null) { 103 throw new IOException("EAP-TTLS provisioned without user name or password"); 104 } 105 106 EAPMethod eapMethod = credential.getEAPMethod(); 107 108 AuthParam authParam = eapMethod.getAuthParam(); 109 if (authParam == null || 110 authParam.getAuthInfoID() != EAP.AuthInfoID.NonEAPInnerAuthType) { 111 throw new IOException("Bad auth parameter for EAP-TTLS: " + authParam); 112 } 113 114 WifiConfiguration config = buildBaseConfiguration(homeSP); 115 NonEAPInnerAuth ttlsParam = (NonEAPInnerAuth) authParam; 116 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 117 enterpriseConfig.setPhase2Method(remapInnerMethod(ttlsParam.getType())); 118 enterpriseConfig.setIdentity(credential.getUserName()); 119 enterpriseConfig.setPassword(credential.getPassword()); 120 121 return config; 122 } 123 124 private static WifiConfiguration buildTLSConfig(HomeSP homeSP, 125 List<X509Certificate> clientChain, 126 PrivateKey clientKey) 127 throws IOException, GeneralSecurityException { 128 129 Credential credential = homeSP.getCredential(); 130 131 X509Certificate clientCertificate = null; 132 133 if (clientKey == null || clientChain == null) { 134 throw new IOException("No key and/or cert passed for EAP-TLS"); 135 } 136 if (credential.getCertType() != Credential.CertType.x509v3) { 137 throw new IOException("Invalid certificate type for TLS: " + 138 credential.getCertType()); 139 } 140 141 byte[] reference = credential.getFingerPrint(); 142 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 143 for (X509Certificate certificate : clientChain) { 144 digester.reset(); 145 byte[] fingerprint = digester.digest(certificate.getEncoded()); 146 if (Arrays.equals(reference, fingerprint)) { 147 clientCertificate = certificate; 148 break; 149 } 150 } 151 if (clientCertificate == null) { 152 throw new IOException("No certificate in chain matches supplied fingerprint"); 153 } 154 155 String alias = Base64.encodeToString(reference, Base64.DEFAULT); 156 157 WifiConfiguration config = buildBaseConfiguration(homeSP); 158 WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig; 159 enterpriseConfig.setClientCertificateAlias(alias); 160 enterpriseConfig.setClientKeyEntry(clientKey, clientCertificate); 161 162 return config; 163 } 164 165 private static WifiConfiguration buildSIMConfig(HomeSP homeSP) 166 throws IOException { 167 168 Credential credential = homeSP.getCredential(); 169 IMSIParameter credImsi = credential.getImsi(); 170 171 /* 172 * Uncomment to enforce strict IMSI matching with currently installed SIM cards. 173 * 174 TelephonyManager tm = TelephonyManager.from(context); 175 SubscriptionManager sub = SubscriptionManager.from(context); 176 boolean match = false; 177 178 for (int subId : sub.getActiveSubscriptionIdList()) { 179 String imsi = tm.getSubscriberId(subId); 180 if (credImsi.matches(imsi)) { 181 match = true; 182 break; 183 } 184 } 185 if (!match) { 186 throw new IOException("Supplied IMSI does not match any SIM card"); 187 } 188 */ 189 190 WifiConfiguration config = buildBaseConfiguration(homeSP); 191 config.enterpriseConfig.setPlmn(credImsi.toString()); 192 return config; 193 } 194 195 private static WifiConfiguration buildBaseConfiguration(HomeSP homeSP) throws IOException { 196 EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID(); 197 198 WifiConfiguration config = new WifiConfiguration(); 199 200 config.FQDN = homeSP.getFQDN(); 201 202 HashSet<Long> roamingConsortiumIds = homeSP.getRoamingConsortiums(); 203 config.roamingConsortiumIds = new long[roamingConsortiumIds.size()]; 204 int i = 0; 205 for (long id : roamingConsortiumIds) { 206 config.roamingConsortiumIds[i] = id; 207 i++; 208 } 209 config.providerFriendlyName = homeSP.getFriendlyName(); 210 211 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 212 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); 213 214 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 215 enterpriseConfig.setEapMethod(remapEAPMethod(eapMethodID)); 216 enterpriseConfig.setRealm(homeSP.getCredential().getRealm()); 217 if (homeSP.getUpdateIdentifier() >= 0) { 218 config.updateIdentifier = Integer.toString(homeSP.getUpdateIdentifier()); 219 } 220 config.enterpriseConfig = enterpriseConfig; 221 if (homeSP.getUpdateIdentifier() >= 0) { 222 config.updateIdentifier = Integer.toString(homeSP.getUpdateIdentifier()); 223 } 224 225 return config; 226 } 227 228 private static int remapEAPMethod(EAP.EAPMethodID eapMethodID) throws IOException { 229 switch (eapMethodID) { 230 case EAP_TTLS: 231 return WifiEnterpriseConfig.Eap.TTLS; 232 case EAP_TLS: 233 return WifiEnterpriseConfig.Eap.TLS; 234 case EAP_SIM: 235 return WifiEnterpriseConfig.Eap.SIM; 236 case EAP_AKA: 237 return WifiEnterpriseConfig.Eap.AKA; 238 case EAP_AKAPrim: 239 return WifiEnterpriseConfig.Eap.AKA_PRIME; 240 default: 241 throw new IOException("Bad EAP method: " + eapMethodID); 242 } 243 } 244 245 private static int remapInnerMethod(NonEAPInnerAuth.NonEAPType type) throws IOException { 246 switch (type) { 247 case PAP: 248 return WifiEnterpriseConfig.Phase2.PAP; 249 case MSCHAP: 250 return WifiEnterpriseConfig.Phase2.MSCHAP; 251 case MSCHAPv2: 252 return WifiEnterpriseConfig.Phase2.MSCHAPV2; 253 case CHAP: 254 default: 255 throw new IOException("Inner method " + type + " not supported"); 256 } 257 } 258 } 259