PasspointProvider.java revision 2aa7cc7c13207c3afa3ef428ec1c8cdc1893c50c
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi.hotspot2; 18 19import android.net.wifi.EAPConstants; 20import android.net.wifi.WifiConfiguration; 21import android.net.wifi.WifiEnterpriseConfig; 22import android.net.wifi.hotspot2.PasspointConfiguration; 23import android.net.wifi.hotspot2.pps.Credential; 24import android.net.wifi.hotspot2.pps.Credential.SimCredential; 25import android.net.wifi.hotspot2.pps.Credential.UserCredential; 26import android.net.wifi.hotspot2.pps.HomeSp; 27import android.security.Credentials; 28import android.text.TextUtils; 29import android.util.Base64; 30import android.util.Log; 31 32import com.android.server.wifi.IMSIParameter; 33import com.android.server.wifi.SIMAccessor; 34import com.android.server.wifi.WifiKeyStore; 35import com.android.server.wifi.hotspot2.anqp.ANQPElement; 36import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; 37import com.android.server.wifi.hotspot2.anqp.DomainNameElement; 38import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; 39import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement; 40import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement; 41import com.android.server.wifi.hotspot2.anqp.eap.AuthParam; 42import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth; 43 44import java.nio.charset.StandardCharsets; 45import java.security.MessageDigest; 46import java.security.NoSuchAlgorithmException; 47import java.security.cert.CertificateEncodingException; 48import java.security.cert.X509Certificate; 49import java.util.Arrays; 50import java.util.List; 51import java.util.Map; 52import java.util.Objects; 53 54/** 55 * Abstraction for Passpoint service provider. This class contains the both static 56 * Passpoint configuration data and the runtime data (e.g. blacklisted SSIDs, statistics). 57 */ 58public class PasspointProvider { 59 private static final String TAG = "PasspointProvider"; 60 61 /** 62 * Used as part of alias string for certificates and keys. The alias string is in the format 63 * of: [KEY_TYPE]_HS2_[ProviderID] 64 * For example: "CACERT_HS2_0", "USRCERT_HS2_0", "USRPKEY_HS2_0" 65 */ 66 private static final String ALIAS_HS_TYPE = "HS2_"; 67 68 private final PasspointConfiguration mConfig; 69 private final WifiKeyStore mKeyStore; 70 71 /** 72 * Aliases for the private keys and certificates installed in the keystore. Each alias 73 * is a suffix of the actual certificate or key name installed in the keystore. The 74 * certificate or key name in the keystore is consist of |Type|_|alias|. 75 * This will be consistent with the usage of the term "alias" in {@link WifiEnterpriseConfig}. 76 */ 77 private String mCaCertificateAlias; 78 private String mClientPrivateKeyAlias; 79 private String mClientCertificateAlias; 80 81 private final long mProviderId; 82 83 private final IMSIParameter mImsiParameter; 84 private final List<String> mMatchingSIMImsiList; 85 86 private final int mEAPMethodID; 87 private final AuthParam mAuthParam; 88 89 public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, 90 SIMAccessor simAccessor, long providerId) { 91 this(config, keyStore, simAccessor, providerId, null, null, null); 92 } 93 94 public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore, 95 SIMAccessor simAccessor, long providerId, String caCertificateAlias, 96 String clientCertificateAlias, String clientPrivateKeyAlias) { 97 // Maintain a copy of the configuration to avoid it being updated by others. 98 mConfig = new PasspointConfiguration(config); 99 mKeyStore = keyStore; 100 mProviderId = providerId; 101 mCaCertificateAlias = caCertificateAlias; 102 mClientCertificateAlias = clientCertificateAlias; 103 mClientPrivateKeyAlias = clientPrivateKeyAlias; 104 105 // Setup EAP method and authentication parameter based on the credential. 106 if (mConfig.getCredential().getUserCredential() != null) { 107 mEAPMethodID = EAPConstants.EAP_TTLS; 108 mAuthParam = new NonEAPInnerAuth(NonEAPInnerAuth.getAuthTypeID( 109 mConfig.getCredential().getUserCredential().getNonEapInnerMethod())); 110 mImsiParameter = null; 111 mMatchingSIMImsiList = null; 112 } else if (mConfig.getCredential().getCertCredential() != null) { 113 mEAPMethodID = EAPConstants.EAP_TLS; 114 mAuthParam = null; 115 mImsiParameter = null; 116 mMatchingSIMImsiList = null; 117 } else { 118 mEAPMethodID = mConfig.getCredential().getSimCredential().getEapType(); 119 mAuthParam = null; 120 mImsiParameter = IMSIParameter.build( 121 mConfig.getCredential().getSimCredential().getImsi()); 122 mMatchingSIMImsiList = simAccessor.getMatchingImsis(mImsiParameter); 123 } 124 } 125 126 public PasspointConfiguration getConfig() { 127 // Return a copy of the configuration to avoid it being updated by others. 128 return new PasspointConfiguration(mConfig); 129 } 130 131 public String getCaCertificateAlias() { 132 return mCaCertificateAlias; 133 } 134 135 public String getClientPrivateKeyAlias() { 136 return mClientPrivateKeyAlias; 137 } 138 139 public String getClientCertificateAlias() { 140 return mClientCertificateAlias; 141 } 142 143 public long getProviderId() { 144 return mProviderId; 145 } 146 147 /** 148 * Install certificates and key based on current configuration. 149 * Note: the certificates and keys in the configuration will get cleared once 150 * they're installed in the keystore. 151 * 152 * @return true on success 153 */ 154 public boolean installCertsAndKeys() { 155 // Install CA certificate. 156 if (mConfig.getCredential().getCaCertificate() != null) { 157 String certName = Credentials.CA_CERTIFICATE + ALIAS_HS_TYPE + mProviderId; 158 if (!mKeyStore.putCertInKeyStore(certName, 159 mConfig.getCredential().getCaCertificate())) { 160 Log.e(TAG, "Failed to install CA Certificate"); 161 uninstallCertsAndKeys(); 162 return false; 163 } 164 mCaCertificateAlias = ALIAS_HS_TYPE + mProviderId; 165 } 166 167 // Install the client private key. 168 if (mConfig.getCredential().getClientPrivateKey() != null) { 169 String keyName = Credentials.USER_PRIVATE_KEY + ALIAS_HS_TYPE + mProviderId; 170 if (!mKeyStore.putKeyInKeyStore(keyName, 171 mConfig.getCredential().getClientPrivateKey())) { 172 Log.e(TAG, "Failed to install client private key"); 173 uninstallCertsAndKeys(); 174 return false; 175 } 176 mClientPrivateKeyAlias = ALIAS_HS_TYPE + mProviderId; 177 } 178 179 // Install the client certificate. 180 if (mConfig.getCredential().getClientCertificateChain() != null) { 181 X509Certificate clientCert = getClientCertificate( 182 mConfig.getCredential().getClientCertificateChain(), 183 mConfig.getCredential().getCertCredential().getCertSha256Fingerprint()); 184 if (clientCert == null) { 185 Log.e(TAG, "Failed to locate client certificate"); 186 uninstallCertsAndKeys(); 187 return false; 188 } 189 String certName = Credentials.USER_CERTIFICATE + ALIAS_HS_TYPE + mProviderId; 190 if (!mKeyStore.putCertInKeyStore(certName, clientCert)) { 191 Log.e(TAG, "Failed to install client certificate"); 192 uninstallCertsAndKeys(); 193 return false; 194 } 195 mClientCertificateAlias = ALIAS_HS_TYPE + mProviderId; 196 } 197 198 // Clear the keys and certificates in the configuration. 199 mConfig.getCredential().setCaCertificate(null); 200 mConfig.getCredential().setClientPrivateKey(null); 201 mConfig.getCredential().setClientCertificateChain(null); 202 return true; 203 } 204 205 /** 206 * Remove any installed certificates and key. 207 */ 208 public void uninstallCertsAndKeys() { 209 if (mCaCertificateAlias != null) { 210 if (!mKeyStore.removeEntryFromKeyStore( 211 Credentials.CA_CERTIFICATE + mCaCertificateAlias)) { 212 Log.e(TAG, "Failed to remove entry: " + mCaCertificateAlias); 213 } 214 mCaCertificateAlias = null; 215 } 216 if (mClientPrivateKeyAlias != null) { 217 if (!mKeyStore.removeEntryFromKeyStore( 218 Credentials.USER_PRIVATE_KEY + mClientPrivateKeyAlias)) { 219 Log.e(TAG, "Failed to remove entry: " + mClientPrivateKeyAlias); 220 } 221 mClientPrivateKeyAlias = null; 222 } 223 if (mClientCertificateAlias != null) { 224 if (!mKeyStore.removeEntryFromKeyStore( 225 Credentials.USER_CERTIFICATE + mClientCertificateAlias)) { 226 Log.e(TAG, "Failed to remove entry: " + mClientCertificateAlias); 227 } 228 mClientCertificateAlias = null; 229 } 230 } 231 232 /** 233 * Return the matching status with the given AP, based on the ANQP elements from the AP. 234 * 235 * @param anqpElements ANQP elements from the AP 236 * @return {@link PasspointMatch} 237 */ 238 public PasspointMatch match(Map<ANQPElementType, ANQPElement> anqpElements) { 239 PasspointMatch providerMatch = matchProvider(anqpElements); 240 241 // Perform authentication match against the NAI Realm. 242 int authMatch = ANQPMatcher.matchNAIRealm( 243 (NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm), 244 mConfig.getCredential().getRealm(), mEAPMethodID, mAuthParam); 245 246 // Auth mismatch, demote provider match. 247 if (authMatch == AuthMatch.NONE) { 248 return PasspointMatch.None; 249 } 250 251 // No realm match, return provider match as is. 252 if ((authMatch & AuthMatch.REALM) == 0) { 253 return providerMatch; 254 } 255 256 // Realm match, promote provider match to roaming if no other provider match is found. 257 return providerMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider 258 : providerMatch; 259 } 260 261 /** 262 * Generate a WifiConfiguration based on the provider's configuration. The generated 263 * WifiConfiguration will include all the necessary credentials for network connection except 264 * the SSID, which should be added by the caller when the config is being used for network 265 * connection. 266 * 267 * @return {@link WifiConfiguration} 268 */ 269 public WifiConfiguration getWifiConfig() { 270 WifiConfiguration wifiConfig = new WifiConfiguration(); 271 wifiConfig.FQDN = mConfig.getHomeSp().getFqdn(); 272 if (mConfig.getHomeSp().getRoamingConsortiumOis() != null) { 273 wifiConfig.roamingConsortiumIds = Arrays.copyOf( 274 mConfig.getHomeSp().getRoamingConsortiumOis(), 275 mConfig.getHomeSp().getRoamingConsortiumOis().length); 276 } 277 wifiConfig.providerFriendlyName = mConfig.getHomeSp().getFriendlyName(); 278 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); 279 wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); 280 281 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 282 enterpriseConfig.setRealm(mConfig.getCredential().getRealm()); 283 enterpriseConfig.setDomainSuffixMatch(mConfig.getHomeSp().getFqdn()); 284 if (mConfig.getCredential().getUserCredential() != null) { 285 buildEnterpriseConfigForUserCredential(enterpriseConfig, 286 mConfig.getCredential().getUserCredential()); 287 setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); 288 } else if (mConfig.getCredential().getCertCredential() != null) { 289 buildEnterpriseConfigForCertCredential(enterpriseConfig); 290 setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); 291 } else { 292 buildEnterpriseConfigForSimCredential(enterpriseConfig, 293 mConfig.getCredential().getSimCredential()); 294 } 295 wifiConfig.enterpriseConfig = enterpriseConfig; 296 return wifiConfig; 297 } 298 299 /** 300 * Convert a legacy {@link WifiConfiguration} representation of a Passpoint configuration to 301 * a {@link PasspointConfiguration}. This is used for migrating legacy Passpoint 302 * configuration (release N and older). 303 * 304 * @param wifiConfig The {@link WifiConfiguration} to convert 305 * @return {@link PasspointConfiguration} 306 */ 307 public static PasspointConfiguration convertFromWifiConfig(WifiConfiguration wifiConfig) { 308 PasspointConfiguration passpointConfig = new PasspointConfiguration(); 309 310 // Setup HomeSP. 311 HomeSp homeSp = new HomeSp(); 312 if (TextUtils.isEmpty(wifiConfig.FQDN)) { 313 Log.e(TAG, "Missing FQDN"); 314 return null; 315 } 316 homeSp.setFqdn(wifiConfig.FQDN); 317 homeSp.setFriendlyName(wifiConfig.providerFriendlyName); 318 if (wifiConfig.roamingConsortiumIds != null) { 319 homeSp.setRoamingConsortiumOis(Arrays.copyOf( 320 wifiConfig.roamingConsortiumIds, wifiConfig.roamingConsortiumIds.length)); 321 } 322 passpointConfig.setHomeSp(homeSp); 323 324 // Setup Credential. 325 Credential credential = new Credential(); 326 credential.setRealm(wifiConfig.enterpriseConfig.getRealm()); 327 switch (wifiConfig.enterpriseConfig.getEapMethod()) { 328 case WifiEnterpriseConfig.Eap.TTLS: 329 credential.setUserCredential(buildUserCredentialFromEnterpriseConfig( 330 wifiConfig.enterpriseConfig)); 331 break; 332 case WifiEnterpriseConfig.Eap.TLS: 333 Credential.CertificateCredential certCred = new Credential.CertificateCredential(); 334 certCred.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3); 335 credential.setCertCredential(certCred); 336 break; 337 case WifiEnterpriseConfig.Eap.SIM: 338 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 339 EAPConstants.EAP_SIM, wifiConfig.enterpriseConfig)); 340 break; 341 case WifiEnterpriseConfig.Eap.AKA: 342 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 343 EAPConstants.EAP_AKA, wifiConfig.enterpriseConfig)); 344 break; 345 case WifiEnterpriseConfig.Eap.AKA_PRIME: 346 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 347 EAPConstants.EAP_AKA_PRIME, wifiConfig.enterpriseConfig)); 348 break; 349 default: 350 Log.e(TAG, "Unsupport EAP method: " + wifiConfig.enterpriseConfig.getEapMethod()); 351 return null; 352 } 353 if (credential.getUserCredential() == null && credential.getCertCredential() == null 354 && credential.getSimCredential() == null) { 355 Log.e(TAG, "Missing credential"); 356 return null; 357 } 358 passpointConfig.setCredential(credential); 359 360 return passpointConfig; 361 } 362 363 @Override 364 public boolean equals(Object thatObject) { 365 if (this == thatObject) { 366 return true; 367 } 368 if (!(thatObject instanceof PasspointProvider)) { 369 return false; 370 } 371 PasspointProvider that = (PasspointProvider) thatObject; 372 return mProviderId == that.mProviderId 373 && TextUtils.equals(mCaCertificateAlias, that.mCaCertificateAlias) 374 && TextUtils.equals(mClientCertificateAlias, that.mClientCertificateAlias) 375 && TextUtils.equals(mClientPrivateKeyAlias, that.mClientPrivateKeyAlias) 376 && (mConfig == null ? that.mConfig == null : mConfig.equals(that.mConfig)); 377 } 378 379 @Override 380 public int hashCode() { 381 return Objects.hash(mProviderId, mCaCertificateAlias, mClientCertificateAlias, 382 mClientPrivateKeyAlias, mConfig); 383 } 384 385 @Override 386 public String toString() { 387 StringBuilder builder = new StringBuilder(); 388 builder.append("ProviderId: ").append(mProviderId).append("\n"); 389 builder.append("Configuration Begin ---\n"); 390 builder.append(mConfig); 391 builder.append("Configuration End ---\n"); 392 return builder.toString(); 393 } 394 395 /** 396 * Retrieve the client certificate from the certificates chain. The certificate 397 * with the matching SHA256 digest is the client certificate. 398 * 399 * @param certChain The client certificates chain 400 * @param expectedSha256Fingerprint The expected SHA256 digest of the client certificate 401 * @return {@link java.security.cert.X509Certificate} 402 */ 403 private static X509Certificate getClientCertificate(X509Certificate[] certChain, 404 byte[] expectedSha256Fingerprint) { 405 if (certChain == null) { 406 return null; 407 } 408 try { 409 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 410 for (X509Certificate certificate : certChain) { 411 digester.reset(); 412 byte[] fingerprint = digester.digest(certificate.getEncoded()); 413 if (Arrays.equals(expectedSha256Fingerprint, fingerprint)) { 414 return certificate; 415 } 416 } 417 } catch (CertificateEncodingException | NoSuchAlgorithmException e) { 418 return null; 419 } 420 421 return null; 422 } 423 424 /** 425 * Perform a provider match based on the given ANQP elements. 426 * 427 * @param anqpElements List of ANQP elements 428 * @return {@link PasspointMatch} 429 */ 430 private PasspointMatch matchProvider(Map<ANQPElementType, ANQPElement> anqpElements) { 431 // Domain name matching. 432 if (ANQPMatcher.matchDomainName( 433 (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), 434 mConfig.getHomeSp().getFqdn(), mImsiParameter, mMatchingSIMImsiList)) { 435 return PasspointMatch.HomeProvider; 436 } 437 438 // Roaming Consortium OI matching. 439 if (ANQPMatcher.matchRoamingConsortium( 440 (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium), 441 mConfig.getHomeSp().getRoamingConsortiumOis())) { 442 return PasspointMatch.RoamingProvider; 443 } 444 445 // 3GPP Network matching. 446 if (ANQPMatcher.matchThreeGPPNetwork( 447 (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork), 448 mImsiParameter, mMatchingSIMImsiList)) { 449 return PasspointMatch.RoamingProvider; 450 } 451 return PasspointMatch.None; 452 } 453 454 /** 455 * Fill in WifiEnterpriseConfig with information from an user credential. 456 * 457 * @param config Instance of {@link WifiEnterpriseConfig} 458 * @param credential Instance of {@link UserCredential} 459 */ 460 private void buildEnterpriseConfigForUserCredential(WifiEnterpriseConfig config, 461 Credential.UserCredential credential) { 462 byte[] pwOctets = Base64.decode(credential.getPassword(), Base64.DEFAULT); 463 String decodedPassword = new String(pwOctets, StandardCharsets.UTF_8); 464 config.setEapMethod(WifiEnterpriseConfig.Eap.TTLS); 465 config.setIdentity(credential.getUsername()); 466 config.setPassword(decodedPassword); 467 config.setCaCertificateAlias(mCaCertificateAlias); 468 int phase2Method = WifiEnterpriseConfig.Phase2.NONE; 469 switch (credential.getNonEapInnerMethod()) { 470 case Credential.UserCredential.AUTH_METHOD_PAP: 471 phase2Method = WifiEnterpriseConfig.Phase2.PAP; 472 break; 473 case Credential.UserCredential.AUTH_METHOD_MSCHAP: 474 phase2Method = WifiEnterpriseConfig.Phase2.MSCHAP; 475 break; 476 case Credential.UserCredential.AUTH_METHOD_MSCHAPV2: 477 phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2; 478 break; 479 default: 480 // Should never happen since this is already validated when the provider is 481 // added. 482 Log.wtf(TAG, "Unsupported Auth: " + credential.getNonEapInnerMethod()); 483 break; 484 } 485 config.setPhase2Method(phase2Method); 486 } 487 488 /** 489 * Fill in WifiEnterpriseConfig with information from a certificate credential. 490 * 491 * @param config Instance of {@link WifiEnterpriseConfig} 492 */ 493 private void buildEnterpriseConfigForCertCredential(WifiEnterpriseConfig config) { 494 config.setEapMethod(WifiEnterpriseConfig.Eap.TLS); 495 config.setClientCertificateAlias(mClientCertificateAlias); 496 config.setCaCertificateAlias(mCaCertificateAlias); 497 } 498 499 /** 500 * Fill in WifiEnterpriseConfig with information from a SIM credential. 501 * 502 * @param config Instance of {@link WifiEnterpriseConfig} 503 * @param credential Instance of {@link SimCredential} 504 */ 505 private void buildEnterpriseConfigForSimCredential(WifiEnterpriseConfig config, 506 Credential.SimCredential credential) { 507 int eapMethod = WifiEnterpriseConfig.Eap.NONE; 508 switch(credential.getEapType()) { 509 case EAPConstants.EAP_SIM: 510 eapMethod = WifiEnterpriseConfig.Eap.SIM; 511 break; 512 case EAPConstants.EAP_AKA: 513 eapMethod = WifiEnterpriseConfig.Eap.AKA; 514 break; 515 case EAPConstants.EAP_AKA_PRIME: 516 eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME; 517 break; 518 default: 519 // Should never happen since this is already validated when the provider is 520 // added. 521 Log.wtf(TAG, "Unsupported EAP Method: " + credential.getEapType()); 522 break; 523 } 524 config.setEapMethod(eapMethod); 525 config.setPlmn(credential.getImsi()); 526 } 527 528 private static void setAnonymousIdentityToNaiRealm(WifiEnterpriseConfig config, String realm) { 529 /** 530 * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so 531 * that this value will be sent to the EAP server as part of the EAP-Response/ Identity 532 * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity 533 * packet, and revert to using the (real) identity field for subsequent transactions that 534 * request an identity (e.g. in EAP-TTLS). 535 * 536 * This NAI realm value (the portion of the identity after the '@') is used to tell the 537 * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a 538 * placeholder that is not used--it is set to this value by convention. See Section 5.1 of 539 * RFC3748 for more details. 540 * 541 * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the 542 * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to 543 * identify the device. 544 */ 545 config.setAnonymousIdentity("anonymous@" + realm); 546 } 547 548 /** 549 * Helper function for creating a 550 * {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from the given 551 * {@link WifiEnterpriseConfig} 552 * 553 * @param config The enterprise configuration containing the credential 554 * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} 555 */ 556 private static Credential.UserCredential buildUserCredentialFromEnterpriseConfig( 557 WifiEnterpriseConfig config) { 558 Credential.UserCredential userCredential = new Credential.UserCredential(); 559 userCredential.setEapType(EAPConstants.EAP_TTLS); 560 561 if (TextUtils.isEmpty(config.getIdentity())) { 562 Log.e(TAG, "Missing username for user credential"); 563 return null; 564 } 565 userCredential.setUsername(config.getIdentity()); 566 567 if (TextUtils.isEmpty(config.getPassword())) { 568 Log.e(TAG, "Missing password for user credential"); 569 return null; 570 } 571 String encodedPassword = 572 new String(Base64.encode(config.getPassword().getBytes(StandardCharsets.UTF_8), 573 Base64.DEFAULT), StandardCharsets.UTF_8); 574 userCredential.setPassword(encodedPassword); 575 576 switch(config.getPhase2Method()) { 577 case WifiEnterpriseConfig.Phase2.PAP: 578 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_PAP); 579 break; 580 case WifiEnterpriseConfig.Phase2.MSCHAP: 581 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP); 582 break; 583 case WifiEnterpriseConfig.Phase2.MSCHAPV2: 584 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2); 585 break; 586 default: 587 Log.e(TAG, "Unsupported phase2 method for TTLS: " + config.getPhase2Method()); 588 return null; 589 } 590 return userCredential; 591 } 592 593 /** 594 * Helper function for creating a 595 * {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from the given 596 * {@link WifiEnterpriseConfig} 597 * 598 * @param eapType The EAP type of the SIM credential 599 * @param config The enterprise configuration containing the credential 600 * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} 601 */ 602 private static Credential.SimCredential buildSimCredentialFromEnterpriseConfig( 603 int eapType, WifiEnterpriseConfig config) { 604 Credential.SimCredential simCredential = new Credential.SimCredential(); 605 if (TextUtils.isEmpty(config.getPlmn())) { 606 Log.e(TAG, "Missing IMSI for SIM credential"); 607 return null; 608 } 609 simCredential.setImsi(config.getPlmn()); 610 simCredential.setEapType(eapType); 611 return simCredential; 612 } 613} 614