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.Build; 24 import android.os.Bundle; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import androidx.annotation.RequiresApi; 31 32 import com.android.modules.utils.build.SdkLevel; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.nio.charset.StandardCharsets; 37 import java.security.PrivateKey; 38 import java.security.cert.X509Certificate; 39 import java.security.interfaces.ECPublicKey; 40 import java.security.interfaces.RSAPublicKey; 41 import java.security.spec.ECParameterSpec; 42 import java.util.Arrays; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 47 /** 48 * Enterprise configuration details for Wi-Fi. Stores details about the EAP method 49 * and any associated credentials. 50 */ 51 public class WifiEnterpriseConfig implements Parcelable { 52 53 /** Key prefix for WAPI AS certificates. */ 54 public static final String WAPI_AS_CERTIFICATE = "WAPIAS_"; 55 56 /** Key prefix for WAPI user certificates. */ 57 public static final String WAPI_USER_CERTIFICATE = "WAPIUSR_"; 58 59 /** 60 * Intent extra: name for WAPI AS certificates 61 */ 62 public static final String EXTRA_WAPI_AS_CERTIFICATE_NAME = 63 "android.net.wifi.extra.WAPI_AS_CERTIFICATE_NAME"; 64 65 /** 66 * Intent extra: data for WAPI AS certificates 67 */ 68 public static final String EXTRA_WAPI_AS_CERTIFICATE_DATA = 69 "android.net.wifi.extra.WAPI_AS_CERTIFICATE_DATA"; 70 71 /** 72 * Intent extra: name for WAPI USER certificates 73 */ 74 public static final String EXTRA_WAPI_USER_CERTIFICATE_NAME = 75 "android.net.wifi.extra.WAPI_USER_CERTIFICATE_NAME"; 76 77 /** 78 * Intent extra: data for WAPI USER certificates 79 */ 80 public static final String EXTRA_WAPI_USER_CERTIFICATE_DATA = 81 "android.net.wifi.extra.WAPI_USER_CERTIFICATE_DATA"; 82 83 /** @hide */ 84 public static final String EMPTY_VALUE = "NULL"; 85 /** @hide */ 86 public static final String EAP_KEY = "eap"; 87 /** @hide */ 88 public static final String PHASE2_KEY = "phase2"; 89 /** @hide */ 90 public static final String IDENTITY_KEY = "identity"; 91 /** @hide */ 92 public static final String ANON_IDENTITY_KEY = "anonymous_identity"; 93 /** @hide */ 94 public static final String PASSWORD_KEY = "password"; 95 /** @hide */ 96 public static final String SUBJECT_MATCH_KEY = "subject_match"; 97 /** @hide */ 98 public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match"; 99 /** @hide */ 100 public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match"; 101 /** @hide */ 102 public static final String OPP_KEY_CACHING = "proactive_key_caching"; 103 /** @hide */ 104 public static final String EAP_ERP = "eap_erp"; 105 /** @hide */ 106 public static final String OCSP = "ocsp"; 107 /** @hide */ 108 public static final String DECORATED_IDENTITY_PREFIX_KEY = "decorated_username_prefix"; 109 110 /** 111 * String representing the keystore OpenSSL ENGINE's ID. 112 * @hide 113 */ 114 public static final String ENGINE_ID_KEYSTORE = "keystore"; 115 116 /** 117 * String representing the keystore URI used for wpa_supplicant. 118 * @hide 119 */ 120 public static final String KEYSTORE_URI = "keystore://"; 121 122 /** 123 * String representing the keystore URI used for wpa_supplicant, 124 * Unlike #KEYSTORE_URI, this supports a list of space-delimited aliases 125 * @hide 126 */ 127 public static final String KEYSTORES_URI = "keystores://"; 128 129 /** 130 * String representing a SHA-256 certificate hash used for wpa_supplicant. 131 */ 132 private static final String CERT_HASH_PREFIX = "hash://server/sha256/"; 133 134 /** 135 * String to set the engine value to when it should be enabled. 136 * @hide 137 */ 138 public static final String ENGINE_ENABLE = "1"; 139 140 /** 141 * String to set the engine value to when it should be disabled. 142 * @hide 143 */ 144 public static final String ENGINE_DISABLE = "0"; 145 146 /** 147 * Key prefix for CA certificates. 148 * Note: copied from {@link android.security.Credentials#CA_CERTIFICATE} since it is @hide. 149 */ 150 private static final String CA_CERTIFICATE = "CACERT_"; 151 /** 152 * Key prefix for user certificates. 153 * Note: copied from {@link android.security.Credentials#USER_CERTIFICATE} since it is @hide. 154 */ 155 private static final String USER_CERTIFICATE = "USRCERT_"; 156 /** 157 * Key prefix for user private and secret keys. 158 * Note: copied from {@link android.security.Credentials#USER_PRIVATE_KEY} since it is @hide. 159 */ 160 private static final String USER_PRIVATE_KEY = "USRPKEY_"; 161 162 /** @hide */ 163 public static final String CA_CERT_PREFIX = KEYSTORE_URI + CA_CERTIFICATE; 164 /** @hide */ 165 public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + USER_CERTIFICATE; 166 /** @hide */ 167 public static final String CLIENT_CERT_KEY = "client_cert"; 168 /** @hide */ 169 public static final String CA_CERT_KEY = "ca_cert"; 170 /** @hide */ 171 public static final String CA_PATH_KEY = "ca_path"; 172 /** @hide */ 173 public static final String ENGINE_KEY = "engine"; 174 /** @hide */ 175 public static final String ENGINE_ID_KEY = "engine_id"; 176 /** @hide */ 177 public static final String PRIVATE_KEY_ID_KEY = "key_id"; 178 /** @hide */ 179 public static final String REALM_KEY = "realm"; 180 /** @hide */ 181 public static final String PLMN_KEY = "plmn"; 182 /** @hide */ 183 public static final String CA_CERT_ALIAS_DELIMITER = " "; 184 /** @hide */ 185 public static final String WAPI_CERT_SUITE_KEY = "wapi_cert_suite"; 186 187 /** 188 * Do not use OCSP stapling (TLS certificate status extension) 189 * @hide 190 */ 191 @SystemApi 192 public static final int OCSP_NONE = 0; 193 194 /** 195 * Try to use OCSP stapling, but not require response 196 * @hide 197 */ 198 @SystemApi 199 public static final int OCSP_REQUEST_CERT_STATUS = 1; 200 201 /** 202 * Require valid OCSP stapling response 203 * @hide 204 */ 205 @SystemApi 206 public static final int OCSP_REQUIRE_CERT_STATUS = 2; 207 208 /** 209 * Require valid OCSP stapling response for all not-trusted certificates in the server 210 * certificate chain. 211 * @apiNote This option is not supported by most SSL libraries and should not be used. 212 * Specifying this option will most likely cause connection failures. 213 * @hide 214 */ 215 @SystemApi 216 public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3; 217 218 /** @hide */ 219 @IntDef(prefix = {"OCSP_"}, value = { 220 OCSP_NONE, 221 OCSP_REQUEST_CERT_STATUS, 222 OCSP_REQUIRE_CERT_STATUS, 223 OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS 224 }) 225 @Retention(RetentionPolicy.SOURCE) 226 public @interface Ocsp {} 227 228 /** 229 * Whether to use/require OCSP (Online Certificate Status Protocol) to check server certificate. 230 * @hide 231 */ 232 private @Ocsp int mOcsp = OCSP_NONE; 233 234 // Fields to copy verbatim from wpa_supplicant. 235 private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] { 236 IDENTITY_KEY, 237 ANON_IDENTITY_KEY, 238 PASSWORD_KEY, 239 CLIENT_CERT_KEY, 240 CA_CERT_KEY, 241 SUBJECT_MATCH_KEY, 242 ENGINE_KEY, 243 ENGINE_ID_KEY, 244 PRIVATE_KEY_ID_KEY, 245 ALTSUBJECT_MATCH_KEY, 246 DOM_SUFFIX_MATCH_KEY, 247 CA_PATH_KEY 248 }; 249 250 /** 251 * Maximum length of a certificate. 252 */ 253 private static final int CERTIFICATE_MAX_LENGTH = 8192; 254 255 /** 256 * Maximum length of the {@link #mKeyChainAlias} field. 257 */ 258 private static final int KEYCHAIN_ALIAS_MAX_LENGTH = 256; 259 260 /** 261 * Maximum number of elements in a client certificate chain. 262 */ 263 private static final int CLIENT_CERTIFICATE_CHAIN_MAX_ELEMENTS = 5; 264 265 /** 266 * Maximum number of elements in a list of CA certificates. 267 */ 268 private static final int CA_CERTIFICATES_MAX_ELEMENTS = 100; 269 270 /** 271 * Fields that are supported in {@link #mFields}. 272 * Each entry includes the supported field's key and its maximum allowed length. 273 */ 274 private static final Map<String, Integer> SUPPORTED_FIELDS = new HashMap<>() {{ 275 put(ALTSUBJECT_MATCH_KEY, 1024); 276 put(ANON_IDENTITY_KEY, 1024); 277 put(CA_CERT_KEY, CERTIFICATE_MAX_LENGTH); 278 put(CA_PATH_KEY, 4096); 279 put(CLIENT_CERT_KEY, CERTIFICATE_MAX_LENGTH); 280 put(DECORATED_IDENTITY_PREFIX_KEY, 256); 281 put(DOM_SUFFIX_MATCH_KEY, 256); 282 put(EAP_ERP, 1); 283 put(ENGINE_KEY, 1); 284 put(ENGINE_ID_KEY, 64); 285 put(IDENTITY_KEY, 256); 286 put(OPP_KEY_CACHING, 1); 287 put(PASSWORD_KEY, 256); 288 put(PLMN_KEY, 16); 289 put(PRIVATE_KEY_ID_KEY, 256); 290 put(REALM_KEY, 256); 291 put(SUBJECT_MATCH_KEY, 256); 292 put(WAPI_CERT_SUITE_KEY, CERTIFICATE_MAX_LENGTH); 293 }}; 294 295 /** 296 * Fields that have unquoted values in {@link #mFields}. 297 */ 298 private static final List<String> UNQUOTED_KEYS = Arrays.asList(ENGINE_KEY, OPP_KEY_CACHING, 299 EAP_ERP); 300 /** Constant definition for TLS v1.0 which is used in {@link #setMinimumTlsVersion(int)} */ 301 public static final int TLS_V1_0 = 0; 302 303 /** Constant definition for TLS v1.1 which is used in {@link #setMinimumTlsVersion(int)} */ 304 public static final int TLS_V1_1 = 1; 305 306 /** Constant definition for TLS v1.2 which is used in {@link #setMinimumTlsVersion(int)} */ 307 public static final int TLS_V1_2 = 2; 308 309 /** Constant definition for TLS v1.3 which is used in {@link #setMinimumTlsVersion(int)} */ 310 public static final int TLS_V1_3 = 3; 311 312 /** 313 * The minimum valid value for a TLS version. 314 * @hide 315 */ 316 public static final int TLS_VERSION_MIN = TLS_V1_0; 317 318 /** 319 * The maximum valid value for a TLS version. 320 * @hide 321 */ 322 public static final int TLS_VERSION_MAX = TLS_V1_3; 323 324 /** @hide */ 325 @IntDef(prefix = {"TLS_"}, value = { 326 TLS_V1_0, 327 TLS_V1_1, 328 TLS_V1_2, 329 TLS_V1_3 330 }) 331 @Retention(RetentionPolicy.SOURCE) 332 public @interface TlsVersion {} 333 334 /** 335 * TOFU is not enabled for this configuration. 336 * @hide 337 */ 338 public static final int TOFU_STATE_NOT_ENABLED = 0; 339 340 /** 341 * TOFU is enabled pre-connection. 342 * @hide 343 */ 344 public static final int TOFU_STATE_ENABLED_PRE_CONNECTION = 1; 345 346 /** 347 * Root CA was configured post-TOFU connection. 348 * @hide 349 */ 350 351 public static final int TOFU_STATE_CONFIGURE_ROOT_CA = 2; 352 353 /** 354 * Certificate pinning was used post-TOFU connection. 355 * @hide 356 */ 357 public static final int TOFU_STATE_CERT_PINNING = 3; 358 359 /** @hide */ 360 @IntDef(prefix = {"TOFU_STATE_"}, value = { 361 TOFU_STATE_NOT_ENABLED, 362 TOFU_STATE_ENABLED_PRE_CONNECTION, 363 TOFU_STATE_CONFIGURE_ROOT_CA, 364 TOFU_STATE_CERT_PINNING 365 }) 366 @Retention(RetentionPolicy.SOURCE) 367 public @interface TofuConnectionState {} 368 369 /** 370 * TOFU dialog has not been displayed to the user, or state is unknown. 371 * @hide 372 */ 373 public static final int TOFU_DIALOG_STATE_UNSPECIFIED = 0; 374 375 /** 376 * TOFU dialog was rejected by the user. 377 * @hide 378 */ 379 public static final int TOFU_DIALOG_STATE_REJECTED = 1; 380 381 /** 382 * TOFU dialog was accepted by the user. 383 * @hide 384 */ 385 public static final int TOFU_DIALOG_STATE_ACCEPTED = 2; 386 387 /** @hide */ 388 @IntDef(prefix = {"TOFU_DIALOG_STATE_"}, value = { 389 TOFU_DIALOG_STATE_UNSPECIFIED, 390 TOFU_DIALOG_STATE_REJECTED, 391 TOFU_DIALOG_STATE_ACCEPTED 392 }) 393 @Retention(RetentionPolicy.SOURCE) 394 public @interface TofuDialogState {} 395 396 @UnsupportedAppUsage 397 private HashMap<String, String> mFields = new HashMap<String, String>(); 398 private X509Certificate[] mCaCerts; 399 private PrivateKey mClientPrivateKey; 400 private X509Certificate[] mClientCertificateChain; 401 private int mEapMethod = Eap.NONE; 402 private int mPhase2Method = Phase2.NONE; 403 private boolean mIsAppInstalledDeviceKeyAndCert = false; 404 private boolean mIsAppInstalledCaCert = false; 405 private String mKeyChainAlias; 406 private boolean mIsTrustOnFirstUseEnabled = false; 407 private boolean mUserApproveNoCaCert = false; 408 // Default is 1.0, i.e. accept any TLS version. 409 private int mMinimumTlsVersion = TLS_V1_0; 410 private @TofuDialogState int mTofuDialogState = TOFU_DIALOG_STATE_UNSPECIFIED; 411 private @TofuConnectionState int mTofuConnectionState = TOFU_STATE_NOT_ENABLED; 412 413 // Not included in parceling, hashing, or equality because it is an internal, temporary value 414 // which is valid only during an actual connection to a Passpoint network with an RCOI-based 415 // subscription. 416 private long mSelectedRcoi = 0; 417 418 private boolean mIsStrictConservativePeerMode = false; 419 420 private static final String TAG = "WifiEnterpriseConfig"; 421 WifiEnterpriseConfig()422 public WifiEnterpriseConfig() { 423 // Do not set defaults so that the enterprise fields that are not changed 424 // by API are not changed underneath 425 // This is essential because an app may not have all fields like password 426 // available. It allows modification of subset of fields. 427 428 } 429 430 /** 431 * Check whether a key is supported by {@link #mFields}. 432 * @return true if the key is supported, false otherwise. 433 */ isKeySupported(String key)434 private static boolean isKeySupported(String key) { 435 return SUPPORTED_FIELDS.containsKey(key); 436 } 437 438 /** 439 * Check whether a value from {@link #mFields} has a valid length. 440 * @return true if the length is valid, false otherwise. 441 */ isFieldLengthValid(String key, String value)442 private static boolean isFieldLengthValid(String key, String value) { 443 int maxLength = SUPPORTED_FIELDS.getOrDefault(key, 0); 444 return isFieldLengthValid(value, maxLength); 445 } 446 isFieldLengthValid(String value, int maxLength)447 private static boolean isFieldLengthValid(String value, int maxLength) { 448 if (value == null) return true; 449 return value.length() <= maxLength; 450 } 451 452 /** 453 * Check whether a key/value pair from {@link #mFields} is valid. 454 * @return true if the key/value pair is valid, false otherwise. 455 */ isFieldValid(String key, String value)456 private static boolean isFieldValid(String key, String value) { 457 return isKeySupported(key) && isFieldLengthValid(key, value); 458 } 459 460 /** 461 * Convert the {@link #mFields} map to a Bundle for parceling. 462 * Unsupported keys will not be included in the Bundle. 463 */ fieldMapToBundle()464 private Bundle fieldMapToBundle() { 465 Bundle bundle = new Bundle(); 466 for (Map.Entry<String, String> entry : mFields.entrySet()) { 467 if (isFieldValid(entry.getKey(), entry.getValue())) { 468 bundle.putString(entry.getKey(), entry.getValue()); 469 } 470 } 471 return bundle; 472 } 473 474 /** 475 * Convert an unparceled Bundle to the {@link #mFields} map. 476 * Unsupported keys will not be included in the map. 477 */ bundleToFieldMap(Bundle bundle)478 private static HashMap<String, String> bundleToFieldMap(Bundle bundle) { 479 HashMap<String, String> fieldMap = new HashMap<>(); 480 if (bundle == null) return fieldMap; 481 for (String key : bundle.keySet()) { 482 String value = bundle.getString(key); 483 if (isFieldValid(key, value)) { 484 fieldMap.put(key, value); 485 } 486 } 487 return fieldMap; 488 } 489 490 /** 491 * Copy over the contents of the source WifiEnterpriseConfig object over to this object. 492 * 493 * @param source Source WifiEnterpriseConfig object. 494 * @param ignoreMaskedPassword Set to true to ignore masked password field, false otherwise. 495 * @param mask if |ignoreMaskedPassword| is set, check if the incoming password field is set 496 * to this value. 497 */ copyFrom(WifiEnterpriseConfig source, boolean ignoreMaskedPassword, String mask)498 private void copyFrom(WifiEnterpriseConfig source, boolean ignoreMaskedPassword, String mask) { 499 for (String key : source.mFields.keySet()) { 500 String value = source.mFields.get(key); 501 if (ignoreMaskedPassword && key.equals(PASSWORD_KEY) 502 && TextUtils.equals(value, mask)) { 503 continue; 504 } 505 if (isFieldValid(key, value)) { 506 mFields.put(key, source.mFields.get(key)); 507 } 508 } 509 if (source.mCaCerts != null) { 510 mCaCerts = Arrays.copyOf(source.mCaCerts, source.mCaCerts.length); 511 } else { 512 mCaCerts = null; 513 } 514 mClientPrivateKey = source.mClientPrivateKey; 515 if (source.mClientCertificateChain != null) { 516 mClientCertificateChain = Arrays.copyOf( 517 source.mClientCertificateChain, 518 source.mClientCertificateChain.length); 519 } else { 520 mClientCertificateChain = null; 521 } 522 mKeyChainAlias = source.mKeyChainAlias; 523 mEapMethod = source.mEapMethod; 524 mPhase2Method = source.mPhase2Method; 525 mIsAppInstalledDeviceKeyAndCert = source.mIsAppInstalledDeviceKeyAndCert; 526 mIsAppInstalledCaCert = source.mIsAppInstalledCaCert; 527 mOcsp = source.mOcsp; 528 mIsTrustOnFirstUseEnabled = source.mIsTrustOnFirstUseEnabled; 529 mUserApproveNoCaCert = source.mUserApproveNoCaCert; 530 mSelectedRcoi = source.mSelectedRcoi; 531 mMinimumTlsVersion = source.mMinimumTlsVersion; 532 mIsStrictConservativePeerMode = source.mIsStrictConservativePeerMode; 533 mTofuDialogState = source.mTofuDialogState; 534 mTofuConnectionState = source.mTofuConnectionState; 535 } 536 537 /** 538 * Copy constructor. 539 * This copies over all the fields verbatim (does not ignore masked password fields). 540 * 541 * @param source Source WifiEnterpriseConfig object. 542 */ WifiEnterpriseConfig(WifiEnterpriseConfig source)543 public WifiEnterpriseConfig(WifiEnterpriseConfig source) { 544 copyFrom(source, false, ""); 545 } 546 547 /** 548 * Copy fields from the provided external WifiEnterpriseConfig. 549 * This is needed to handle the WifiEnterpriseConfig objects which were sent by apps with the 550 * password field masked. 551 * 552 * @param externalConfig External WifiEnterpriseConfig object. 553 * @param mask String mask to compare against. 554 * @hide 555 */ copyFromExternal(WifiEnterpriseConfig externalConfig, String mask)556 public void copyFromExternal(WifiEnterpriseConfig externalConfig, String mask) { 557 copyFrom(externalConfig, true, convertToQuotedString(mask)); 558 } 559 560 @Override describeContents()561 public int describeContents() { 562 return 0; 563 } 564 565 @Override writeToParcel(Parcel dest, int flags)566 public void writeToParcel(Parcel dest, int flags) { 567 dest.writeBundle(fieldMapToBundle()); 568 dest.writeInt(mEapMethod); 569 dest.writeInt(mPhase2Method); 570 ParcelUtil.writeCertificates(dest, mCaCerts); 571 ParcelUtil.writePrivateKey(dest, mClientPrivateKey); 572 ParcelUtil.writeCertificates(dest, mClientCertificateChain); 573 dest.writeString(mKeyChainAlias); 574 dest.writeBoolean(mIsAppInstalledDeviceKeyAndCert); 575 dest.writeBoolean(mIsAppInstalledCaCert); 576 dest.writeInt(mOcsp); 577 dest.writeBoolean(mIsTrustOnFirstUseEnabled); 578 dest.writeBoolean(mUserApproveNoCaCert); 579 dest.writeInt(mMinimumTlsVersion); 580 dest.writeInt(mTofuDialogState); 581 dest.writeInt(mTofuConnectionState); 582 } 583 584 public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR = 585 new Creator<WifiEnterpriseConfig>() { 586 @Override 587 public WifiEnterpriseConfig createFromParcel(Parcel in) { 588 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 589 enterpriseConfig.mFields = bundleToFieldMap(in.readBundle()); 590 enterpriseConfig.mEapMethod = in.readInt(); 591 enterpriseConfig.mPhase2Method = in.readInt(); 592 593 X509Certificate[] caCerts = ParcelUtil.readCertificates(in); 594 if (caCerts != null && caCerts.length > CA_CERTIFICATES_MAX_ELEMENTS) { 595 Log.e(TAG, "List of CA certificates with size " 596 + caCerts.length + " received during unparceling"); 597 enterpriseConfig.mCaCerts = null; 598 } else { 599 enterpriseConfig.mCaCerts = caCerts; 600 } 601 602 PrivateKey privateKey = ParcelUtil.readPrivateKey(in); 603 if (privateKey != null && privateKey.getEncoded() != null 604 && privateKey.getEncoded().length > CERTIFICATE_MAX_LENGTH) { 605 Log.e(TAG, "Invalid private key with size " 606 + privateKey.getEncoded().length + " received during unparceling"); 607 enterpriseConfig.mClientPrivateKey = null; 608 } else { 609 enterpriseConfig.mClientPrivateKey = privateKey; 610 } 611 612 X509Certificate[] clientCertificateChain = ParcelUtil.readCertificates(in); 613 if (clientCertificateChain != null 614 && clientCertificateChain.length 615 > CLIENT_CERTIFICATE_CHAIN_MAX_ELEMENTS) { 616 Log.e(TAG, "Client certificate chain with size " 617 + clientCertificateChain.length + " received during unparceling"); 618 enterpriseConfig.mClientCertificateChain = null; 619 } else { 620 enterpriseConfig.mClientCertificateChain = clientCertificateChain; 621 } 622 623 String keyChainAlias = in.readString(); 624 enterpriseConfig.mKeyChainAlias = 625 isFieldLengthValid(keyChainAlias, KEYCHAIN_ALIAS_MAX_LENGTH) 626 ? keyChainAlias : ""; 627 enterpriseConfig.mIsAppInstalledDeviceKeyAndCert = in.readBoolean(); 628 enterpriseConfig.mIsAppInstalledCaCert = in.readBoolean(); 629 enterpriseConfig.mOcsp = in.readInt(); 630 enterpriseConfig.mIsTrustOnFirstUseEnabled = in.readBoolean(); 631 enterpriseConfig.mUserApproveNoCaCert = in.readBoolean(); 632 enterpriseConfig.mMinimumTlsVersion = in.readInt(); 633 enterpriseConfig.mTofuDialogState = in.readInt(); 634 enterpriseConfig.mTofuConnectionState = in.readInt(); 635 return enterpriseConfig; 636 } 637 638 @Override 639 public WifiEnterpriseConfig[] newArray(int size) { 640 return new WifiEnterpriseConfig[size]; 641 } 642 }; 643 644 /** The Extensible Authentication Protocol method used */ 645 public static final class Eap { 646 /** No EAP method used. Represents an empty config */ 647 public static final int NONE = -1; 648 /** Protected EAP */ 649 public static final int PEAP = 0; 650 /** EAP-Transport Layer Security */ 651 public static final int TLS = 1; 652 /** EAP-Tunneled Transport Layer Security */ 653 public static final int TTLS = 2; 654 /** EAP-Password */ 655 public static final int PWD = 3; 656 /** EAP-Subscriber Identity Module [RFC-4186] */ 657 public static final int SIM = 4; 658 /** EAP-Authentication and Key Agreement [RFC-4187] */ 659 public static final int AKA = 5; 660 /** EAP-Authentication and Key Agreement Prime [RFC-5448] */ 661 public static final int AKA_PRIME = 6; 662 /** Hotspot 2.0 r2 OSEN */ 663 public static final int UNAUTH_TLS = 7; 664 /** WAPI Certificate */ 665 public static final int WAPI_CERT = 8; 666 /** @hide */ 667 public static final String[] strings = 668 { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'", "WFA-UNAUTH-TLS", 669 "WAPI_CERT" }; 670 671 /** Prevent initialization */ Eap()672 private Eap() {} 673 } 674 675 /** The inner authentication method used */ 676 public static final class Phase2 { 677 public static final int NONE = 0; 678 /** Password Authentication Protocol */ 679 public static final int PAP = 1; 680 /** Microsoft Challenge Handshake Authentication Protocol */ 681 public static final int MSCHAP = 2; 682 /** Microsoft Challenge Handshake Authentication Protocol v2 */ 683 public static final int MSCHAPV2 = 3; 684 /** Generic Token Card */ 685 public static final int GTC = 4; 686 /** EAP-Subscriber Identity Module [RFC-4186] */ 687 public static final int SIM = 5; 688 /** EAP-Authentication and Key Agreement [RFC-4187] */ 689 public static final int AKA = 6; 690 /** EAP-Authentication and Key Agreement Prime [RFC-5448] */ 691 public static final int AKA_PRIME = 7; 692 private static final String AUTH_PREFIX = "auth="; 693 private static final String AUTHEAP_PREFIX = "autheap="; 694 /** @hide */ 695 public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", 696 "MSCHAPV2", "GTC", "SIM", "AKA", "AKA'" }; 697 698 /** Prevent initialization */ Phase2()699 private Phase2() {} 700 } 701 702 // Loader and saver interfaces for exchanging data with wpa_supplicant. 703 // TODO: Decouple this object (which is just a placeholder of the configuration) 704 // from the implementation that knows what wpa_supplicant wants. 705 /** 706 * Interface used for retrieving supplicant configuration from WifiEnterpriseConfig 707 * @hide 708 */ 709 public interface SupplicantSaver { 710 /** 711 * Set a value within wpa_supplicant configuration 712 * @param key index to set within wpa_supplciant 713 * @param value the value for the key 714 * @return true if successful; false otherwise 715 */ saveValue(String key, String value)716 boolean saveValue(String key, String value); 717 } 718 719 /** 720 * Interface used for populating a WifiEnterpriseConfig from supplicant configuration 721 * @hide 722 */ 723 public interface SupplicantLoader { 724 /** 725 * Returns a value within wpa_supplicant configuration 726 * @param key index to set within wpa_supplciant 727 * @return string value if successful; null otherwise 728 */ loadValue(String key)729 String loadValue(String key); 730 } 731 732 /** 733 * Internal use only; supply field values to wpa_supplicant config. The configuration 734 * process aborts on the first failed call on {@code saver}. 735 * @param saver proxy for setting configuration in wpa_supplciant 736 * @return whether the save succeeded on all attempts 737 * @hide 738 */ saveToSupplicant(SupplicantSaver saver)739 public boolean saveToSupplicant(SupplicantSaver saver) { 740 if (!isEapMethodValid()) { 741 return false; 742 } 743 744 // wpa_supplicant can update the anonymous identity for these kinds of networks after 745 // framework reads them, so make sure the framework doesn't try to overwrite them. 746 boolean shouldNotWriteAnonIdentity = mEapMethod == WifiEnterpriseConfig.Eap.SIM 747 || mEapMethod == WifiEnterpriseConfig.Eap.AKA 748 || mEapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME; 749 for (String key : mFields.keySet()) { 750 String value = mFields.get(key); 751 if (!isFieldValid(key, value)) { 752 continue; 753 } 754 if (shouldNotWriteAnonIdentity && ANON_IDENTITY_KEY.equals(key)) { 755 continue; 756 } 757 if (!saver.saveValue(key, value)) { 758 return false; 759 } 760 } 761 762 if (!saver.saveValue(EAP_KEY, Eap.strings[mEapMethod])) { 763 return false; 764 } 765 766 if (mEapMethod != Eap.TLS && mEapMethod != Eap.UNAUTH_TLS && mPhase2Method != Phase2.NONE) { 767 boolean is_autheap = mEapMethod == Eap.TTLS && mPhase2Method == Phase2.GTC; 768 String prefix = is_autheap ? Phase2.AUTHEAP_PREFIX : Phase2.AUTH_PREFIX; 769 String value = convertToQuotedString(prefix + Phase2.strings[mPhase2Method]); 770 return saver.saveValue(PHASE2_KEY, value); 771 } else if (mPhase2Method == Phase2.NONE) { 772 // By default, send a null phase 2 to clear old configuration values. 773 return saver.saveValue(PHASE2_KEY, null); 774 } else { 775 Log.e(TAG, "WiFi enterprise configuration is invalid as it supplies a " 776 + "phase 2 method but the phase1 method does not support it."); 777 return false; 778 } 779 } 780 781 /** 782 * Internal use only; retrieve configuration from wpa_supplicant config. 783 * @param loader proxy for retrieving configuration keys from wpa_supplicant 784 * @hide 785 */ loadFromSupplicant(SupplicantLoader loader)786 public void loadFromSupplicant(SupplicantLoader loader) { 787 for (String key : SUPPLICANT_CONFIG_KEYS) { 788 String value = loader.loadValue(key); 789 if (!isFieldValid(key, value)) { 790 continue; 791 } else if (value == null) { 792 mFields.put(key, EMPTY_VALUE); 793 } else { 794 mFields.put(key, value); 795 } 796 } 797 String eapMethod = loader.loadValue(EAP_KEY); 798 mEapMethod = getStringIndex(Eap.strings, eapMethod, Eap.NONE); 799 800 String phase2Method = removeDoubleQuotes(loader.loadValue(PHASE2_KEY)); 801 // Remove "auth=" or "autheap=" prefix. 802 if (phase2Method.startsWith(Phase2.AUTH_PREFIX)) { 803 phase2Method = phase2Method.substring(Phase2.AUTH_PREFIX.length()); 804 } else if (phase2Method.startsWith(Phase2.AUTHEAP_PREFIX)) { 805 phase2Method = phase2Method.substring(Phase2.AUTHEAP_PREFIX.length()); 806 } 807 mPhase2Method = getStringIndex(Phase2.strings, phase2Method, Phase2.NONE); 808 } 809 810 /** 811 * Set the EAP authentication method. 812 * @param eapMethod is one of {@link Eap}, except for {@link Eap#NONE} 813 * @throws IllegalArgumentException on an invalid eap method 814 */ setEapMethod(int eapMethod)815 public void setEapMethod(int eapMethod) { 816 switch (eapMethod) { 817 /** Valid methods */ 818 case Eap.WAPI_CERT: 819 mEapMethod = eapMethod; 820 setPhase2Method(Phase2.NONE); 821 break; 822 case Eap.TLS: 823 case Eap.UNAUTH_TLS: 824 setPhase2Method(Phase2.NONE); 825 /* fall through */ 826 case Eap.PEAP: 827 case Eap.PWD: 828 case Eap.TTLS: 829 case Eap.SIM: 830 case Eap.AKA: 831 case Eap.AKA_PRIME: 832 mEapMethod = eapMethod; 833 setFieldValue(OPP_KEY_CACHING, "1"); 834 break; 835 default: 836 throw new IllegalArgumentException("Unknown EAP method"); 837 } 838 } 839 840 /** 841 * Get the eap method. 842 * @return eap method configured 843 */ getEapMethod()844 public int getEapMethod() { 845 return mEapMethod; 846 } 847 848 /** 849 * Set Phase 2 authentication method. Sets the inner authentication method to be used in 850 * phase 2 after setting up a secure channel 851 * @param phase2Method is the inner authentication method and can be one of {@link Phase2} 852 * @throws IllegalArgumentException on an invalid phase2 method 853 */ setPhase2Method(int phase2Method)854 public void setPhase2Method(int phase2Method) { 855 switch (phase2Method) { 856 case Phase2.NONE: 857 case Phase2.PAP: 858 case Phase2.MSCHAP: 859 case Phase2.MSCHAPV2: 860 case Phase2.GTC: 861 case Phase2.SIM: 862 case Phase2.AKA: 863 case Phase2.AKA_PRIME: 864 mPhase2Method = phase2Method; 865 break; 866 default: 867 throw new IllegalArgumentException("Unknown Phase 2 method"); 868 } 869 } 870 871 /** 872 * Get the phase 2 authentication method. 873 * @return a phase 2 method defined at {@link Phase2} 874 * */ getPhase2Method()875 public int getPhase2Method() { 876 return mPhase2Method; 877 } 878 879 /** 880 * Set the identity 881 * @param identity 882 */ setIdentity(String identity)883 public void setIdentity(String identity) { 884 setFieldValue(IDENTITY_KEY, identity, ""); 885 } 886 887 /** 888 * Get the identity 889 * @return the identity 890 */ getIdentity()891 public String getIdentity() { 892 return getFieldValue(IDENTITY_KEY); 893 } 894 895 /** 896 * Set anonymous identity. This is used as the unencrypted identity with 897 * certain EAP types 898 * @param anonymousIdentity the anonymous identity 899 */ setAnonymousIdentity(String anonymousIdentity)900 public void setAnonymousIdentity(String anonymousIdentity) { 901 setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity); 902 } 903 904 /** 905 * Get the anonymous identity 906 * @return anonymous identity 907 */ getAnonymousIdentity()908 public String getAnonymousIdentity() { 909 return getFieldValue(ANON_IDENTITY_KEY); 910 } 911 912 /** 913 * Set the password. 914 * @param password the password 915 */ setPassword(String password)916 public void setPassword(String password) { 917 setFieldValue(PASSWORD_KEY, password); 918 } 919 920 /** 921 * Get the password. 922 * 923 * Returns locally set password value. For networks fetched from 924 * framework, returns "*". 925 */ getPassword()926 public String getPassword() { 927 return getFieldValue(PASSWORD_KEY); 928 } 929 930 /** 931 * Encode a CA certificate alias so it does not contain illegal character. 932 * @hide 933 */ encodeCaCertificateAlias(String alias)934 public static String encodeCaCertificateAlias(String alias) { 935 byte[] bytes = alias.getBytes(StandardCharsets.UTF_8); 936 StringBuilder sb = new StringBuilder(bytes.length * 2); 937 for (byte o : bytes) { 938 sb.append(String.format("%02x", o & 0xFF)); 939 } 940 return sb.toString(); 941 } 942 943 /** 944 * Decode a previously-encoded CA certificate alias. 945 * @hide 946 */ decodeCaCertificateAlias(String alias)947 public static String decodeCaCertificateAlias(String alias) { 948 byte[] data = new byte[alias.length() >> 1]; 949 for (int n = 0, position = 0; n < alias.length(); n += 2, position++) { 950 data[position] = (byte) Integer.parseInt(alias.substring(n, n + 2), 16); 951 } 952 try { 953 return new String(data, StandardCharsets.UTF_8); 954 } catch (NumberFormatException e) { 955 e.printStackTrace(); 956 return alias; 957 } 958 } 959 960 /** 961 * Set a server certificate hash instead of a CA certificate for a TOFU connection 962 * 963 * @param certHash Server certificate hash to match against in subsequent connections 964 * @hide 965 */ setServerCertificateHash(String certHash)966 public void setServerCertificateHash(String certHash) { 967 setFieldValue(CA_CERT_KEY, certHash, CERT_HASH_PREFIX); 968 } 969 970 /** 971 * Set CA certificate alias. 972 * 973 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 974 * a certificate 975 * </p> 976 * @param alias identifies the certificate 977 * @hide 978 */ 979 @UnsupportedAppUsage setCaCertificateAlias(String alias)980 public void setCaCertificateAlias(String alias) { 981 setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); 982 } 983 984 /** 985 * Set CA certificate aliases. When creating installing the corresponding certificate to 986 * the keystore, please use alias encoded by {@link #encodeCaCertificateAlias(String)}. 987 * 988 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 989 * a certificate. 990 * </p> 991 * @param aliases identifies the certificate. Can be null to indicate the absence of a 992 * certificate. 993 * @hide 994 */ 995 @SystemApi setCaCertificateAliases(@ullable String[] aliases)996 public void setCaCertificateAliases(@Nullable String[] aliases) { 997 if (aliases == null) { 998 setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX); 999 } else if (aliases.length == 1) { 1000 // Backwards compatibility: use the original cert prefix if setting only one alias. 1001 setCaCertificateAlias(aliases[0]); 1002 } else { 1003 // Use KEYSTORES_URI which supports multiple aliases. 1004 StringBuilder sb = new StringBuilder(); 1005 for (int i = 0; i < aliases.length; i++) { 1006 if (i > 0) { 1007 sb.append(CA_CERT_ALIAS_DELIMITER); 1008 } 1009 sb.append(encodeCaCertificateAlias(CA_CERTIFICATE + aliases[i])); 1010 } 1011 setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI); 1012 } 1013 } 1014 1015 /** 1016 * Indicates whether or not this enterprise config has a CA certificate configured. 1017 */ hasCaCertificate()1018 public boolean hasCaCertificate() { 1019 if (getCaCertificateAliases() != null) return true; 1020 if (getCaCertificates() != null) return true; 1021 if (!TextUtils.isEmpty(getCaPath())) return true; 1022 return false; 1023 } 1024 1025 /** 1026 * Get CA certificate alias 1027 * @return alias to the CA certificate 1028 * @hide 1029 */ 1030 @UnsupportedAppUsage getCaCertificateAlias()1031 public String getCaCertificateAlias() { 1032 return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); 1033 } 1034 1035 /** 1036 * Get CA certificate aliases. 1037 * @return alias to the CA certificate, or null if unset. 1038 * @hide 1039 */ 1040 @Nullable 1041 @SystemApi getCaCertificateAliases()1042 public String[] getCaCertificateAliases() { 1043 String value = getFieldValue(CA_CERT_KEY); 1044 if (value.startsWith(CA_CERT_PREFIX)) { 1045 // Backwards compatibility: parse the original alias prefix. 1046 return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)}; 1047 } else if (value.startsWith(KEYSTORES_URI)) { 1048 String values = value.substring(KEYSTORES_URI.length()); 1049 1050 String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER); 1051 for (int i = 0; i < aliases.length; i++) { 1052 aliases[i] = decodeCaCertificateAlias(aliases[i]); 1053 if (aliases[i].startsWith(CA_CERTIFICATE)) { 1054 aliases[i] = aliases[i].substring(CA_CERTIFICATE.length()); 1055 } 1056 } 1057 return aliases.length != 0 ? aliases : null; 1058 } else { 1059 return TextUtils.isEmpty(value) ? null : new String[] {value}; 1060 } 1061 } 1062 1063 /** 1064 * Specify a X.509 certificate that identifies the server. 1065 * 1066 * <p>A default name is automatically assigned to the certificate and used 1067 * with this configuration. The framework takes care of installing the 1068 * certificate when the config is saved and removing the certificate when 1069 * the config is removed. 1070 * 1071 * Note: If no certificate is set for an Enterprise configuration, either by not calling this 1072 * API (or the {@link #setCaCertificates(X509Certificate[])}, or by calling it with null, then 1073 * the server certificate validation is skipped - which means that the connection is not secure. 1074 * 1075 * @param cert X.509 CA certificate 1076 * @throws IllegalArgumentException if not a CA certificate 1077 */ setCaCertificate(@ullable X509Certificate cert)1078 public void setCaCertificate(@Nullable X509Certificate cert) { 1079 if (cert != null) { 1080 if (cert.getBasicConstraints() >= 0) { 1081 mIsAppInstalledCaCert = true; 1082 mCaCerts = new X509Certificate[] {cert}; 1083 } else { 1084 mCaCerts = null; 1085 throw new IllegalArgumentException("Not a CA certificate"); 1086 } 1087 } else { 1088 mCaCerts = null; 1089 } 1090 } 1091 1092 /** 1093 * Specify a X.509 certificate that identifies the server. 1094 * 1095 * This hidden API allows setting self-signed certificate for Trust on First Use. 1096 * 1097 * @param cert X.509 CA certificate 1098 * @throws IllegalArgumentException if Trust on First Use is not enabled. 1099 * @hide 1100 */ setCaCertificateForTrustOnFirstUse(@ullable X509Certificate cert)1101 public void setCaCertificateForTrustOnFirstUse(@Nullable X509Certificate cert) { 1102 if (cert != null) { 1103 if (isTrustOnFirstUseEnabled()) { 1104 mIsAppInstalledCaCert = true; 1105 mCaCerts = new X509Certificate[] {cert}; 1106 } else { 1107 mCaCerts = null; 1108 throw new IllegalArgumentException("Trust on First Use is not enabled."); 1109 } 1110 } else { 1111 mCaCerts = null; 1112 } 1113 } 1114 1115 /** 1116 * Get CA certificate. If multiple CA certificates are configured previously, 1117 * return the first one. 1118 * @return X.509 CA certificate 1119 */ getCaCertificate()1120 @Nullable public X509Certificate getCaCertificate() { 1121 if (mCaCerts != null && mCaCerts.length > 0) { 1122 return mCaCerts[0]; 1123 } else { 1124 return null; 1125 } 1126 } 1127 1128 /** 1129 * Specify a list of X.509 certificates that identifies the server. The validation 1130 * passes if the CA of server certificate matches one of the given certificates. 1131 1132 * <p>Default names are automatically assigned to the certificates and used 1133 * with this configuration. The framework takes care of installing the 1134 * certificates when the config is saved and removing the certificates when 1135 * the config is removed. 1136 * 1137 * Note: If no certificates are set for an Enterprise configuration, either by not calling this 1138 * API (or the {@link #setCaCertificate(X509Certificate)}, or by calling it with null, then the 1139 * server certificate validation is skipped - which means that the 1140 * connection is not secure. 1141 * 1142 * @param certs X.509 CA certificates 1143 * @throws IllegalArgumentException if any of the provided certificates is 1144 * not a CA certificate, or if too many CA certificates are provided 1145 */ setCaCertificates(@ullable X509Certificate[] certs)1146 public void setCaCertificates(@Nullable X509Certificate[] certs) { 1147 if (certs != null) { 1148 if (certs.length > CA_CERTIFICATES_MAX_ELEMENTS) { 1149 mCaCerts = null; 1150 throw new IllegalArgumentException("List of CA certificates contains more " 1151 + "than the allowed number of elements"); 1152 } 1153 X509Certificate[] newCerts = new X509Certificate[certs.length]; 1154 for (int i = 0; i < certs.length; i++) { 1155 if (certs[i].getBasicConstraints() >= 0) { 1156 newCerts[i] = certs[i]; 1157 } else { 1158 mCaCerts = null; 1159 throw new IllegalArgumentException("Not a CA certificate"); 1160 } 1161 } 1162 mCaCerts = newCerts; 1163 mIsAppInstalledCaCert = true; 1164 } else { 1165 mCaCerts = null; 1166 } 1167 } 1168 1169 /** 1170 * Get CA certificates. 1171 */ getCaCertificates()1172 @Nullable public X509Certificate[] getCaCertificates() { 1173 if (mCaCerts != null && mCaCerts.length > 0) { 1174 return mCaCerts; 1175 } else { 1176 return null; 1177 } 1178 } 1179 1180 /** 1181 * @hide 1182 */ resetCaCertificate()1183 public void resetCaCertificate() { 1184 mCaCerts = null; 1185 } 1186 1187 /** 1188 * Set the ca_path directive on wpa_supplicant. 1189 * 1190 * From wpa_supplicant documentation: 1191 * 1192 * Directory path for CA certificate files (PEM). This path may contain 1193 * multiple CA certificates in OpenSSL format. Common use for this is to 1194 * point to system trusted CA list which is often installed into directory 1195 * like /etc/ssl/certs. If configured, these certificates are added to the 1196 * list of trusted CAs. ca_cert may also be included in that case, but it is 1197 * not required. 1198 * 1199 * Note: If no certificate path is set for an Enterprise configuration, either by not calling 1200 * this API, or by calling it with null, and no certificate is set by 1201 * {@link #setCaCertificate(X509Certificate)} or {@link #setCaCertificates(X509Certificate[])}, 1202 * then the server certificate validation is skipped - which means that the connection is not 1203 * secure. 1204 * 1205 * @param path The path for CA certificate files, or empty string to clear. 1206 * @hide 1207 */ 1208 @SystemApi setCaPath(@onNull String path)1209 public void setCaPath(@NonNull String path) { 1210 setFieldValue(CA_PATH_KEY, path); 1211 } 1212 1213 /** 1214 * Get the ca_path directive from wpa_supplicant. 1215 * @return The path for CA certificate files, or an empty string if unset. 1216 * @hide 1217 */ 1218 @NonNull 1219 @SystemApi getCaPath()1220 public String getCaPath() { 1221 return getFieldValue(CA_PATH_KEY); 1222 } 1223 1224 /** 1225 * Set Client certificate alias. 1226 * 1227 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 1228 * a certificate 1229 * </p> 1230 * @param alias identifies the certificate, or empty string to clear. 1231 * @hide 1232 */ 1233 @SystemApi setClientCertificateAlias(@onNull String alias)1234 public void setClientCertificateAlias(@NonNull String alias) { 1235 setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); 1236 setFieldValue(PRIVATE_KEY_ID_KEY, alias, USER_PRIVATE_KEY); 1237 // Also, set engine parameters 1238 if (TextUtils.isEmpty(alias)) { 1239 setFieldValue(ENGINE_KEY, ENGINE_DISABLE); 1240 setFieldValue(ENGINE_ID_KEY, ""); 1241 } else { 1242 setFieldValue(ENGINE_KEY, ENGINE_ENABLE); 1243 setFieldValue(ENGINE_ID_KEY, ENGINE_ID_KEYSTORE); 1244 } 1245 } 1246 1247 /** 1248 * Get client certificate alias. 1249 * @return alias to the client certificate, or an empty string if unset. 1250 * @hide 1251 */ 1252 @NonNull 1253 @SystemApi getClientCertificateAlias()1254 public String getClientCertificateAlias() { 1255 return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); 1256 } 1257 1258 /** 1259 * Specify a private key and client certificate for client authorization. 1260 * 1261 * <p>A default name is automatically assigned to the key entry and used 1262 * with this configuration. The framework takes care of installing the 1263 * key entry when the config is saved and removing the key entry when 1264 * the config is removed. 1265 1266 * @param privateKey a PrivateKey instance for the end certificate. 1267 * @param clientCertificate an X509Certificate representing the end certificate. 1268 * @throws IllegalArgumentException for an invalid key or certificate. 1269 */ setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate)1270 public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { 1271 X509Certificate[] clientCertificates = null; 1272 if (clientCertificate != null) { 1273 clientCertificates = new X509Certificate[] {clientCertificate}; 1274 } 1275 setClientKeyEntryWithCertificateChain(privateKey, clientCertificates); 1276 } 1277 1278 /** 1279 * Specify a private key and client certificate chain for client authorization. 1280 * 1281 * <p>A default name is automatically assigned to the key entry and used 1282 * with this configuration. The framework takes care of installing the 1283 * key entry when the config is saved and removing the key entry when 1284 * the config is removed. 1285 * 1286 * @param privateKey a PrivateKey instance for the end certificate. 1287 * @param clientCertificateChain an array of X509Certificate instances which starts with 1288 * end certificate and continues with additional CA certificates necessary to 1289 * link the end certificate with some root certificate known by the authenticator. 1290 * @throws IllegalArgumentException for an invalid key or certificate. 1291 */ setClientKeyEntryWithCertificateChain(PrivateKey privateKey, X509Certificate[] clientCertificateChain)1292 public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey, 1293 X509Certificate[] clientCertificateChain) { 1294 X509Certificate[] newCerts = null; 1295 if (clientCertificateChain != null && clientCertificateChain.length > 0) { 1296 // We validate that this is a well formed chain that starts 1297 // with an end-certificate and is followed by CA certificates. 1298 // We don't validate that each following certificate verifies 1299 // the previous. https://en.wikipedia.org/wiki/Chain_of_trust 1300 // 1301 // Basic constraints is an X.509 extension type that defines 1302 // whether a given certificate is allowed to sign additional 1303 // certificates and what path length restrictions may exist. 1304 // We use this to judge whether the certificate is an end 1305 // certificate or a CA certificate. 1306 // https://cryptography.io/en/latest/x509/reference/ 1307 if (clientCertificateChain.length > CLIENT_CERTIFICATE_CHAIN_MAX_ELEMENTS) { 1308 throw new IllegalArgumentException( 1309 "Certificate chain contains more than the allowed number of elements"); 1310 } 1311 if (clientCertificateChain[0].getBasicConstraints() != -1) { 1312 throw new IllegalArgumentException( 1313 "First certificate in the chain must be a client end certificate"); 1314 } 1315 1316 for (int i = 1; i < clientCertificateChain.length; i++) { 1317 if (clientCertificateChain[i].getBasicConstraints() == -1) { 1318 throw new IllegalArgumentException( 1319 "All certificates following the first must be CA certificates"); 1320 } 1321 } 1322 newCerts = Arrays.copyOf(clientCertificateChain, 1323 clientCertificateChain.length); 1324 1325 if (privateKey == null) { 1326 throw new IllegalArgumentException("Client cert without a private key"); 1327 } 1328 byte[] encodedKey = privateKey.getEncoded(); 1329 if (encodedKey == null) { 1330 throw new IllegalArgumentException("Private key cannot be encoded"); 1331 } 1332 if (encodedKey.length > CERTIFICATE_MAX_LENGTH) { 1333 throw new IllegalArgumentException( 1334 "Private key exceeds the maximum allowed length"); 1335 } 1336 } 1337 1338 mClientPrivateKey = privateKey; 1339 mClientCertificateChain = newCerts; 1340 mIsAppInstalledDeviceKeyAndCert = true; 1341 } 1342 1343 /** 1344 * Specify a key pair via KeyChain alias for client authentication. 1345 * 1346 * The alias should refer to a key pair in KeyChain that is allowed for WiFi authentication. 1347 * 1348 * @param alias key pair alias 1349 * @see android.app.admin.DevicePolicyManager#grantKeyPairToWifiAuth(String) 1350 */ 1351 @RequiresApi(Build.VERSION_CODES.S) setClientKeyPairAlias(@onNull String alias)1352 public void setClientKeyPairAlias(@NonNull String alias) { 1353 if (!SdkLevel.isAtLeastS()) { 1354 throw new UnsupportedOperationException(); 1355 } 1356 if (!isFieldLengthValid(alias, KEYCHAIN_ALIAS_MAX_LENGTH)) { 1357 throw new IllegalArgumentException(); 1358 } 1359 mKeyChainAlias = alias; 1360 } 1361 1362 /** 1363 * Get KeyChain alias to use for client authentication. 1364 */ 1365 @RequiresApi(Build.VERSION_CODES.S) getClientKeyPairAlias()1366 public @Nullable String getClientKeyPairAlias() { 1367 if (!SdkLevel.isAtLeastS()) { 1368 throw new UnsupportedOperationException(); 1369 } 1370 return mKeyChainAlias; 1371 } 1372 1373 /** 1374 * Get KeyChain alias to use for client authentication without SDK check. 1375 * @hide 1376 */ getClientKeyPairAliasInternal()1377 public @Nullable String getClientKeyPairAliasInternal() { 1378 return mKeyChainAlias; 1379 } 1380 1381 /** 1382 * Get client certificate 1383 * 1384 * @return X.509 client certificate 1385 */ getClientCertificate()1386 public X509Certificate getClientCertificate() { 1387 if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { 1388 return mClientCertificateChain[0]; 1389 } else { 1390 return null; 1391 } 1392 } 1393 1394 /** 1395 * Get the complete client certificate chain in the same order as it was last supplied. 1396 * 1397 * <p>If the chain was last supplied by a call to 1398 * {@link #setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate)} 1399 * with a non-null * certificate instance, a single-element array containing the certificate 1400 * will be * returned. If {@link #setClientKeyEntryWithCertificateChain( 1401 * java.security.PrivateKey, java.security.cert.X509Certificate[])} was last called with a 1402 * non-empty array, this array will be returned in the same order as it was supplied. 1403 * Otherwise, {@code null} will be returned. 1404 * 1405 * @return X.509 client certificates 1406 */ getClientCertificateChain()1407 @Nullable public X509Certificate[] getClientCertificateChain() { 1408 if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { 1409 return mClientCertificateChain; 1410 } else { 1411 return null; 1412 } 1413 } 1414 1415 /** 1416 * @hide 1417 */ resetClientKeyEntry()1418 public void resetClientKeyEntry() { 1419 mClientPrivateKey = null; 1420 mClientCertificateChain = null; 1421 } 1422 1423 /** 1424 * Get the client private key as supplied in {@link #setClientKeyEntryWithCertificateChain}, or 1425 * null if unset. 1426 */ 1427 @Nullable getClientPrivateKey()1428 public PrivateKey getClientPrivateKey() { 1429 return mClientPrivateKey; 1430 } 1431 1432 /** 1433 * Set subject match (deprecated). This is the substring to be matched against the subject of 1434 * the authentication server certificate. 1435 * @param subjectMatch substring to be matched 1436 * @deprecated in favor of altSubjectMatch 1437 */ setSubjectMatch(String subjectMatch)1438 public void setSubjectMatch(String subjectMatch) { 1439 setFieldValue(SUBJECT_MATCH_KEY, subjectMatch); 1440 } 1441 1442 /** 1443 * Get subject match (deprecated) 1444 * @return the subject match string 1445 * @deprecated in favor of altSubjectMatch 1446 */ getSubjectMatch()1447 public String getSubjectMatch() { 1448 return getFieldValue(SUBJECT_MATCH_KEY); 1449 } 1450 1451 /** 1452 * Set alternate subject match. This is the substring to be matched against the 1453 * alternate subject of the authentication server certificate. 1454 * 1455 * Note: If no alternate subject is set for an Enterprise configuration, either by not calling 1456 * this API, or by calling it with null, or not setting domain suffix match using the 1457 * {@link #setDomainSuffixMatch(String)}, then the server certificate validation is incomplete - 1458 * which means that the connection is not secure. 1459 * 1460 * @param altSubjectMatch substring to be matched, for example 1461 * DNS:server.example.com;EMAIL:server@example.com 1462 */ setAltSubjectMatch(String altSubjectMatch)1463 public void setAltSubjectMatch(String altSubjectMatch) { 1464 setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch); 1465 } 1466 1467 /** 1468 * Get alternate subject match 1469 * @return the alternate subject match string 1470 */ getAltSubjectMatch()1471 public String getAltSubjectMatch() { 1472 return getFieldValue(ALTSUBJECT_MATCH_KEY); 1473 } 1474 1475 /** 1476 * Set the domain_suffix_match directive on wpa_supplicant. This is the parameter to use 1477 * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2, 1478 * second paragraph. 1479 * 1480 * <p>From wpa_supplicant documentation: 1481 * <p>Constraint for server domain name. If set, this FQDN is used as a suffix match requirement 1482 * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is 1483 * found, this constraint is met. 1484 * <p>Suffix match here means that the host/domain name is compared one label at a time starting 1485 * from the top-level domain and all the labels in domain_suffix_match shall be included in the 1486 * certificate. The certificate may include additional sub-level labels in addition to the 1487 * required labels. 1488 * <p>More than one match string can be provided by using semicolons to separate the strings 1489 * (e.g., example.org;example.com). When multiple strings are specified, a match with any one of 1490 * the values is considered a sufficient match for the certificate, i.e., the conditions are 1491 * ORed ogether. 1492 * <p>For example, domain_suffix_match=example.com would match test.example.com but would not 1493 * match test-example.com. 1494 * 1495 * Note: If no domain suffix is set for an Enterprise configuration, either by not calling this 1496 * API, or by calling it with null, or not setting alternate subject match using the 1497 * {@link #setAltSubjectMatch(String)}, then the server certificate 1498 * validation is incomplete - which means that the connection is not secure. 1499 * 1500 * @param domain The domain value 1501 */ setDomainSuffixMatch(String domain)1502 public void setDomainSuffixMatch(String domain) { 1503 setFieldValue(DOM_SUFFIX_MATCH_KEY, domain); 1504 } 1505 1506 /** 1507 * Get the domain_suffix_match value. See setDomSuffixMatch. 1508 * @return The domain value. 1509 */ getDomainSuffixMatch()1510 public String getDomainSuffixMatch() { 1511 return getFieldValue(DOM_SUFFIX_MATCH_KEY); 1512 } 1513 1514 /** 1515 * Set realm for Passpoint credential; realm identifies a set of networks where your 1516 * Passpoint credential can be used 1517 * @param realm the realm 1518 */ setRealm(String realm)1519 public void setRealm(String realm) { 1520 setFieldValue(REALM_KEY, realm); 1521 } 1522 1523 /** 1524 * Get realm for Passpoint credential; see {@link #setRealm(String)} for more information 1525 * @return the realm 1526 */ getRealm()1527 public String getRealm() { 1528 return getFieldValue(REALM_KEY); 1529 } 1530 1531 /** 1532 * Set selected RCOI for Passpoint: Indicates which RCOI was selected on a particular network 1533 * @param selectedRcoi the selected RCOI on a particular network 1534 * @hide 1535 */ setSelectedRcoi(long selectedRcoi)1536 public void setSelectedRcoi(long selectedRcoi) { 1537 mSelectedRcoi = selectedRcoi; 1538 } 1539 1540 /** 1541 * Get the selected RCOI matched for a Passpoint connection 1542 * @return the selected RCOI 1543 * @hide 1544 */ getSelectedRcoi()1545 public long getSelectedRcoi() { 1546 return mSelectedRcoi; 1547 } 1548 1549 /** 1550 * Enable or disable the conservative peer mode, this is only meaningful for 1551 * EAP-SIM/AKA/AKA' 1552 * @param enable true if the conservative peer mode is enabled. 1553 * @hide 1554 */ setStrictConservativePeerMode(boolean enable)1555 public void setStrictConservativePeerMode(boolean enable) { 1556 mIsStrictConservativePeerMode = enable; 1557 } 1558 1559 /** 1560 * Check if the conservative peer mode is enabled or not, this is only meaningful for 1561 * EAP-SIM/AKA/AKA' 1562 * @hide 1563 */ getStrictConservativePeerMode()1564 public boolean getStrictConservativePeerMode() { 1565 return mIsStrictConservativePeerMode; 1566 } 1567 1568 /** 1569 * Set plmn (Public Land Mobile Network) of the provider of Passpoint credential 1570 * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code) 1571 */ setPlmn(String plmn)1572 public void setPlmn(String plmn) { 1573 setFieldValue(PLMN_KEY, plmn); 1574 } 1575 1576 /** 1577 * Get plmn (Public Land Mobile Network) for Passpoint credential; see {@link #setPlmn 1578 * (String)} for more information 1579 * @return the plmn 1580 */ getPlmn()1581 public String getPlmn() { 1582 return getFieldValue(PLMN_KEY); 1583 } 1584 1585 /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ getKeyId(WifiEnterpriseConfig current)1586 public String getKeyId(WifiEnterpriseConfig current) { 1587 // If EAP method is not initialized, use current config details 1588 if (mEapMethod == Eap.NONE) { 1589 return (current != null) ? current.getKeyId(null) : EMPTY_VALUE; 1590 } 1591 if (!isEapMethodValid()) { 1592 return EMPTY_VALUE; 1593 } 1594 return Eap.strings[mEapMethod] + "_" + Phase2.strings[mPhase2Method]; 1595 } 1596 removeDoubleQuotes(String string)1597 private String removeDoubleQuotes(String string) { 1598 if (TextUtils.isEmpty(string)) return ""; 1599 int length = string.length(); 1600 if ((length > 1) && (string.charAt(0) == '"') 1601 && (string.charAt(length - 1) == '"')) { 1602 return string.substring(1, length - 1); 1603 } 1604 return string; 1605 } 1606 convertToQuotedString(String string)1607 private String convertToQuotedString(String string) { 1608 return "\"" + string + "\""; 1609 } 1610 1611 /** 1612 * Returns the index at which the toBeFound string is found in the array. 1613 * @param arr array of strings 1614 * @param toBeFound string to be found 1615 * @param defaultIndex default index to be returned when string is not found 1616 * @return the index into array 1617 */ getStringIndex(String arr[], String toBeFound, int defaultIndex)1618 private int getStringIndex(String arr[], String toBeFound, int defaultIndex) { 1619 if (TextUtils.isEmpty(toBeFound)) return defaultIndex; 1620 for (int i = 0; i < arr.length; i++) { 1621 if (toBeFound.equals(arr[i])) return i; 1622 } 1623 return defaultIndex; 1624 } 1625 1626 /** 1627 * Returns the field value for the key with prefix removed. 1628 * @param key into the hash 1629 * @param prefix is the prefix that the value may have 1630 * @return value 1631 * @hide 1632 */ getFieldValue(String key, String prefix)1633 private String getFieldValue(String key, String prefix) { 1634 if (!isKeySupported(key)) { 1635 return ""; 1636 } 1637 1638 String value = mFields.get(key); 1639 // Uninitialized or known to be empty after reading from supplicant 1640 if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return ""; 1641 1642 value = removeDoubleQuotes(value); 1643 if (value.startsWith(prefix)) { 1644 return value.substring(prefix.length()); 1645 } else { 1646 return value; 1647 } 1648 } 1649 1650 /** 1651 * Returns the field value for the key. 1652 * @param key into the hash 1653 * @return value 1654 * @hide 1655 */ getFieldValue(String key)1656 public String getFieldValue(String key) { 1657 return getFieldValue(key, ""); 1658 } 1659 1660 /** 1661 * Set a value with an optional prefix at key 1662 * @param key into the hash 1663 * @param value to be set 1664 * @param prefix an optional value to be prefixed to actual value 1665 * @hide 1666 */ setFieldValue(String key, String value, String prefix)1667 private void setFieldValue(String key, String value, String prefix) { 1668 if (!isFieldValid(key, value)) { 1669 return; 1670 } 1671 if (TextUtils.isEmpty(value)) { 1672 mFields.put(key, EMPTY_VALUE); 1673 } else { 1674 String valueToSet; 1675 if (!UNQUOTED_KEYS.contains(key)) { 1676 valueToSet = convertToQuotedString(prefix + value); 1677 } else { 1678 valueToSet = prefix + value; 1679 } 1680 mFields.put(key, valueToSet); 1681 } 1682 } 1683 1684 /** 1685 * Set a value at key 1686 * @param key into the hash 1687 * @param value to be set 1688 * @hide 1689 */ setFieldValue(String key, String value)1690 public void setFieldValue(String key, String value) { 1691 setFieldValue(key, value, ""); 1692 } 1693 1694 @Override toString()1695 public String toString() { 1696 StringBuffer sb = new StringBuffer(); 1697 for (String key : mFields.keySet()) { 1698 // Don't display password in toString(). 1699 String value = PASSWORD_KEY.equals(key) ? "<removed>" : mFields.get(key); 1700 sb.append(key).append(" ").append(value).append("\n"); 1701 } 1702 if (mEapMethod >= 0 && mEapMethod < Eap.strings.length) { 1703 sb.append("eap_method: ").append(Eap.strings[mEapMethod]).append("\n"); 1704 } 1705 if (mPhase2Method > 0 && mPhase2Method < Phase2.strings.length) { 1706 sb.append("phase2_method: ").append(Phase2.strings[mPhase2Method]).append("\n"); 1707 } 1708 sb.append(" ocsp: ").append(mOcsp).append("\n"); 1709 sb.append(" trust_on_first_use: ").append(mIsTrustOnFirstUseEnabled).append("\n"); 1710 sb.append(" user_approve_no_ca_cert: ").append(mUserApproveNoCaCert).append("\n"); 1711 sb.append(" selected_rcoi: ").append(mSelectedRcoi).append("\n"); 1712 sb.append(" minimum_tls_version: ").append(mMinimumTlsVersion).append("\n"); 1713 sb.append(" enable_conservative_peer_mode: ") 1714 .append(mIsStrictConservativePeerMode).append("\n"); 1715 sb.append(" tofu_dialog_state: ").append(mTofuDialogState).append("\n"); 1716 sb.append(" tofu_connection_state: ").append(mTofuConnectionState).append("\n"); 1717 return sb.toString(); 1718 } 1719 1720 /** 1721 * Returns whether the EAP method data is valid, i.e., whether mEapMethod and mPhase2Method 1722 * are valid indices into {@code Eap.strings[]} and {@code Phase2.strings[]} respectively. 1723 */ isEapMethodValid()1724 private boolean isEapMethodValid() { 1725 if (mEapMethod == Eap.NONE) { 1726 Log.e(TAG, "WiFi enterprise configuration is invalid as it supplies no EAP method."); 1727 return false; 1728 } 1729 if (mEapMethod < 0 || mEapMethod >= Eap.strings.length) { 1730 Log.e(TAG, "mEapMethod is invald for WiFi enterprise configuration: " + mEapMethod); 1731 return false; 1732 } 1733 if (mPhase2Method < 0 || mPhase2Method >= Phase2.strings.length) { 1734 Log.e(TAG, "mPhase2Method is invald for WiFi enterprise configuration: " 1735 + mPhase2Method); 1736 return false; 1737 } 1738 return true; 1739 } 1740 1741 /** 1742 * Check if certificate was installed by an app, or manually (not by an app). If true, 1743 * certificate and keys will be removed from key storage when this network is removed. If not, 1744 * then certificates and keys remain persistent until the user manually removes them. 1745 * 1746 * @return true if certificate was installed by an app, false if certificate was installed 1747 * manually by the user. 1748 * @hide 1749 */ isAppInstalledDeviceKeyAndCert()1750 public boolean isAppInstalledDeviceKeyAndCert() { 1751 return mIsAppInstalledDeviceKeyAndCert; 1752 } 1753 1754 /** 1755 * Initialize the value of the app installed device key and cert flag. 1756 * 1757 * @param isAppInstalledDeviceKeyAndCert true or false 1758 * @hide 1759 */ initIsAppInstalledDeviceKeyAndCert(boolean isAppInstalledDeviceKeyAndCert)1760 public void initIsAppInstalledDeviceKeyAndCert(boolean isAppInstalledDeviceKeyAndCert) { 1761 mIsAppInstalledDeviceKeyAndCert = isAppInstalledDeviceKeyAndCert; 1762 } 1763 1764 /** 1765 * Check if CA certificate was installed by an app, or manually (not by an app). If true, 1766 * CA certificate will be removed from key storage when this network is removed. If not, 1767 * then certificates and keys remain persistent until the user manually removes them. 1768 * 1769 * @return true if CA certificate was installed by an app, false if CA certificate was installed 1770 * manually by the user. 1771 * @hide 1772 */ isAppInstalledCaCert()1773 public boolean isAppInstalledCaCert() { 1774 return mIsAppInstalledCaCert; 1775 } 1776 1777 /** 1778 * Initialize the value of the app installed root CA cert flag. 1779 * 1780 * @param isAppInstalledCaCert true or false 1781 * @hide 1782 */ initIsAppInstalledCaCert(boolean isAppInstalledCaCert)1783 public void initIsAppInstalledCaCert(boolean isAppInstalledCaCert) { 1784 mIsAppInstalledCaCert = isAppInstalledCaCert; 1785 } 1786 1787 /** 1788 * Set the OCSP type. 1789 * @param ocsp is one of {@link ##OCSP_NONE}, {@link #OCSP_REQUEST_CERT_STATUS}, 1790 * {@link #OCSP_REQUIRE_CERT_STATUS} or 1791 * {@link #OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS} 1792 * @throws IllegalArgumentException if the OCSP type is invalid 1793 * @hide 1794 */ 1795 @SystemApi setOcsp(@csp int ocsp)1796 public void setOcsp(@Ocsp int ocsp) { 1797 if (ocsp >= OCSP_NONE && ocsp <= OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS) { 1798 mOcsp = ocsp; 1799 } else { 1800 throw new IllegalArgumentException("Invalid OCSP type."); 1801 } 1802 } 1803 1804 /** 1805 * Get the OCSP type. 1806 * @hide 1807 */ 1808 @SystemApi getOcsp()1809 public @Ocsp int getOcsp() { 1810 return mOcsp; 1811 } 1812 1813 /** 1814 * Utility method to determine whether the configuration's authentication method is SIM-based. 1815 * 1816 * @return true if the credential information requires SIM card for current authentication 1817 * method, otherwise it returns false. 1818 */ isAuthenticationSimBased()1819 public boolean isAuthenticationSimBased() { 1820 if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) { 1821 return true; 1822 } 1823 if (mEapMethod == Eap.PEAP) { 1824 return mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA 1825 || mPhase2Method == Phase2.AKA_PRIME; 1826 } 1827 return false; 1828 } 1829 1830 /** 1831 * Set the WAPI certificate suite name on wpa_supplicant. 1832 * 1833 * If this field is not specified, WAPI-CERT uses ASU ID from WAI packet 1834 * as the certificate suite name automatically. 1835 * 1836 * @param wapiCertSuite The name for WAPI certificate suite, or empty string to clear. 1837 * @hide 1838 */ 1839 @SystemApi setWapiCertSuite(@onNull String wapiCertSuite)1840 public void setWapiCertSuite(@NonNull String wapiCertSuite) { 1841 setFieldValue(WAPI_CERT_SUITE_KEY, wapiCertSuite); 1842 } 1843 1844 /** 1845 * Get the WAPI certificate suite name 1846 * @return the certificate suite name 1847 * @hide 1848 */ 1849 @NonNull 1850 @SystemApi getWapiCertSuite()1851 public String getWapiCertSuite() { 1852 return getFieldValue(WAPI_CERT_SUITE_KEY); 1853 } 1854 1855 /** 1856 * Determines whether an Enterprise configuration's EAP method requires a Root CA certification 1857 * to validate the authentication server i.e. PEAP, TLS, UNAUTH_TLS, or TTLS. 1858 * @return True if configuration requires a CA certification, false otherwise. 1859 */ isEapMethodServerCertUsed()1860 public boolean isEapMethodServerCertUsed() { 1861 return mEapMethod == Eap.PEAP || mEapMethod == Eap.TLS || mEapMethod == Eap.TTLS 1862 || mEapMethod == Eap.UNAUTH_TLS; 1863 } 1864 /** 1865 * Determines whether an Enterprise configuration enables server certificate validation. 1866 * <p> 1867 * The caller can determine, along with {@link #isEapMethodServerCertUsed()}, if an 1868 * Enterprise configuration enables server certificate validation, which is a mandatory 1869 * requirement for networks that use TLS based EAP methods. A configuration that does not 1870 * enable server certificate validation will be ignored and will not be considered for 1871 * network selection. A network suggestion with such a configuration will cause an 1872 * IllegalArgumentException to be thrown when suggested. 1873 * Server validation is achieved by the following: 1874 * - Either certificate or CA path is configured. 1875 * - Either alternative subject match or domain suffix match is set. 1876 * @return True for server certificate validation is enabled, false otherwise. 1877 * @throws IllegalStateException on configuration which doesn't use server certificate. 1878 * @see #isEapMethodServerCertUsed() 1879 */ isServerCertValidationEnabled()1880 public boolean isServerCertValidationEnabled() { 1881 if (!isEapMethodServerCertUsed()) { 1882 throw new IllegalStateException("Configuration doesn't use server certificates for " 1883 + "authentication"); 1884 } 1885 return isMandatoryParameterSetForServerCertValidation(); 1886 } 1887 1888 /** 1889 * Helper method to check if mandatory parameter for server cert validation is set. 1890 * @hide 1891 */ isMandatoryParameterSetForServerCertValidation()1892 public boolean isMandatoryParameterSetForServerCertValidation() { 1893 if (TextUtils.isEmpty(getAltSubjectMatch()) 1894 && TextUtils.isEmpty(getDomainSuffixMatch())) { 1895 // Both subject and domain match are not set, validation is not enabled. 1896 return false; 1897 } 1898 if (mIsAppInstalledCaCert) { 1899 // CA certificate is installed by App, validation is enabled. 1900 return true; 1901 } 1902 if (getCaCertificateAliases() != null) { 1903 // CA certificate alias from keyStore is set, validation is enabled. 1904 return true; 1905 } 1906 return !TextUtils.isEmpty(getCaPath()); 1907 } 1908 1909 /** 1910 * Check if a given certificate Get the Suite-B cipher from the certificate 1911 * 1912 * @param x509Certificate Certificate to process 1913 * @return true if the certificate OID matches the Suite-B requirements for RSA or ECDSA 1914 * certificates, or false otherwise. 1915 * @hide 1916 */ isSuiteBCipherCert(@ullable X509Certificate x509Certificate)1917 public static boolean isSuiteBCipherCert(@Nullable X509Certificate x509Certificate) { 1918 if (x509Certificate == null) { 1919 return false; 1920 } 1921 final String sigAlgOid = x509Certificate.getSigAlgOID(); 1922 1923 // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates 1924 // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192 1925 // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term 1926 // Suite-B was already coined in the IEEE 802.11-2016 specification for 1927 // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates 1928 // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally 1929 // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments, 1930 // we are supporting both types here. 1931 if (sigAlgOid.equals("1.2.840.113549.1.1.12")) { 1932 // sha384WithRSAEncryption 1933 if (x509Certificate.getPublicKey() instanceof RSAPublicKey) { 1934 final RSAPublicKey rsaPublicKey = (RSAPublicKey) x509Certificate.getPublicKey(); 1935 if (rsaPublicKey.getModulus() != null 1936 && rsaPublicKey.getModulus().bitLength() >= 3072) { 1937 return true; 1938 } 1939 } 1940 } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) { 1941 // ecdsa-with-SHA384 1942 if (x509Certificate.getPublicKey() instanceof ECPublicKey) { 1943 final ECPublicKey ecPublicKey = (ECPublicKey) x509Certificate.getPublicKey(); 1944 final ECParameterSpec ecParameterSpec = ecPublicKey.getParams(); 1945 1946 if (ecParameterSpec != null && ecParameterSpec.getOrder() != null 1947 && ecParameterSpec.getOrder().bitLength() >= 384) { 1948 return true; 1949 } 1950 } 1951 } 1952 return false; 1953 } 1954 1955 /** 1956 * Set a prefix for a decorated identity as per RFC 7542. 1957 * This prefix must contain a list of realms (could be a list of 1) delimited by a '!' 1958 * character. e.g. homerealm.example.org! or proxyrealm.example.net!homerealm.example.org! 1959 * A prefix of "homerealm.example.org!" will generate a decorated identity that 1960 * looks like: homerealm.example.org!user@otherrealm.example.net 1961 * Calling with a null parameter will clear the decorated prefix. 1962 * Note: Caller must verify that the device supports this feature by calling 1963 * {@link WifiManager#isDecoratedIdentitySupported()} 1964 * 1965 * @param decoratedIdentityPrefix The prefix to add to the outer/anonymous identity 1966 */ 1967 @RequiresApi(Build.VERSION_CODES.S) setDecoratedIdentityPrefix(@ullable String decoratedIdentityPrefix)1968 public void setDecoratedIdentityPrefix(@Nullable String decoratedIdentityPrefix) { 1969 if (!SdkLevel.isAtLeastS()) { 1970 throw new UnsupportedOperationException(); 1971 } 1972 if (!TextUtils.isEmpty(decoratedIdentityPrefix) && !decoratedIdentityPrefix.endsWith("!")) { 1973 throw new IllegalArgumentException( 1974 "Decorated identity prefix must be delimited by '!'"); 1975 } 1976 setFieldValue(DECORATED_IDENTITY_PREFIX_KEY, decoratedIdentityPrefix); 1977 } 1978 1979 /** 1980 * Get the decorated identity prefix. 1981 * 1982 * @return The decorated identity prefix 1983 */ 1984 @RequiresApi(Build.VERSION_CODES.S) getDecoratedIdentityPrefix()1985 public @Nullable String getDecoratedIdentityPrefix() { 1986 if (!SdkLevel.isAtLeastS()) { 1987 throw new UnsupportedOperationException(); 1988 } 1989 final String decoratedId = getFieldValue(DECORATED_IDENTITY_PREFIX_KEY); 1990 return decoratedId.isEmpty() ? null : decoratedId; 1991 } 1992 1993 /** 1994 * Enable Trust On First Use. 1995 * 1996 * Trust On First Use (TOFU) simplifies manual or partial configurations 1997 * of TLS-based EAP networks. TOFU operates by installing the Root CA cert 1998 * which is received from the server during an initial connection to a new network. 1999 * Such installation is gated by user approval. 2000 * Use only when it is not possible to configure the Root CA cert for the server. 2001 * <br> 2002 * Note: If a Root CA cert is already configured, this option is ignored, 2003 * e.g. if {@link #setCaCertificate(X509Certificate)}, or 2004 * {@link #setCaCertificates(X509Certificate[])} is called. 2005 * 2006 * @param enable true to enable; false otherwise (the default if the method is not called). 2007 */ enableTrustOnFirstUse(boolean enable)2008 public void enableTrustOnFirstUse(boolean enable) { 2009 mIsTrustOnFirstUseEnabled = enable; 2010 if (mTofuConnectionState != TOFU_STATE_CONFIGURE_ROOT_CA && 2011 mTofuConnectionState != TOFU_STATE_CERT_PINNING) { 2012 // Override the current pre-connection state. 2013 mTofuConnectionState = enable ? 2014 TOFU_STATE_ENABLED_PRE_CONNECTION : TOFU_STATE_NOT_ENABLED; 2015 } 2016 } 2017 2018 /** 2019 * Indicates whether or not Trust On First Use (TOFU) is enabled. 2020 * 2021 * @return Trust On First Use is enabled or not. 2022 */ isTrustOnFirstUseEnabled()2023 public boolean isTrustOnFirstUseEnabled() { 2024 return mIsTrustOnFirstUseEnabled; 2025 } 2026 2027 /** 2028 * Set the TOFU connection state. 2029 * @hide 2030 */ setTofuConnectionState(@ofuConnectionState int state)2031 public void setTofuConnectionState(@TofuConnectionState int state) { 2032 if (state < TOFU_STATE_NOT_ENABLED || state > TOFU_STATE_CERT_PINNING) { 2033 Log.e(TAG, "Invalid TOFU connection state received. state=" + state); 2034 return; 2035 } 2036 mTofuConnectionState = state; 2037 } 2038 2039 /** 2040 * Get the TOFU connection state. 2041 * @hide 2042 */ getTofuConnectionState()2043 public @TofuConnectionState int getTofuConnectionState() { 2044 return mTofuConnectionState; 2045 } 2046 2047 /** 2048 * Indicate whether the user accepted the TOFU dialog. 2049 * @hide 2050 */ setTofuDialogApproved(boolean approved)2051 public void setTofuDialogApproved(boolean approved) { 2052 mTofuDialogState = approved ? TOFU_DIALOG_STATE_ACCEPTED : TOFU_DIALOG_STATE_REJECTED; 2053 } 2054 2055 /** 2056 * Set the TOFU dialog state. 2057 * @hide 2058 */ setTofuDialogState(@ofuDialogState int state)2059 public void setTofuDialogState(@TofuDialogState int state) { 2060 if (state < TOFU_DIALOG_STATE_UNSPECIFIED || state > TOFU_DIALOG_STATE_ACCEPTED) { 2061 Log.e(TAG, "Invalid TOFU dialog state received. state=" + state); 2062 return; 2063 } 2064 mTofuDialogState = state; 2065 } 2066 2067 /** 2068 * Get the TOFU dialog state. 2069 * @hide 2070 */ getTofuDialogState()2071 public @TofuDialogState int getTofuDialogState() { 2072 return mTofuDialogState; 2073 } 2074 2075 /** 2076 * For devices with no TOFU support, indicate that the user approved that a 2077 * legacy TLS-based EAP configuration from a previous release can be used 2078 * without a Root CA certificate. 2079 * 2080 * @hide 2081 */ setUserApproveNoCaCert(boolean approved)2082 public void setUserApproveNoCaCert(boolean approved) { 2083 mUserApproveNoCaCert = approved; 2084 } 2085 2086 /** 2087 * For devices with no TOFU support, indicates if the user approved that a 2088 * legacy TLS-based EAP configuration from a previous release can be used 2089 * without a Root CA certificate. 2090 * 2091 * @return indicate whether a user approves this no CA cert config. 2092 * @hide 2093 */ isUserApproveNoCaCert()2094 public boolean isUserApproveNoCaCert() { 2095 return mUserApproveNoCaCert; 2096 } 2097 2098 /** 2099 * Set the minimum TLS version for TLS-based EAP methods. 2100 * 2101 * {@link WifiManager#isTlsMinimumVersionSupported()} indicates whether or not a minimum 2102 * TLS version can be set. If not supported, the minimum TLS version is always TLS v1.0. 2103 * <p> 2104 * {@link WifiManager#isTlsV13Supported()} indicates whether or not TLS v1.3 is supported. 2105 * If requested minimum is not supported, it will default to the maximum supported version. 2106 * 2107 * @param tlsVersion the TLS version 2108 * @throws IllegalArgumentException if the TLS version is invalid. 2109 */ setMinimumTlsVersion(@lsVersion int tlsVersion)2110 public void setMinimumTlsVersion(@TlsVersion int tlsVersion) throws IllegalArgumentException { 2111 if (tlsVersion < TLS_VERSION_MIN || tlsVersion > TLS_VERSION_MAX) { 2112 throw new IllegalArgumentException( 2113 "Invalid TLS version: " + tlsVersion); 2114 } 2115 mMinimumTlsVersion = tlsVersion; 2116 } 2117 2118 /** 2119 * Get the minimum TLS version for TLS-based EAP methods. 2120 * 2121 * @return the TLS version 2122 */ getMinimumTlsVersion()2123 public @TlsVersion int getMinimumTlsVersion() { 2124 return mMinimumTlsVersion; 2125 } 2126 } 2127