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