1 /* 2 * Copyright (C) 2013 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 package android.net.wifi; 17 18 import android.annotation.IntDef; 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.nio.charset.StandardCharsets; 31 import java.security.PrivateKey; 32 import java.security.cert.X509Certificate; 33 import java.util.Arrays; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 /** 39 * Enterprise configuration details for Wi-Fi. Stores details about the EAP method 40 * and any associated credentials. 41 */ 42 public class WifiEnterpriseConfig implements Parcelable { 43 44 /** Key prefix for WAPI AS certificates. */ 45 public static final String WAPI_AS_CERTIFICATE = "WAPIAS_"; 46 47 /** Key prefix for WAPI user certificates. */ 48 public static final String WAPI_USER_CERTIFICATE = "WAPIUSR_"; 49 50 /** 51 * Intent extra: name for WAPI AS certificates 52 */ 53 public static final String EXTRA_WAPI_AS_CERTIFICATE_NAME = 54 "android.net.wifi.extra.WAPI_AS_CERTIFICATE_NAME"; 55 56 /** 57 * Intent extra: data for WAPI AS certificates 58 */ 59 public static final String EXTRA_WAPI_AS_CERTIFICATE_DATA = 60 "android.net.wifi.extra.WAPI_AS_CERTIFICATE_DATA"; 61 62 /** 63 * Intent extra: name for WAPI USER certificates 64 */ 65 public static final String EXTRA_WAPI_USER_CERTIFICATE_NAME = 66 "android.net.wifi.extra.WAPI_USER_CERTIFICATE_NAME"; 67 68 /** 69 * Intent extra: data for WAPI USER certificates 70 */ 71 public static final String EXTRA_WAPI_USER_CERTIFICATE_DATA = 72 "android.net.wifi.extra.WAPI_USER_CERTIFICATE_DATA"; 73 74 /** @hide */ 75 public static final String EMPTY_VALUE = "NULL"; 76 /** @hide */ 77 public static final String EAP_KEY = "eap"; 78 /** @hide */ 79 public static final String PHASE2_KEY = "phase2"; 80 /** @hide */ 81 public static final String IDENTITY_KEY = "identity"; 82 /** @hide */ 83 public static final String ANON_IDENTITY_KEY = "anonymous_identity"; 84 /** @hide */ 85 public static final String PASSWORD_KEY = "password"; 86 /** @hide */ 87 public static final String SUBJECT_MATCH_KEY = "subject_match"; 88 /** @hide */ 89 public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match"; 90 /** @hide */ 91 public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match"; 92 /** @hide */ 93 public static final String OPP_KEY_CACHING = "proactive_key_caching"; 94 /** @hide */ 95 public static final String EAP_ERP = "eap_erp"; 96 /** @hide */ 97 public static final String OCSP = "ocsp"; 98 99 /** 100 * String representing the keystore OpenSSL ENGINE's ID. 101 * @hide 102 */ 103 public static final String ENGINE_ID_KEYSTORE = "keystore"; 104 105 /** 106 * String representing the keystore URI used for wpa_supplicant. 107 * @hide 108 */ 109 public static final String KEYSTORE_URI = "keystore://"; 110 111 /** 112 * String representing the keystore URI used for wpa_supplicant, 113 * Unlike #KEYSTORE_URI, this supports a list of space-delimited aliases 114 * @hide 115 */ 116 public static final String KEYSTORES_URI = "keystores://"; 117 118 /** 119 * String to set the engine value to when it should be enabled. 120 * @hide 121 */ 122 public static final String ENGINE_ENABLE = "1"; 123 124 /** 125 * String to set the engine value to when it should be disabled. 126 * @hide 127 */ 128 public static final String ENGINE_DISABLE = "0"; 129 130 /** 131 * Key prefix for CA certificates. 132 * Note: copied from {@link android.security.Credentials#CA_CERTIFICATE} since it is @hide. 133 */ 134 private static final String CA_CERTIFICATE = "CACERT_"; 135 /** 136 * Key prefix for user certificates. 137 * Note: copied from {@link android.security.Credentials#USER_CERTIFICATE} since it is @hide. 138 */ 139 private static final String USER_CERTIFICATE = "USRCERT_"; 140 /** 141 * Key prefix for user private and secret keys. 142 * Note: copied from {@link android.security.Credentials#USER_PRIVATE_KEY} since it is @hide. 143 */ 144 private static final String USER_PRIVATE_KEY = "USRPKEY_"; 145 146 /** @hide */ 147 public static final String CA_CERT_PREFIX = KEYSTORE_URI + CA_CERTIFICATE; 148 /** @hide */ 149 public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + USER_CERTIFICATE; 150 /** @hide */ 151 public static final String CLIENT_CERT_KEY = "client_cert"; 152 /** @hide */ 153 public static final String CA_CERT_KEY = "ca_cert"; 154 /** @hide */ 155 public static final String CA_PATH_KEY = "ca_path"; 156 /** @hide */ 157 public static final String ENGINE_KEY = "engine"; 158 /** @hide */ 159 public static final String ENGINE_ID_KEY = "engine_id"; 160 /** @hide */ 161 public static final String PRIVATE_KEY_ID_KEY = "key_id"; 162 /** @hide */ 163 public static final String REALM_KEY = "realm"; 164 /** @hide */ 165 public static final String PLMN_KEY = "plmn"; 166 /** @hide */ 167 public static final String CA_CERT_ALIAS_DELIMITER = " "; 168 /** @hide */ 169 public static final String WAPI_CERT_SUITE_KEY = "wapi_cert_suite"; 170 171 /** 172 * Do not use OCSP stapling (TLS certificate status extension) 173 * @hide 174 */ 175 @SystemApi 176 public static final int OCSP_NONE = 0; 177 178 /** 179 * Try to use OCSP stapling, but not require response 180 * @hide 181 */ 182 @SystemApi 183 public static final int OCSP_REQUEST_CERT_STATUS = 1; 184 185 /** 186 * Require valid OCSP stapling response 187 * @hide 188 */ 189 @SystemApi 190 public static final int OCSP_REQUIRE_CERT_STATUS = 2; 191 192 /** 193 * Require valid OCSP stapling response for all not-trusted certificates in the server 194 * certificate chain 195 * @hide 196 */ 197 @SystemApi 198 public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3; 199 200 /** @hide */ 201 @IntDef(prefix = {"OCSP_"}, value = { 202 OCSP_NONE, 203 OCSP_REQUEST_CERT_STATUS, 204 OCSP_REQUIRE_CERT_STATUS, 205 OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS 206 }) 207 @Retention(RetentionPolicy.SOURCE) 208 public @interface Ocsp {} 209 210 /** 211 * Whether to use/require OCSP (Online Certificate Status Protocol) to check server certificate. 212 * @hide 213 */ 214 private @Ocsp int mOcsp = OCSP_NONE; 215 216 // Fields to copy verbatim from wpa_supplicant. 217 private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] { 218 IDENTITY_KEY, 219 ANON_IDENTITY_KEY, 220 PASSWORD_KEY, 221 CLIENT_CERT_KEY, 222 CA_CERT_KEY, 223 SUBJECT_MATCH_KEY, 224 ENGINE_KEY, 225 ENGINE_ID_KEY, 226 PRIVATE_KEY_ID_KEY, 227 ALTSUBJECT_MATCH_KEY, 228 DOM_SUFFIX_MATCH_KEY, 229 CA_PATH_KEY 230 }; 231 232 /** 233 * Fields that have unquoted values in {@link #mFields}. 234 */ 235 private static final List<String> UNQUOTED_KEYS = Arrays.asList(ENGINE_KEY, OPP_KEY_CACHING, 236 EAP_ERP); 237 238 @UnsupportedAppUsage 239 private HashMap<String, String> mFields = new HashMap<String, String>(); 240 private X509Certificate[] mCaCerts; 241 private PrivateKey mClientPrivateKey; 242 private X509Certificate[] mClientCertificateChain; 243 private int mEapMethod = Eap.NONE; 244 private int mPhase2Method = Phase2.NONE; 245 private boolean mIsAppInstalledDeviceKeyAndCert = false; 246 private boolean mIsAppInstalledCaCert = false; 247 248 private static final String TAG = "WifiEnterpriseConfig"; 249 WifiEnterpriseConfig()250 public WifiEnterpriseConfig() { 251 // Do not set defaults so that the enterprise fields that are not changed 252 // by API are not changed underneath 253 // This is essential because an app may not have all fields like password 254 // available. It allows modification of subset of fields. 255 256 } 257 258 /** 259 * Copy over the contents of the source WifiEnterpriseConfig object over to this object. 260 * 261 * @param source Source WifiEnterpriseConfig object. 262 * @param ignoreMaskedPassword Set to true to ignore masked password field, false otherwise. 263 * @param mask if |ignoreMaskedPassword| is set, check if the incoming password field is set 264 * to this value. 265 */ copyFrom(WifiEnterpriseConfig source, boolean ignoreMaskedPassword, String mask)266 private void copyFrom(WifiEnterpriseConfig source, boolean ignoreMaskedPassword, String mask) { 267 for (String key : source.mFields.keySet()) { 268 if (ignoreMaskedPassword && key.equals(PASSWORD_KEY) 269 && TextUtils.equals(source.mFields.get(key), mask)) { 270 continue; 271 } 272 mFields.put(key, source.mFields.get(key)); 273 } 274 if (source.mCaCerts != null) { 275 mCaCerts = Arrays.copyOf(source.mCaCerts, source.mCaCerts.length); 276 } else { 277 mCaCerts = null; 278 } 279 mClientPrivateKey = source.mClientPrivateKey; 280 if (source.mClientCertificateChain != null) { 281 mClientCertificateChain = Arrays.copyOf( 282 source.mClientCertificateChain, 283 source.mClientCertificateChain.length); 284 } else { 285 mClientCertificateChain = null; 286 } 287 mEapMethod = source.mEapMethod; 288 mPhase2Method = source.mPhase2Method; 289 mIsAppInstalledDeviceKeyAndCert = source.mIsAppInstalledDeviceKeyAndCert; 290 mIsAppInstalledCaCert = source.mIsAppInstalledCaCert; 291 mOcsp = source.mOcsp; 292 } 293 294 /** 295 * Copy constructor. 296 * This copies over all the fields verbatim (does not ignore masked password fields). 297 * 298 * @param source Source WifiEnterpriseConfig object. 299 */ WifiEnterpriseConfig(WifiEnterpriseConfig source)300 public WifiEnterpriseConfig(WifiEnterpriseConfig source) { 301 copyFrom(source, false, ""); 302 } 303 304 /** 305 * Copy fields from the provided external WifiEnterpriseConfig. 306 * This is needed to handle the WifiEnterpriseConfig objects which were sent by apps with the 307 * password field masked. 308 * 309 * @param externalConfig External WifiEnterpriseConfig object. 310 * @param mask String mask to compare against. 311 * @hide 312 */ copyFromExternal(WifiEnterpriseConfig externalConfig, String mask)313 public void copyFromExternal(WifiEnterpriseConfig externalConfig, String mask) { 314 copyFrom(externalConfig, true, convertToQuotedString(mask)); 315 } 316 317 @Override describeContents()318 public int describeContents() { 319 return 0; 320 } 321 322 @Override writeToParcel(Parcel dest, int flags)323 public void writeToParcel(Parcel dest, int flags) { 324 dest.writeInt(mFields.size()); 325 for (Map.Entry<String, String> entry : mFields.entrySet()) { 326 dest.writeString(entry.getKey()); 327 dest.writeString(entry.getValue()); 328 } 329 330 dest.writeInt(mEapMethod); 331 dest.writeInt(mPhase2Method); 332 ParcelUtil.writeCertificates(dest, mCaCerts); 333 ParcelUtil.writePrivateKey(dest, mClientPrivateKey); 334 ParcelUtil.writeCertificates(dest, mClientCertificateChain); 335 dest.writeBoolean(mIsAppInstalledDeviceKeyAndCert); 336 dest.writeBoolean(mIsAppInstalledCaCert); 337 dest.writeInt(mOcsp); 338 } 339 340 public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR = 341 new Creator<WifiEnterpriseConfig>() { 342 @Override 343 public WifiEnterpriseConfig createFromParcel(Parcel in) { 344 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 345 int count = in.readInt(); 346 for (int i = 0; i < count; i++) { 347 String key = in.readString(); 348 String value = in.readString(); 349 enterpriseConfig.mFields.put(key, value); 350 } 351 352 enterpriseConfig.mEapMethod = in.readInt(); 353 enterpriseConfig.mPhase2Method = in.readInt(); 354 enterpriseConfig.mCaCerts = ParcelUtil.readCertificates(in); 355 enterpriseConfig.mClientPrivateKey = ParcelUtil.readPrivateKey(in); 356 enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in); 357 enterpriseConfig.mIsAppInstalledDeviceKeyAndCert = in.readBoolean(); 358 enterpriseConfig.mIsAppInstalledCaCert = in.readBoolean(); 359 enterpriseConfig.mOcsp = in.readInt(); 360 return enterpriseConfig; 361 } 362 363 @Override 364 public WifiEnterpriseConfig[] newArray(int size) { 365 return new WifiEnterpriseConfig[size]; 366 } 367 }; 368 369 /** The Extensible Authentication Protocol method used */ 370 public static final class Eap { 371 /** No EAP method used. Represents an empty config */ 372 public static final int NONE = -1; 373 /** Protected EAP */ 374 public static final int PEAP = 0; 375 /** EAP-Transport Layer Security */ 376 public static final int TLS = 1; 377 /** EAP-Tunneled Transport Layer Security */ 378 public static final int TTLS = 2; 379 /** EAP-Password */ 380 public static final int PWD = 3; 381 /** EAP-Subscriber Identity Module [RFC-4186] */ 382 public static final int SIM = 4; 383 /** EAP-Authentication and Key Agreement [RFC-4187] */ 384 public static final int AKA = 5; 385 /** EAP-Authentication and Key Agreement Prime [RFC-5448] */ 386 public static final int AKA_PRIME = 6; 387 /** Hotspot 2.0 r2 OSEN */ 388 public static final int UNAUTH_TLS = 7; 389 /** WAPI Certificate */ 390 public static final int WAPI_CERT = 8; 391 /** @hide */ 392 public static final String[] strings = 393 { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'", "WFA-UNAUTH-TLS", 394 "WAPI_CERT" }; 395 396 /** Prevent initialization */ Eap()397 private Eap() {} 398 } 399 400 /** The inner authentication method used */ 401 public static final class Phase2 { 402 public static final int NONE = 0; 403 /** Password Authentication Protocol */ 404 public static final int PAP = 1; 405 /** Microsoft Challenge Handshake Authentication Protocol */ 406 public static final int MSCHAP = 2; 407 /** Microsoft Challenge Handshake Authentication Protocol v2 */ 408 public static final int MSCHAPV2 = 3; 409 /** Generic Token Card */ 410 public static final int GTC = 4; 411 /** EAP-Subscriber Identity Module [RFC-4186] */ 412 public static final int SIM = 5; 413 /** EAP-Authentication and Key Agreement [RFC-4187] */ 414 public static final int AKA = 6; 415 /** EAP-Authentication and Key Agreement Prime [RFC-5448] */ 416 public static final int AKA_PRIME = 7; 417 private static final String AUTH_PREFIX = "auth="; 418 private static final String AUTHEAP_PREFIX = "autheap="; 419 /** @hide */ 420 public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", 421 "MSCHAPV2", "GTC", "SIM", "AKA", "AKA'" }; 422 423 /** Prevent initialization */ Phase2()424 private Phase2() {} 425 } 426 427 // Loader and saver interfaces for exchanging data with wpa_supplicant. 428 // TODO: Decouple this object (which is just a placeholder of the configuration) 429 // from the implementation that knows what wpa_supplicant wants. 430 /** 431 * Interface used for retrieving supplicant configuration from WifiEnterpriseConfig 432 * @hide 433 */ 434 public interface SupplicantSaver { 435 /** 436 * Set a value within wpa_supplicant configuration 437 * @param key index to set within wpa_supplciant 438 * @param value the value for the key 439 * @return true if successful; false otherwise 440 */ saveValue(String key, String value)441 boolean saveValue(String key, String value); 442 } 443 444 /** 445 * Interface used for populating a WifiEnterpriseConfig from supplicant configuration 446 * @hide 447 */ 448 public interface SupplicantLoader { 449 /** 450 * Returns a value within wpa_supplicant configuration 451 * @param key index to set within wpa_supplciant 452 * @return string value if successful; null otherwise 453 */ loadValue(String key)454 String loadValue(String key); 455 } 456 457 /** 458 * Internal use only; supply field values to wpa_supplicant config. The configuration 459 * process aborts on the first failed call on {@code saver}. 460 * @param saver proxy for setting configuration in wpa_supplciant 461 * @return whether the save succeeded on all attempts 462 * @hide 463 */ saveToSupplicant(SupplicantSaver saver)464 public boolean saveToSupplicant(SupplicantSaver saver) { 465 if (!isEapMethodValid()) { 466 return false; 467 } 468 469 // wpa_supplicant can update the anonymous identity for these kinds of networks after 470 // framework reads them, so make sure the framework doesn't try to overwrite them. 471 boolean shouldNotWriteAnonIdentity = mEapMethod == WifiEnterpriseConfig.Eap.SIM 472 || mEapMethod == WifiEnterpriseConfig.Eap.AKA 473 || mEapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME; 474 for (String key : mFields.keySet()) { 475 if (shouldNotWriteAnonIdentity && ANON_IDENTITY_KEY.equals(key)) { 476 continue; 477 } 478 if (!saver.saveValue(key, mFields.get(key))) { 479 return false; 480 } 481 } 482 483 if (!saver.saveValue(EAP_KEY, Eap.strings[mEapMethod])) { 484 return false; 485 } 486 487 if (mEapMethod != Eap.TLS && mEapMethod != Eap.UNAUTH_TLS && mPhase2Method != Phase2.NONE) { 488 boolean is_autheap = mEapMethod == Eap.TTLS && mPhase2Method == Phase2.GTC; 489 String prefix = is_autheap ? Phase2.AUTHEAP_PREFIX : Phase2.AUTH_PREFIX; 490 String value = convertToQuotedString(prefix + Phase2.strings[mPhase2Method]); 491 return saver.saveValue(PHASE2_KEY, value); 492 } else if (mPhase2Method == Phase2.NONE) { 493 // By default, send a null phase 2 to clear old configuration values. 494 return saver.saveValue(PHASE2_KEY, null); 495 } else { 496 Log.e(TAG, "WiFi enterprise configuration is invalid as it supplies a " 497 + "phase 2 method but the phase1 method does not support it."); 498 return false; 499 } 500 } 501 502 /** 503 * Internal use only; retrieve configuration from wpa_supplicant config. 504 * @param loader proxy for retrieving configuration keys from wpa_supplicant 505 * @hide 506 */ loadFromSupplicant(SupplicantLoader loader)507 public void loadFromSupplicant(SupplicantLoader loader) { 508 for (String key : SUPPLICANT_CONFIG_KEYS) { 509 String value = loader.loadValue(key); 510 if (value == null) { 511 mFields.put(key, EMPTY_VALUE); 512 } else { 513 mFields.put(key, value); 514 } 515 } 516 String eapMethod = loader.loadValue(EAP_KEY); 517 mEapMethod = getStringIndex(Eap.strings, eapMethod, Eap.NONE); 518 519 String phase2Method = removeDoubleQuotes(loader.loadValue(PHASE2_KEY)); 520 // Remove "auth=" or "autheap=" prefix. 521 if (phase2Method.startsWith(Phase2.AUTH_PREFIX)) { 522 phase2Method = phase2Method.substring(Phase2.AUTH_PREFIX.length()); 523 } else if (phase2Method.startsWith(Phase2.AUTHEAP_PREFIX)) { 524 phase2Method = phase2Method.substring(Phase2.AUTHEAP_PREFIX.length()); 525 } 526 mPhase2Method = getStringIndex(Phase2.strings, phase2Method, Phase2.NONE); 527 } 528 529 /** 530 * Set the EAP authentication method. 531 * @param eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or 532 * {@link Eap#PWD} 533 * @throws IllegalArgumentException on an invalid eap method 534 */ setEapMethod(int eapMethod)535 public void setEapMethod(int eapMethod) { 536 switch (eapMethod) { 537 /** Valid methods */ 538 case Eap.WAPI_CERT: 539 mEapMethod = eapMethod; 540 setPhase2Method(Phase2.NONE); 541 break; 542 case Eap.TLS: 543 case Eap.UNAUTH_TLS: 544 setPhase2Method(Phase2.NONE); 545 /* fall through */ 546 case Eap.PEAP: 547 case Eap.PWD: 548 case Eap.TTLS: 549 case Eap.SIM: 550 case Eap.AKA: 551 case Eap.AKA_PRIME: 552 mEapMethod = eapMethod; 553 setFieldValue(OPP_KEY_CACHING, "1"); 554 break; 555 default: 556 throw new IllegalArgumentException("Unknown EAP method"); 557 } 558 } 559 560 /** 561 * Get the eap method. 562 * @return eap method configured 563 */ getEapMethod()564 public int getEapMethod() { 565 return mEapMethod; 566 } 567 568 /** 569 * Set Phase 2 authentication method. Sets the inner authentication method to be used in 570 * phase 2 after setting up a secure channel 571 * @param phase2Method is the inner authentication method and can be one of {@link Phase2#NONE}, 572 * {@link Phase2#PAP}, {@link Phase2#MSCHAP}, {@link Phase2#MSCHAPV2}, 573 * {@link Phase2#GTC} 574 * @throws IllegalArgumentException on an invalid phase2 method 575 * 576 */ setPhase2Method(int phase2Method)577 public void setPhase2Method(int phase2Method) { 578 switch (phase2Method) { 579 case Phase2.NONE: 580 case Phase2.PAP: 581 case Phase2.MSCHAP: 582 case Phase2.MSCHAPV2: 583 case Phase2.GTC: 584 case Phase2.SIM: 585 case Phase2.AKA: 586 case Phase2.AKA_PRIME: 587 mPhase2Method = phase2Method; 588 break; 589 default: 590 throw new IllegalArgumentException("Unknown Phase 2 method"); 591 } 592 } 593 594 /** 595 * Get the phase 2 authentication method. 596 * @return a phase 2 method defined at {@link Phase2} 597 * */ getPhase2Method()598 public int getPhase2Method() { 599 return mPhase2Method; 600 } 601 602 /** 603 * Set the identity 604 * @param identity 605 */ setIdentity(String identity)606 public void setIdentity(String identity) { 607 setFieldValue(IDENTITY_KEY, identity, ""); 608 } 609 610 /** 611 * Get the identity 612 * @return the identity 613 */ getIdentity()614 public String getIdentity() { 615 return getFieldValue(IDENTITY_KEY); 616 } 617 618 /** 619 * Set anonymous identity. This is used as the unencrypted identity with 620 * certain EAP types 621 * @param anonymousIdentity the anonymous identity 622 */ setAnonymousIdentity(String anonymousIdentity)623 public void setAnonymousIdentity(String anonymousIdentity) { 624 setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity); 625 } 626 627 /** 628 * Get the anonymous identity 629 * @return anonymous identity 630 */ getAnonymousIdentity()631 public String getAnonymousIdentity() { 632 return getFieldValue(ANON_IDENTITY_KEY); 633 } 634 635 /** 636 * Set the password. 637 * @param password the password 638 */ setPassword(String password)639 public void setPassword(String password) { 640 setFieldValue(PASSWORD_KEY, password); 641 } 642 643 /** 644 * Get the password. 645 * 646 * Returns locally set password value. For networks fetched from 647 * framework, returns "*". 648 */ getPassword()649 public String getPassword() { 650 return getFieldValue(PASSWORD_KEY); 651 } 652 653 /** 654 * Encode a CA certificate alias so it does not contain illegal character. 655 * @hide 656 */ encodeCaCertificateAlias(String alias)657 public static String encodeCaCertificateAlias(String alias) { 658 byte[] bytes = alias.getBytes(StandardCharsets.UTF_8); 659 StringBuilder sb = new StringBuilder(bytes.length * 2); 660 for (byte o : bytes) { 661 sb.append(String.format("%02x", o & 0xFF)); 662 } 663 return sb.toString(); 664 } 665 666 /** 667 * Decode a previously-encoded CA certificate alias. 668 * @hide 669 */ decodeCaCertificateAlias(String alias)670 public static String decodeCaCertificateAlias(String alias) { 671 byte[] data = new byte[alias.length() >> 1]; 672 for (int n = 0, position = 0; n < alias.length(); n += 2, position++) { 673 data[position] = (byte) Integer.parseInt(alias.substring(n, n + 2), 16); 674 } 675 try { 676 return new String(data, StandardCharsets.UTF_8); 677 } catch (NumberFormatException e) { 678 e.printStackTrace(); 679 return alias; 680 } 681 } 682 683 /** 684 * Set CA certificate alias. 685 * 686 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 687 * a certificate 688 * </p> 689 * @param alias identifies the certificate 690 * @hide 691 */ 692 @UnsupportedAppUsage setCaCertificateAlias(String alias)693 public void setCaCertificateAlias(String alias) { 694 setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); 695 } 696 697 /** 698 * Set CA certificate aliases. When creating installing the corresponding certificate to 699 * the keystore, please use alias encoded by {@link #encodeCaCertificateAlias(String)}. 700 * 701 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 702 * a certificate. 703 * </p> 704 * @param aliases identifies the certificate. Can be null to indicate the absence of a 705 * certificate. 706 * @hide 707 */ 708 @SystemApi setCaCertificateAliases(@ullable String[] aliases)709 public void setCaCertificateAliases(@Nullable String[] aliases) { 710 if (aliases == null) { 711 setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX); 712 } else if (aliases.length == 1) { 713 // Backwards compatibility: use the original cert prefix if setting only one alias. 714 setCaCertificateAlias(aliases[0]); 715 } else { 716 // Use KEYSTORES_URI which supports multiple aliases. 717 StringBuilder sb = new StringBuilder(); 718 for (int i = 0; i < aliases.length; i++) { 719 if (i > 0) { 720 sb.append(CA_CERT_ALIAS_DELIMITER); 721 } 722 sb.append(encodeCaCertificateAlias(CA_CERTIFICATE + aliases[i])); 723 } 724 setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI); 725 } 726 } 727 728 /** 729 * Get CA certificate alias 730 * @return alias to the CA certificate 731 * @hide 732 */ 733 @UnsupportedAppUsage getCaCertificateAlias()734 public String getCaCertificateAlias() { 735 return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); 736 } 737 738 /** 739 * Get CA certificate aliases. 740 * @return alias to the CA certificate, or null if unset. 741 * @hide 742 */ 743 @Nullable 744 @SystemApi getCaCertificateAliases()745 public String[] getCaCertificateAliases() { 746 String value = getFieldValue(CA_CERT_KEY); 747 if (value.startsWith(CA_CERT_PREFIX)) { 748 // Backwards compatibility: parse the original alias prefix. 749 return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)}; 750 } else if (value.startsWith(KEYSTORES_URI)) { 751 String values = value.substring(KEYSTORES_URI.length()); 752 753 String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER); 754 for (int i = 0; i < aliases.length; i++) { 755 aliases[i] = decodeCaCertificateAlias(aliases[i]); 756 if (aliases[i].startsWith(CA_CERTIFICATE)) { 757 aliases[i] = aliases[i].substring(CA_CERTIFICATE.length()); 758 } 759 } 760 return aliases.length != 0 ? aliases : null; 761 } else { 762 return TextUtils.isEmpty(value) ? null : new String[] {value}; 763 } 764 } 765 766 /** 767 * Specify a X.509 certificate that identifies the server. 768 * 769 * <p>A default name is automatically assigned to the certificate and used 770 * with this configuration. The framework takes care of installing the 771 * certificate when the config is saved and removing the certificate when 772 * the config is removed. 773 * 774 * Note: If no certificate is set for an Enterprise configuration, either by not calling this 775 * API (or the {@link #setCaCertificates(X509Certificate[])}, or by calling it with null, then 776 * the server certificate validation is skipped - which means that the connection is not secure. 777 * 778 * @param cert X.509 CA certificate 779 * @throws IllegalArgumentException if not a CA certificate 780 */ setCaCertificate(@ullable X509Certificate cert)781 public void setCaCertificate(@Nullable X509Certificate cert) { 782 if (cert != null) { 783 if (cert.getBasicConstraints() >= 0) { 784 mIsAppInstalledCaCert = true; 785 mCaCerts = new X509Certificate[] {cert}; 786 } else { 787 mCaCerts = null; 788 throw new IllegalArgumentException("Not a CA certificate"); 789 } 790 } else { 791 mCaCerts = null; 792 } 793 } 794 795 /** 796 * Get CA certificate. If multiple CA certificates are configured previously, 797 * return the first one. 798 * @return X.509 CA certificate 799 */ getCaCertificate()800 @Nullable public X509Certificate getCaCertificate() { 801 if (mCaCerts != null && mCaCerts.length > 0) { 802 return mCaCerts[0]; 803 } else { 804 return null; 805 } 806 } 807 808 /** 809 * Specify a list of X.509 certificates that identifies the server. The validation 810 * passes if the CA of server certificate matches one of the given certificates. 811 812 * <p>Default names are automatically assigned to the certificates and used 813 * with this configuration. The framework takes care of installing the 814 * certificates when the config is saved and removing the certificates when 815 * the config is removed. 816 * 817 * Note: If no certificates are set for an Enterprise configuration, either by not calling this 818 * API (or the {@link #setCaCertificate(X509Certificate)}, or by calling it with null, then the 819 * server certificate validation is skipped - which means that the 820 * connection is not secure. 821 * 822 * @param certs X.509 CA certificates 823 * @throws IllegalArgumentException if any of the provided certificates is 824 * not a CA certificate 825 */ setCaCertificates(@ullable X509Certificate[] certs)826 public void setCaCertificates(@Nullable X509Certificate[] certs) { 827 if (certs != null) { 828 X509Certificate[] newCerts = new X509Certificate[certs.length]; 829 for (int i = 0; i < certs.length; i++) { 830 if (certs[i].getBasicConstraints() >= 0) { 831 newCerts[i] = certs[i]; 832 } else { 833 mCaCerts = null; 834 throw new IllegalArgumentException("Not a CA certificate"); 835 } 836 } 837 mCaCerts = newCerts; 838 mIsAppInstalledCaCert = true; 839 } else { 840 mCaCerts = null; 841 } 842 } 843 844 /** 845 * Get CA certificates. 846 */ getCaCertificates()847 @Nullable public X509Certificate[] getCaCertificates() { 848 if (mCaCerts != null && mCaCerts.length > 0) { 849 return mCaCerts; 850 } else { 851 return null; 852 } 853 } 854 855 /** 856 * @hide 857 */ resetCaCertificate()858 public void resetCaCertificate() { 859 mCaCerts = null; 860 } 861 862 /** 863 * Set the ca_path directive on wpa_supplicant. 864 * 865 * From wpa_supplicant documentation: 866 * 867 * Directory path for CA certificate files (PEM). This path may contain 868 * multiple CA certificates in OpenSSL format. Common use for this is to 869 * point to system trusted CA list which is often installed into directory 870 * like /etc/ssl/certs. If configured, these certificates are added to the 871 * list of trusted CAs. ca_cert may also be included in that case, but it is 872 * not required. 873 * 874 * Note: If no certificate path is set for an Enterprise configuration, either by not calling 875 * this API, or by calling it with null, and no certificate is set by 876 * {@link #setCaCertificate(X509Certificate)} or {@link #setCaCertificates(X509Certificate[])}, 877 * then the server certificate validation is skipped - which means that the connection is not 878 * secure. 879 * 880 * @param path The path for CA certificate files, or empty string to clear. 881 * @hide 882 */ 883 @SystemApi setCaPath(@onNull String path)884 public void setCaPath(@NonNull String path) { 885 setFieldValue(CA_PATH_KEY, path); 886 } 887 888 /** 889 * Get the ca_path directive from wpa_supplicant. 890 * @return The path for CA certificate files, or an empty string if unset. 891 * @hide 892 */ 893 @NonNull 894 @SystemApi getCaPath()895 public String getCaPath() { 896 return getFieldValue(CA_PATH_KEY); 897 } 898 899 /** 900 * Set Client certificate alias. 901 * 902 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 903 * a certificate 904 * </p> 905 * @param alias identifies the certificate, or empty string to clear. 906 * @hide 907 */ 908 @SystemApi setClientCertificateAlias(@onNull String alias)909 public void setClientCertificateAlias(@NonNull String alias) { 910 setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); 911 setFieldValue(PRIVATE_KEY_ID_KEY, alias, USER_PRIVATE_KEY); 912 // Also, set engine parameters 913 if (TextUtils.isEmpty(alias)) { 914 setFieldValue(ENGINE_KEY, ENGINE_DISABLE); 915 setFieldValue(ENGINE_ID_KEY, ""); 916 } else { 917 setFieldValue(ENGINE_KEY, ENGINE_ENABLE); 918 setFieldValue(ENGINE_ID_KEY, ENGINE_ID_KEYSTORE); 919 } 920 } 921 922 /** 923 * Get client certificate alias. 924 * @return alias to the client certificate, or an empty string if unset. 925 * @hide 926 */ 927 @NonNull 928 @SystemApi getClientCertificateAlias()929 public String getClientCertificateAlias() { 930 return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); 931 } 932 933 /** 934 * Specify a private key and client certificate for client authorization. 935 * 936 * <p>A default name is automatically assigned to the key entry and used 937 * with this configuration. The framework takes care of installing the 938 * key entry when the config is saved and removing the key entry when 939 * the config is removed. 940 941 * @param privateKey a PrivateKey instance for the end certificate. 942 * @param clientCertificate an X509Certificate representing the end certificate. 943 * @throws IllegalArgumentException for an invalid key or certificate. 944 */ setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate)945 public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { 946 X509Certificate[] clientCertificates = null; 947 if (clientCertificate != null) { 948 clientCertificates = new X509Certificate[] {clientCertificate}; 949 } 950 setClientKeyEntryWithCertificateChain(privateKey, clientCertificates); 951 } 952 953 /** 954 * Specify a private key and client certificate chain for client authorization. 955 * 956 * <p>A default name is automatically assigned to the key entry and used 957 * with this configuration. The framework takes care of installing the 958 * key entry when the config is saved and removing the key entry when 959 * the config is removed. 960 * 961 * @param privateKey a PrivateKey instance for the end certificate. 962 * @param clientCertificateChain an array of X509Certificate instances which starts with 963 * end certificate and continues with additional CA certificates necessary to 964 * link the end certificate with some root certificate known by the authenticator. 965 * @throws IllegalArgumentException for an invalid key or certificate. 966 */ setClientKeyEntryWithCertificateChain(PrivateKey privateKey, X509Certificate[] clientCertificateChain)967 public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey, 968 X509Certificate[] clientCertificateChain) { 969 X509Certificate[] newCerts = null; 970 if (clientCertificateChain != null && clientCertificateChain.length > 0) { 971 // We validate that this is a well formed chain that starts 972 // with an end-certificate and is followed by CA certificates. 973 // We don't validate that each following certificate verifies 974 // the previous. https://en.wikipedia.org/wiki/Chain_of_trust 975 // 976 // Basic constraints is an X.509 extension type that defines 977 // whether a given certificate is allowed to sign additional 978 // certificates and what path length restrictions may exist. 979 // We use this to judge whether the certificate is an end 980 // certificate or a CA certificate. 981 // https://cryptography.io/en/latest/x509/reference/ 982 if (clientCertificateChain[0].getBasicConstraints() != -1) { 983 throw new IllegalArgumentException( 984 "First certificate in the chain must be a client end certificate"); 985 } 986 987 for (int i = 1; i < clientCertificateChain.length; i++) { 988 if (clientCertificateChain[i].getBasicConstraints() == -1) { 989 throw new IllegalArgumentException( 990 "All certificates following the first must be CA certificates"); 991 } 992 } 993 newCerts = Arrays.copyOf(clientCertificateChain, 994 clientCertificateChain.length); 995 996 if (privateKey == null) { 997 throw new IllegalArgumentException("Client cert without a private key"); 998 } 999 if (privateKey.getEncoded() == null) { 1000 throw new IllegalArgumentException("Private key cannot be encoded"); 1001 } 1002 } 1003 1004 mClientPrivateKey = privateKey; 1005 mClientCertificateChain = newCerts; 1006 mIsAppInstalledDeviceKeyAndCert = true; 1007 } 1008 1009 /** 1010 * Get client certificate 1011 * 1012 * @return X.509 client certificate 1013 */ getClientCertificate()1014 public X509Certificate getClientCertificate() { 1015 if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { 1016 return mClientCertificateChain[0]; 1017 } else { 1018 return null; 1019 } 1020 } 1021 1022 /** 1023 * Get the complete client certificate chain in the same order as it was last supplied. 1024 * 1025 * <p>If the chain was last supplied by a call to 1026 * {@link #setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate)} 1027 * with a non-null * certificate instance, a single-element array containing the certificate 1028 * will be * returned. If {@link #setClientKeyEntryWithCertificateChain( 1029 * java.security.PrivateKey, java.security.cert.X509Certificate[])} was last called with a 1030 * non-empty array, this array will be returned in the same order as it was supplied. 1031 * Otherwise, {@code null} will be returned. 1032 * 1033 * @return X.509 client certificates 1034 */ getClientCertificateChain()1035 @Nullable public X509Certificate[] getClientCertificateChain() { 1036 if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { 1037 return mClientCertificateChain; 1038 } else { 1039 return null; 1040 } 1041 } 1042 1043 /** 1044 * @hide 1045 */ resetClientKeyEntry()1046 public void resetClientKeyEntry() { 1047 mClientPrivateKey = null; 1048 mClientCertificateChain = null; 1049 } 1050 1051 /** 1052 * Get the client private key as supplied in {@link #setClientKeyEntryWithCertificateChain}, or 1053 * null if unset. 1054 */ 1055 @Nullable getClientPrivateKey()1056 public PrivateKey getClientPrivateKey() { 1057 return mClientPrivateKey; 1058 } 1059 1060 /** 1061 * Set subject match (deprecated). This is the substring to be matched against the subject of 1062 * the authentication server certificate. 1063 * @param subjectMatch substring to be matched 1064 * @deprecated in favor of altSubjectMatch 1065 */ setSubjectMatch(String subjectMatch)1066 public void setSubjectMatch(String subjectMatch) { 1067 setFieldValue(SUBJECT_MATCH_KEY, subjectMatch); 1068 } 1069 1070 /** 1071 * Get subject match (deprecated) 1072 * @return the subject match string 1073 * @deprecated in favor of altSubjectMatch 1074 */ getSubjectMatch()1075 public String getSubjectMatch() { 1076 return getFieldValue(SUBJECT_MATCH_KEY); 1077 } 1078 1079 /** 1080 * Set alternate subject match. This is the substring to be matched against the 1081 * alternate subject of the authentication server certificate. 1082 * 1083 * Note: If no alternate subject is set for an Enterprise configuration, either by not calling 1084 * this API, or by calling it with null, or not setting domain suffix match using the 1085 * {@link #setDomainSuffixMatch(String)}, then the server certificate validation is incomplete - 1086 * which means that the connection is not secure. 1087 * 1088 * @param altSubjectMatch substring to be matched, for example 1089 * DNS:server.example.com;EMAIL:server@example.com 1090 */ setAltSubjectMatch(String altSubjectMatch)1091 public void setAltSubjectMatch(String altSubjectMatch) { 1092 setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch); 1093 } 1094 1095 /** 1096 * Get alternate subject match 1097 * @return the alternate subject match string 1098 */ getAltSubjectMatch()1099 public String getAltSubjectMatch() { 1100 return getFieldValue(ALTSUBJECT_MATCH_KEY); 1101 } 1102 1103 /** 1104 * Set the domain_suffix_match directive on wpa_supplicant. This is the parameter to use 1105 * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2, 1106 * second paragraph. 1107 * 1108 * <p>From wpa_supplicant documentation: 1109 * <p>Constraint for server domain name. If set, this FQDN is used as a suffix match requirement 1110 * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is 1111 * found, this constraint is met. 1112 * <p>Suffix match here means that the host/domain name is compared one label at a time starting 1113 * from the top-level domain and all the labels in domain_suffix_match shall be included in the 1114 * certificate. The certificate may include additional sub-level labels in addition to the 1115 * required labels. 1116 * <p>More than one match string can be provided by using semicolons to separate the strings 1117 * (e.g., example.org;example.com). When multiple strings are specified, a match with any one of 1118 * the values is considered a sufficient match for the certificate, i.e., the conditions are 1119 * ORed ogether. 1120 * <p>For example, domain_suffix_match=example.com would match test.example.com but would not 1121 * match test-example.com. 1122 * 1123 * Note: If no domain suffix is set for an Enterprise configuration, either by not calling this 1124 * API, or by calling it with null, or not setting alternate subject match using the 1125 * {@link #setAltSubjectMatch(String)}, then the server certificate 1126 * validation is incomplete - which means that the connection is not secure. 1127 * 1128 * @param domain The domain value 1129 */ setDomainSuffixMatch(String domain)1130 public void setDomainSuffixMatch(String domain) { 1131 setFieldValue(DOM_SUFFIX_MATCH_KEY, domain); 1132 } 1133 1134 /** 1135 * Get the domain_suffix_match value. See setDomSuffixMatch. 1136 * @return The domain value. 1137 */ getDomainSuffixMatch()1138 public String getDomainSuffixMatch() { 1139 return getFieldValue(DOM_SUFFIX_MATCH_KEY); 1140 } 1141 1142 /** 1143 * Set realm for Passpoint credential; realm identifies a set of networks where your 1144 * Passpoint credential can be used 1145 * @param realm the realm 1146 */ setRealm(String realm)1147 public void setRealm(String realm) { 1148 setFieldValue(REALM_KEY, realm); 1149 } 1150 1151 /** 1152 * Get realm for Passpoint credential; see {@link #setRealm(String)} for more information 1153 * @return the realm 1154 */ getRealm()1155 public String getRealm() { 1156 return getFieldValue(REALM_KEY); 1157 } 1158 1159 /** 1160 * Set plmn (Public Land Mobile Network) of the provider of Passpoint credential 1161 * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code) 1162 */ setPlmn(String plmn)1163 public void setPlmn(String plmn) { 1164 setFieldValue(PLMN_KEY, plmn); 1165 } 1166 1167 /** 1168 * Get plmn (Public Land Mobile Network) for Passpoint credential; see {@link #setPlmn 1169 * (String)} for more information 1170 * @return the plmn 1171 */ getPlmn()1172 public String getPlmn() { 1173 return getFieldValue(PLMN_KEY); 1174 } 1175 1176 /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ getKeyId(WifiEnterpriseConfig current)1177 public String getKeyId(WifiEnterpriseConfig current) { 1178 // If EAP method is not initialized, use current config details 1179 if (mEapMethod == Eap.NONE) { 1180 return (current != null) ? current.getKeyId(null) : EMPTY_VALUE; 1181 } 1182 if (!isEapMethodValid()) { 1183 return EMPTY_VALUE; 1184 } 1185 return Eap.strings[mEapMethod] + "_" + Phase2.strings[mPhase2Method]; 1186 } 1187 removeDoubleQuotes(String string)1188 private String removeDoubleQuotes(String string) { 1189 if (TextUtils.isEmpty(string)) return ""; 1190 int length = string.length(); 1191 if ((length > 1) && (string.charAt(0) == '"') 1192 && (string.charAt(length - 1) == '"')) { 1193 return string.substring(1, length - 1); 1194 } 1195 return string; 1196 } 1197 convertToQuotedString(String string)1198 private String convertToQuotedString(String string) { 1199 return "\"" + string + "\""; 1200 } 1201 1202 /** 1203 * Returns the index at which the toBeFound string is found in the array. 1204 * @param arr array of strings 1205 * @param toBeFound string to be found 1206 * @param defaultIndex default index to be returned when string is not found 1207 * @return the index into array 1208 */ getStringIndex(String arr[], String toBeFound, int defaultIndex)1209 private int getStringIndex(String arr[], String toBeFound, int defaultIndex) { 1210 if (TextUtils.isEmpty(toBeFound)) return defaultIndex; 1211 for (int i = 0; i < arr.length; i++) { 1212 if (toBeFound.equals(arr[i])) return i; 1213 } 1214 return defaultIndex; 1215 } 1216 1217 /** 1218 * Returns the field value for the key with prefix removed. 1219 * @param key into the hash 1220 * @param prefix is the prefix that the value may have 1221 * @return value 1222 * @hide 1223 */ getFieldValue(String key, String prefix)1224 private String getFieldValue(String key, String prefix) { 1225 // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since 1226 // neither of these keys should be retrieved in this manner. 1227 String value = mFields.get(key); 1228 // Uninitialized or known to be empty after reading from supplicant 1229 if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return ""; 1230 1231 value = removeDoubleQuotes(value); 1232 if (value.startsWith(prefix)) { 1233 return value.substring(prefix.length()); 1234 } else { 1235 return value; 1236 } 1237 } 1238 1239 /** 1240 * Returns the field value for the key. 1241 * @param key into the hash 1242 * @return value 1243 * @hide 1244 */ getFieldValue(String key)1245 public String getFieldValue(String key) { 1246 return getFieldValue(key, ""); 1247 } 1248 1249 /** 1250 * Set a value with an optional prefix at key 1251 * @param key into the hash 1252 * @param value to be set 1253 * @param prefix an optional value to be prefixed to actual value 1254 * @hide 1255 */ setFieldValue(String key, String value, String prefix)1256 private void setFieldValue(String key, String value, String prefix) { 1257 // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since 1258 // neither of these keys should be set in this manner. 1259 if (TextUtils.isEmpty(value)) { 1260 mFields.put(key, EMPTY_VALUE); 1261 } else { 1262 String valueToSet; 1263 if (!UNQUOTED_KEYS.contains(key)) { 1264 valueToSet = convertToQuotedString(prefix + value); 1265 } else { 1266 valueToSet = prefix + value; 1267 } 1268 mFields.put(key, valueToSet); 1269 } 1270 } 1271 1272 /** 1273 * Set a value at key 1274 * @param key into the hash 1275 * @param value to be set 1276 * @hide 1277 */ setFieldValue(String key, String value)1278 public void setFieldValue(String key, String value) { 1279 setFieldValue(key, value, ""); 1280 } 1281 1282 @Override toString()1283 public String toString() { 1284 StringBuffer sb = new StringBuffer(); 1285 for (String key : mFields.keySet()) { 1286 // Don't display password in toString(). 1287 String value = PASSWORD_KEY.equals(key) ? "<removed>" : mFields.get(key); 1288 sb.append(key).append(" ").append(value).append("\n"); 1289 } 1290 if (mEapMethod >= 0 && mEapMethod < Eap.strings.length) { 1291 sb.append("eap_method: ").append(Eap.strings[mEapMethod]).append("\n"); 1292 } 1293 if (mPhase2Method > 0 && mPhase2Method < Phase2.strings.length) { 1294 sb.append("phase2_method: ").append(Phase2.strings[mPhase2Method]).append("\n"); 1295 } 1296 sb.append(" ocsp: ").append(mOcsp).append("\n"); 1297 return sb.toString(); 1298 } 1299 1300 /** 1301 * Returns whether the EAP method data is valid, i.e., whether mEapMethod and mPhase2Method 1302 * are valid indices into {@code Eap.strings[]} and {@code Phase2.strings[]} respectively. 1303 */ isEapMethodValid()1304 private boolean isEapMethodValid() { 1305 if (mEapMethod == Eap.NONE) { 1306 Log.e(TAG, "WiFi enterprise configuration is invalid as it supplies no EAP method."); 1307 return false; 1308 } 1309 if (mEapMethod < 0 || mEapMethod >= Eap.strings.length) { 1310 Log.e(TAG, "mEapMethod is invald for WiFi enterprise configuration: " + mEapMethod); 1311 return false; 1312 } 1313 if (mPhase2Method < 0 || mPhase2Method >= Phase2.strings.length) { 1314 Log.e(TAG, "mPhase2Method is invald for WiFi enterprise configuration: " 1315 + mPhase2Method); 1316 return false; 1317 } 1318 return true; 1319 } 1320 1321 /** 1322 * Check if certificate was installed by an app, or manually (not by an app). If true, 1323 * certificate and keys will be removed from key storage when this network is removed. If not, 1324 * then certificates and keys remain persistent until the user manually removes them. 1325 * 1326 * @return true if certificate was installed by an app, false if certificate was installed 1327 * manually by the user. 1328 * @hide 1329 */ isAppInstalledDeviceKeyAndCert()1330 public boolean isAppInstalledDeviceKeyAndCert() { 1331 return mIsAppInstalledDeviceKeyAndCert; 1332 } 1333 1334 /** 1335 * Check if CA certificate was installed by an app, or manually (not by an app). If true, 1336 * CA certificate will be removed from key storage when this network is removed. If not, 1337 * then certificates and keys remain persistent until the user manually removes them. 1338 * 1339 * @return true if CA certificate was installed by an app, false if CA certificate was installed 1340 * manually by the user. 1341 * @hide 1342 */ isAppInstalledCaCert()1343 public boolean isAppInstalledCaCert() { 1344 return mIsAppInstalledCaCert; 1345 } 1346 1347 /** 1348 * Set the OCSP type. 1349 * @param ocsp is one of {@link ##OCSP_NONE}, {@link #OCSP_REQUEST_CERT_STATUS}, 1350 * {@link #OCSP_REQUIRE_CERT_STATUS} or 1351 * {@link #OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS} 1352 * @throws IllegalArgumentException if the OCSP type is invalid 1353 * @hide 1354 */ 1355 @SystemApi setOcsp(@csp int ocsp)1356 public void setOcsp(@Ocsp int ocsp) { 1357 if (ocsp >= OCSP_NONE && ocsp <= OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS) { 1358 mOcsp = ocsp; 1359 } else { 1360 throw new IllegalArgumentException("Invalid OCSP type."); 1361 } 1362 } 1363 1364 /** 1365 * Get the OCSP type. 1366 * @hide 1367 */ 1368 @SystemApi getOcsp()1369 public @Ocsp int getOcsp() { 1370 return mOcsp; 1371 } 1372 1373 /** 1374 * Utility method to determine whether the configuration's authentication method is SIM-based. 1375 * 1376 * @return true if the credential information requires SIM card for current authentication 1377 * method, otherwise it returns false. 1378 */ isAuthenticationSimBased()1379 public boolean isAuthenticationSimBased() { 1380 if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) { 1381 return true; 1382 } 1383 if (mEapMethod == Eap.PEAP) { 1384 return mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA 1385 || mPhase2Method == Phase2.AKA_PRIME; 1386 } 1387 return false; 1388 } 1389 1390 /** 1391 * Set the WAPI certificate suite name on wpa_supplicant. 1392 * 1393 * If this field is not specified, WAPI-CERT uses ASU ID from WAI packet 1394 * as the certificate suite name automatically. 1395 * 1396 * @param wapiCertSuite The name for WAPI certificate suite, or empty string to clear. 1397 * @hide 1398 */ 1399 @SystemApi setWapiCertSuite(@onNull String wapiCertSuite)1400 public void setWapiCertSuite(@NonNull String wapiCertSuite) { 1401 setFieldValue(WAPI_CERT_SUITE_KEY, wapiCertSuite); 1402 } 1403 1404 /** 1405 * Get the WAPI certificate suite name 1406 * @return the certificate suite name 1407 * @hide 1408 */ 1409 @NonNull 1410 @SystemApi getWapiCertSuite()1411 public String getWapiCertSuite() { 1412 return getFieldValue(WAPI_CERT_SUITE_KEY); 1413 } 1414 1415 /** 1416 * Method determines whether the Enterprise configuration is insecure. An insecure 1417 * configuration is one where EAP method requires a CA certification, i.e. PEAP, TLS, or 1418 * TTLS, and any of the following conditions are met: 1419 * - Both certificate and CA path are not configured. 1420 * - Both alternative subject match and domain suffix match are not set. 1421 * 1422 * Note: this method does not exhaustively check security of the configuration - i.e. a return 1423 * value of {@code false} is not a guarantee that the configuration is secure. 1424 * @hide 1425 */ isInsecure()1426 public boolean isInsecure() { 1427 if (mEapMethod != Eap.PEAP && mEapMethod != Eap.TLS && mEapMethod != Eap.TTLS) { 1428 return false; 1429 } 1430 if (TextUtils.isEmpty(getAltSubjectMatch()) 1431 && TextUtils.isEmpty(getDomainSuffixMatch())) { 1432 // Both subject and domain match are not set, it's insecure. 1433 return true; 1434 } 1435 if (mIsAppInstalledCaCert) { 1436 // CA certificate is installed by App, it's secure. 1437 return false; 1438 } 1439 if (getCaCertificateAliases() != null) { 1440 // CA certificate alias from keyStore is set, it's secure. 1441 return false; 1442 } 1443 return TextUtils.isEmpty(getCaPath()); 1444 } 1445 } 1446