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 17 package com.android.server.wifi.hotspot2; 18 19 import android.net.wifi.EAPConstants; 20 import android.net.wifi.WifiConfiguration; 21 import android.net.wifi.WifiEnterpriseConfig; 22 import android.net.wifi.hotspot2.PasspointConfiguration; 23 import android.net.wifi.hotspot2.pps.Credential; 24 import android.net.wifi.hotspot2.pps.Credential.SimCredential; 25 import android.net.wifi.hotspot2.pps.Credential.UserCredential; 26 import android.net.wifi.hotspot2.pps.HomeSp; 27 import android.security.Credentials; 28 import android.text.TextUtils; 29 import android.util.Base64; 30 import android.util.Log; 31 32 import com.android.server.wifi.IMSIParameter; 33 import com.android.server.wifi.SIMAccessor; 34 import com.android.server.wifi.WifiKeyStore; 35 import com.android.server.wifi.hotspot2.anqp.ANQPElement; 36 import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType; 37 import com.android.server.wifi.hotspot2.anqp.DomainNameElement; 38 import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; 39 import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement; 40 import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement; 41 import com.android.server.wifi.hotspot2.anqp.eap.AuthParam; 42 import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth; 43 44 import java.nio.charset.StandardCharsets; 45 import java.security.MessageDigest; 46 import java.security.NoSuchAlgorithmException; 47 import java.security.cert.CertificateEncodingException; 48 import java.security.cert.X509Certificate; 49 import java.util.Arrays; 50 import java.util.List; 51 import java.util.Map; 52 import 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 */ 58 public 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 enterpriseConfig.setDomainSuffixMatch(mConfig.getHomeSp().getFqdn()); 290 if (mConfig.getCredential().getUserCredential() != null) { 291 buildEnterpriseConfigForUserCredential(enterpriseConfig, 292 mConfig.getCredential().getUserCredential()); 293 setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); 294 } else if (mConfig.getCredential().getCertCredential() != null) { 295 buildEnterpriseConfigForCertCredential(enterpriseConfig); 296 setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm()); 297 } else { 298 buildEnterpriseConfigForSimCredential(enterpriseConfig, 299 mConfig.getCredential().getSimCredential()); 300 } 301 wifiConfig.enterpriseConfig = enterpriseConfig; 302 return wifiConfig; 303 } 304 305 /** 306 * Convert a legacy {@link WifiConfiguration} representation of a Passpoint configuration to 307 * a {@link PasspointConfiguration}. This is used for migrating legacy Passpoint 308 * configuration (release N and older). 309 * 310 * @param wifiConfig The {@link WifiConfiguration} to convert 311 * @return {@link PasspointConfiguration} 312 */ 313 public static PasspointConfiguration convertFromWifiConfig(WifiConfiguration wifiConfig) { 314 PasspointConfiguration passpointConfig = new PasspointConfiguration(); 315 316 // Setup HomeSP. 317 HomeSp homeSp = new HomeSp(); 318 if (TextUtils.isEmpty(wifiConfig.FQDN)) { 319 Log.e(TAG, "Missing FQDN"); 320 return null; 321 } 322 homeSp.setFqdn(wifiConfig.FQDN); 323 homeSp.setFriendlyName(wifiConfig.providerFriendlyName); 324 if (wifiConfig.roamingConsortiumIds != null) { 325 homeSp.setRoamingConsortiumOis(Arrays.copyOf( 326 wifiConfig.roamingConsortiumIds, wifiConfig.roamingConsortiumIds.length)); 327 } 328 passpointConfig.setHomeSp(homeSp); 329 330 // Setup Credential. 331 Credential credential = new Credential(); 332 credential.setRealm(wifiConfig.enterpriseConfig.getRealm()); 333 switch (wifiConfig.enterpriseConfig.getEapMethod()) { 334 case WifiEnterpriseConfig.Eap.TTLS: 335 credential.setUserCredential(buildUserCredentialFromEnterpriseConfig( 336 wifiConfig.enterpriseConfig)); 337 break; 338 case WifiEnterpriseConfig.Eap.TLS: 339 Credential.CertificateCredential certCred = new Credential.CertificateCredential(); 340 certCred.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3); 341 credential.setCertCredential(certCred); 342 break; 343 case WifiEnterpriseConfig.Eap.SIM: 344 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 345 EAPConstants.EAP_SIM, wifiConfig.enterpriseConfig)); 346 break; 347 case WifiEnterpriseConfig.Eap.AKA: 348 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 349 EAPConstants.EAP_AKA, wifiConfig.enterpriseConfig)); 350 break; 351 case WifiEnterpriseConfig.Eap.AKA_PRIME: 352 credential.setSimCredential(buildSimCredentialFromEnterpriseConfig( 353 EAPConstants.EAP_AKA_PRIME, wifiConfig.enterpriseConfig)); 354 break; 355 default: 356 Log.e(TAG, "Unsupport EAP method: " + wifiConfig.enterpriseConfig.getEapMethod()); 357 return null; 358 } 359 if (credential.getUserCredential() == null && credential.getCertCredential() == null 360 && credential.getSimCredential() == null) { 361 Log.e(TAG, "Missing credential"); 362 return null; 363 } 364 passpointConfig.setCredential(credential); 365 366 return passpointConfig; 367 } 368 369 @Override 370 public boolean equals(Object thatObject) { 371 if (this == thatObject) { 372 return true; 373 } 374 if (!(thatObject instanceof PasspointProvider)) { 375 return false; 376 } 377 PasspointProvider that = (PasspointProvider) thatObject; 378 return mProviderId == that.mProviderId 379 && TextUtils.equals(mCaCertificateAlias, that.mCaCertificateAlias) 380 && TextUtils.equals(mClientCertificateAlias, that.mClientCertificateAlias) 381 && TextUtils.equals(mClientPrivateKeyAlias, that.mClientPrivateKeyAlias) 382 && (mConfig == null ? that.mConfig == null : mConfig.equals(that.mConfig)); 383 } 384 385 @Override 386 public int hashCode() { 387 return Objects.hash(mProviderId, mCaCertificateAlias, mClientCertificateAlias, 388 mClientPrivateKeyAlias, mConfig); 389 } 390 391 @Override 392 public String toString() { 393 StringBuilder builder = new StringBuilder(); 394 builder.append("ProviderId: ").append(mProviderId).append("\n"); 395 builder.append("CreatorUID: ").append(mCreatorUid).append("\n"); 396 builder.append("Configuration Begin ---\n"); 397 builder.append(mConfig); 398 builder.append("Configuration End ---\n"); 399 return builder.toString(); 400 } 401 402 /** 403 * Retrieve the client certificate from the certificates chain. The certificate 404 * with the matching SHA256 digest is the client certificate. 405 * 406 * @param certChain The client certificates chain 407 * @param expectedSha256Fingerprint The expected SHA256 digest of the client certificate 408 * @return {@link java.security.cert.X509Certificate} 409 */ 410 private static X509Certificate getClientCertificate(X509Certificate[] certChain, 411 byte[] expectedSha256Fingerprint) { 412 if (certChain == null) { 413 return null; 414 } 415 try { 416 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 417 for (X509Certificate certificate : certChain) { 418 digester.reset(); 419 byte[] fingerprint = digester.digest(certificate.getEncoded()); 420 if (Arrays.equals(expectedSha256Fingerprint, fingerprint)) { 421 return certificate; 422 } 423 } 424 } catch (CertificateEncodingException | NoSuchAlgorithmException e) { 425 return null; 426 } 427 428 return null; 429 } 430 431 /** 432 * Perform a provider match based on the given ANQP elements. 433 * 434 * @param anqpElements List of ANQP elements 435 * @return {@link PasspointMatch} 436 */ 437 private PasspointMatch matchProvider(Map<ANQPElementType, ANQPElement> anqpElements) { 438 // Domain name matching. 439 if (ANQPMatcher.matchDomainName( 440 (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), 441 mConfig.getHomeSp().getFqdn(), mImsiParameter, mMatchingSIMImsiList)) { 442 return PasspointMatch.HomeProvider; 443 } 444 445 // Roaming Consortium OI matching. 446 if (ANQPMatcher.matchRoamingConsortium( 447 (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium), 448 mConfig.getHomeSp().getRoamingConsortiumOis())) { 449 return PasspointMatch.RoamingProvider; 450 } 451 452 // 3GPP Network matching. 453 if (ANQPMatcher.matchThreeGPPNetwork( 454 (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork), 455 mImsiParameter, mMatchingSIMImsiList)) { 456 return PasspointMatch.RoamingProvider; 457 } 458 return PasspointMatch.None; 459 } 460 461 /** 462 * Fill in WifiEnterpriseConfig with information from an user credential. 463 * 464 * @param config Instance of {@link WifiEnterpriseConfig} 465 * @param credential Instance of {@link UserCredential} 466 */ 467 private void buildEnterpriseConfigForUserCredential(WifiEnterpriseConfig config, 468 Credential.UserCredential credential) { 469 byte[] pwOctets = Base64.decode(credential.getPassword(), Base64.DEFAULT); 470 String decodedPassword = new String(pwOctets, StandardCharsets.UTF_8); 471 config.setEapMethod(WifiEnterpriseConfig.Eap.TTLS); 472 config.setIdentity(credential.getUsername()); 473 config.setPassword(decodedPassword); 474 config.setCaCertificateAlias(mCaCertificateAlias); 475 int phase2Method = WifiEnterpriseConfig.Phase2.NONE; 476 switch (credential.getNonEapInnerMethod()) { 477 case Credential.UserCredential.AUTH_METHOD_PAP: 478 phase2Method = WifiEnterpriseConfig.Phase2.PAP; 479 break; 480 case Credential.UserCredential.AUTH_METHOD_MSCHAP: 481 phase2Method = WifiEnterpriseConfig.Phase2.MSCHAP; 482 break; 483 case Credential.UserCredential.AUTH_METHOD_MSCHAPV2: 484 phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2; 485 break; 486 default: 487 // Should never happen since this is already validated when the provider is 488 // added. 489 Log.wtf(TAG, "Unsupported Auth: " + credential.getNonEapInnerMethod()); 490 break; 491 } 492 config.setPhase2Method(phase2Method); 493 } 494 495 /** 496 * Fill in WifiEnterpriseConfig with information from a certificate credential. 497 * 498 * @param config Instance of {@link WifiEnterpriseConfig} 499 */ 500 private void buildEnterpriseConfigForCertCredential(WifiEnterpriseConfig config) { 501 config.setEapMethod(WifiEnterpriseConfig.Eap.TLS); 502 config.setClientCertificateAlias(mClientCertificateAlias); 503 config.setCaCertificateAlias(mCaCertificateAlias); 504 } 505 506 /** 507 * Fill in WifiEnterpriseConfig with information from a SIM credential. 508 * 509 * @param config Instance of {@link WifiEnterpriseConfig} 510 * @param credential Instance of {@link SimCredential} 511 */ 512 private void buildEnterpriseConfigForSimCredential(WifiEnterpriseConfig config, 513 Credential.SimCredential credential) { 514 int eapMethod = WifiEnterpriseConfig.Eap.NONE; 515 switch(credential.getEapType()) { 516 case EAPConstants.EAP_SIM: 517 eapMethod = WifiEnterpriseConfig.Eap.SIM; 518 break; 519 case EAPConstants.EAP_AKA: 520 eapMethod = WifiEnterpriseConfig.Eap.AKA; 521 break; 522 case EAPConstants.EAP_AKA_PRIME: 523 eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME; 524 break; 525 default: 526 // Should never happen since this is already validated when the provider is 527 // added. 528 Log.wtf(TAG, "Unsupported EAP Method: " + credential.getEapType()); 529 break; 530 } 531 config.setEapMethod(eapMethod); 532 config.setPlmn(credential.getImsi()); 533 } 534 535 private static void setAnonymousIdentityToNaiRealm(WifiEnterpriseConfig config, String realm) { 536 /** 537 * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so 538 * that this value will be sent to the EAP server as part of the EAP-Response/ Identity 539 * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity 540 * packet, and revert to using the (real) identity field for subsequent transactions that 541 * request an identity (e.g. in EAP-TTLS). 542 * 543 * This NAI realm value (the portion of the identity after the '@') is used to tell the 544 * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a 545 * placeholder that is not used--it is set to this value by convention. See Section 5.1 of 546 * RFC3748 for more details. 547 * 548 * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the 549 * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to 550 * identify the device. 551 */ 552 config.setAnonymousIdentity("anonymous@" + realm); 553 } 554 555 /** 556 * Helper function for creating a 557 * {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from the given 558 * {@link WifiEnterpriseConfig} 559 * 560 * @param config The enterprise configuration containing the credential 561 * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} 562 */ 563 private static Credential.UserCredential buildUserCredentialFromEnterpriseConfig( 564 WifiEnterpriseConfig config) { 565 Credential.UserCredential userCredential = new Credential.UserCredential(); 566 userCredential.setEapType(EAPConstants.EAP_TTLS); 567 568 if (TextUtils.isEmpty(config.getIdentity())) { 569 Log.e(TAG, "Missing username for user credential"); 570 return null; 571 } 572 userCredential.setUsername(config.getIdentity()); 573 574 if (TextUtils.isEmpty(config.getPassword())) { 575 Log.e(TAG, "Missing password for user credential"); 576 return null; 577 } 578 String encodedPassword = 579 new String(Base64.encode(config.getPassword().getBytes(StandardCharsets.UTF_8), 580 Base64.DEFAULT), StandardCharsets.UTF_8); 581 userCredential.setPassword(encodedPassword); 582 583 switch(config.getPhase2Method()) { 584 case WifiEnterpriseConfig.Phase2.PAP: 585 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_PAP); 586 break; 587 case WifiEnterpriseConfig.Phase2.MSCHAP: 588 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP); 589 break; 590 case WifiEnterpriseConfig.Phase2.MSCHAPV2: 591 userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2); 592 break; 593 default: 594 Log.e(TAG, "Unsupported phase2 method for TTLS: " + config.getPhase2Method()); 595 return null; 596 } 597 return userCredential; 598 } 599 600 /** 601 * Helper function for creating a 602 * {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from the given 603 * {@link WifiEnterpriseConfig} 604 * 605 * @param eapType The EAP type of the SIM credential 606 * @param config The enterprise configuration containing the credential 607 * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} 608 */ 609 private static Credential.SimCredential buildSimCredentialFromEnterpriseConfig( 610 int eapType, WifiEnterpriseConfig config) { 611 Credential.SimCredential simCredential = new Credential.SimCredential(); 612 if (TextUtils.isEmpty(config.getPlmn())) { 613 Log.e(TAG, "Missing IMSI for SIM credential"); 614 return null; 615 } 616 simCredential.setImsi(config.getPlmn()); 617 simCredential.setEapType(eapType); 618 return simCredential; 619 } 620 } 621