WifiEnterpriseConfig.java revision 7705a0d6ad04fb3c6f32c7700d09f13fdf7c9cee
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 */ 16package android.net.wifi; 17 18import android.annotation.Nullable; 19import android.os.Parcel; 20import android.os.Parcelable; 21import android.security.Credentials; 22import android.text.TextUtils; 23 24import java.io.ByteArrayInputStream; 25import java.nio.charset.StandardCharsets; 26import java.security.KeyFactory; 27import java.security.NoSuchAlgorithmException; 28import java.security.PrivateKey; 29import java.security.cert.CertificateEncodingException; 30import java.security.cert.CertificateException; 31import java.security.cert.CertificateFactory; 32import java.security.cert.X509Certificate; 33import java.security.spec.InvalidKeySpecException; 34import java.security.spec.PKCS8EncodedKeySpec; 35import java.util.HashMap; 36import java.util.Map; 37 38/** 39 * Enterprise configuration details for Wi-Fi. Stores details about the EAP method 40 * and any associated credentials. 41 */ 42public class WifiEnterpriseConfig implements Parcelable { 43 44 /** @hide */ 45 public static final String EMPTY_VALUE = "NULL"; 46 /** @hide */ 47 public static final String EAP_KEY = "eap"; 48 /** @hide */ 49 public static final String PHASE2_KEY = "phase2"; 50 /** @hide */ 51 public static final String IDENTITY_KEY = "identity"; 52 /** @hide */ 53 public static final String ANON_IDENTITY_KEY = "anonymous_identity"; 54 /** @hide */ 55 public static final String PASSWORD_KEY = "password"; 56 /** @hide */ 57 public static final String SUBJECT_MATCH_KEY = "subject_match"; 58 /** @hide */ 59 public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match"; 60 /** @hide */ 61 public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match"; 62 /** @hide */ 63 public static final String OPP_KEY_CACHING = "proactive_key_caching"; 64 /** 65 * String representing the keystore OpenSSL ENGINE's ID. 66 * @hide 67 */ 68 public static final String ENGINE_ID_KEYSTORE = "keystore"; 69 70 /** 71 * String representing the keystore URI used for wpa_supplicant. 72 * @hide 73 */ 74 public static final String KEYSTORE_URI = "keystore://"; 75 76 /** 77 * String representing the keystore URI used for wpa_supplicant, 78 * Unlike #KEYSTORE_URI, this supports a list of space-delimited aliases 79 * @hide 80 */ 81 public static final String KEYSTORES_URI = "keystores://"; 82 83 /** 84 * String to set the engine value to when it should be enabled. 85 * @hide 86 */ 87 public static final String ENGINE_ENABLE = "1"; 88 89 /** 90 * String to set the engine value to when it should be disabled. 91 * @hide 92 */ 93 public static final String ENGINE_DISABLE = "0"; 94 95 /** @hide */ 96 public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE; 97 /** @hide */ 98 public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE; 99 /** @hide */ 100 public static final String CLIENT_CERT_KEY = "client_cert"; 101 /** @hide */ 102 public static final String CA_CERT_KEY = "ca_cert"; 103 /** @hide */ 104 public static final String ENGINE_KEY = "engine"; 105 /** @hide */ 106 public static final String ENGINE_ID_KEY = "engine_id"; 107 /** @hide */ 108 public static final String PRIVATE_KEY_ID_KEY = "key_id"; 109 /** @hide */ 110 public static final String REALM_KEY = "realm"; 111 /** @hide */ 112 public static final String PLMN_KEY = "plmn"; 113 /** @hide */ 114 public static final String CA_CERT_ALIAS_DELIMITER = " "; 115 116 117 private HashMap<String, String> mFields = new HashMap<String, String>(); 118 private X509Certificate[] mCaCerts; 119 private PrivateKey mClientPrivateKey; 120 private X509Certificate mClientCertificate; 121 122 public WifiEnterpriseConfig() { 123 // Do not set defaults so that the enterprise fields that are not changed 124 // by API are not changed underneath 125 // This is essential because an app may not have all fields like password 126 // available. It allows modification of subset of fields. 127 128 } 129 130 /** Copy constructor */ 131 public WifiEnterpriseConfig(WifiEnterpriseConfig source) { 132 for (String key : source.mFields.keySet()) { 133 mFields.put(key, source.mFields.get(key)); 134 } 135 } 136 137 @Override 138 public int describeContents() { 139 return 0; 140 } 141 142 @Override 143 public void writeToParcel(Parcel dest, int flags) { 144 dest.writeInt(mFields.size()); 145 for (Map.Entry<String, String> entry : mFields.entrySet()) { 146 dest.writeString(entry.getKey()); 147 dest.writeString(entry.getValue()); 148 } 149 150 writeCertificates(dest, mCaCerts); 151 152 if (mClientPrivateKey != null) { 153 String algorithm = mClientPrivateKey.getAlgorithm(); 154 byte[] userKeyBytes = mClientPrivateKey.getEncoded(); 155 dest.writeInt(userKeyBytes.length); 156 dest.writeByteArray(userKeyBytes); 157 dest.writeString(algorithm); 158 } else { 159 dest.writeInt(0); 160 } 161 162 writeCertificate(dest, mClientCertificate); 163 } 164 165 private void writeCertificates(Parcel dest, X509Certificate[] cert) { 166 if (cert != null && cert.length != 0) { 167 dest.writeInt(cert.length); 168 for (int i = 0; i < cert.length; i++) { 169 writeCertificate(dest, cert[i]); 170 } 171 } else { 172 dest.writeInt(0); 173 } 174 } 175 176 private void writeCertificate(Parcel dest, X509Certificate cert) { 177 if (cert != null) { 178 try { 179 byte[] certBytes = cert.getEncoded(); 180 dest.writeInt(certBytes.length); 181 dest.writeByteArray(certBytes); 182 } catch (CertificateEncodingException e) { 183 dest.writeInt(0); 184 } 185 } else { 186 dest.writeInt(0); 187 } 188 } 189 190 public static final Creator<WifiEnterpriseConfig> CREATOR = 191 new Creator<WifiEnterpriseConfig>() { 192 public WifiEnterpriseConfig createFromParcel(Parcel in) { 193 WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); 194 int count = in.readInt(); 195 for (int i = 0; i < count; i++) { 196 String key = in.readString(); 197 String value = in.readString(); 198 enterpriseConfig.mFields.put(key, value); 199 } 200 201 enterpriseConfig.mCaCerts = readCertificates(in); 202 203 PrivateKey userKey = null; 204 int len = in.readInt(); 205 if (len > 0) { 206 try { 207 byte[] bytes = new byte[len]; 208 in.readByteArray(bytes); 209 String algorithm = in.readString(); 210 KeyFactory keyFactory = KeyFactory.getInstance(algorithm); 211 userKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); 212 } catch (NoSuchAlgorithmException e) { 213 userKey = null; 214 } catch (InvalidKeySpecException e) { 215 userKey = null; 216 } 217 } 218 219 enterpriseConfig.mClientPrivateKey = userKey; 220 enterpriseConfig.mClientCertificate = readCertificate(in); 221 return enterpriseConfig; 222 } 223 224 private X509Certificate[] readCertificates(Parcel in) { 225 X509Certificate[] certs = null; 226 int len = in.readInt(); 227 if (len > 0) { 228 certs = new X509Certificate[len]; 229 for (int i = 0; i < len; i++) { 230 certs[i] = readCertificate(in); 231 } 232 } 233 return certs; 234 } 235 236 private X509Certificate readCertificate(Parcel in) { 237 X509Certificate cert = null; 238 int len = in.readInt(); 239 if (len > 0) { 240 try { 241 byte[] bytes = new byte[len]; 242 in.readByteArray(bytes); 243 CertificateFactory cFactory = CertificateFactory.getInstance("X.509"); 244 cert = (X509Certificate) cFactory 245 .generateCertificate(new ByteArrayInputStream(bytes)); 246 } catch (CertificateException e) { 247 cert = null; 248 } 249 } 250 return cert; 251 } 252 253 public WifiEnterpriseConfig[] newArray(int size) { 254 return new WifiEnterpriseConfig[size]; 255 } 256 }; 257 258 /** The Extensible Authentication Protocol method used */ 259 public static final class Eap { 260 /** No EAP method used. Represents an empty config */ 261 public static final int NONE = -1; 262 /** Protected EAP */ 263 public static final int PEAP = 0; 264 /** EAP-Transport Layer Security */ 265 public static final int TLS = 1; 266 /** EAP-Tunneled Transport Layer Security */ 267 public static final int TTLS = 2; 268 /** EAP-Password */ 269 public static final int PWD = 3; 270 /** EAP-Subscriber Identity Module */ 271 public static final int SIM = 4; 272 /** EAP-Authentication and Key Agreement */ 273 public static final int AKA = 5; 274 /** EAP-Authentication and Key Agreement Prime */ 275 public static final int AKA_PRIME = 6; 276 /** @hide */ 277 public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'" }; 278 279 /** Prevent initialization */ 280 private Eap() {} 281 } 282 283 /** The inner authentication method used */ 284 public static final class Phase2 { 285 public static final int NONE = 0; 286 /** Password Authentication Protocol */ 287 public static final int PAP = 1; 288 /** Microsoft Challenge Handshake Authentication Protocol */ 289 public static final int MSCHAP = 2; 290 /** Microsoft Challenge Handshake Authentication Protocol v2 */ 291 public static final int MSCHAPV2 = 3; 292 /** Generic Token Card */ 293 public static final int GTC = 4; 294 private static final String PREFIX = "auth="; 295 /** @hide */ 296 public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", 297 "MSCHAPV2", "GTC" }; 298 299 /** Prevent initialization */ 300 private Phase2() {} 301 } 302 303 /** Internal use only 304 * @hide 305 */ 306 public HashMap<String, String> getFields() { 307 return mFields; 308 } 309 310 /** 311 * Set the EAP authentication method. 312 * @param eapMethod is one {@link Eap#PEAP}, {@link Eap#TLS}, {@link Eap#TTLS} or 313 * {@link Eap#PWD} 314 * @throws IllegalArgumentException on an invalid eap method 315 */ 316 public void setEapMethod(int eapMethod) { 317 switch (eapMethod) { 318 /** Valid methods */ 319 case Eap.TLS: 320 setPhase2Method(Phase2.NONE); 321 /* fall through */ 322 case Eap.PEAP: 323 case Eap.PWD: 324 case Eap.TTLS: 325 case Eap.SIM: 326 case Eap.AKA: 327 case Eap.AKA_PRIME: 328 mFields.put(EAP_KEY, Eap.strings[eapMethod]); 329 mFields.put(OPP_KEY_CACHING, "1"); 330 break; 331 default: 332 throw new IllegalArgumentException("Unknown EAP method"); 333 } 334 } 335 336 /** 337 * Get the eap method. 338 * @return eap method configured 339 */ 340 public int getEapMethod() { 341 String eapMethod = mFields.get(EAP_KEY); 342 return getStringIndex(Eap.strings, eapMethod, Eap.NONE); 343 } 344 345 /** 346 * Set Phase 2 authentication method. Sets the inner authentication method to be used in 347 * phase 2 after setting up a secure channel 348 * @param phase2Method is the inner authentication method and can be one of {@link Phase2#NONE}, 349 * {@link Phase2#PAP}, {@link Phase2#MSCHAP}, {@link Phase2#MSCHAPV2}, 350 * {@link Phase2#GTC} 351 * @throws IllegalArgumentException on an invalid phase2 method 352 * 353 */ 354 public void setPhase2Method(int phase2Method) { 355 switch (phase2Method) { 356 case Phase2.NONE: 357 mFields.put(PHASE2_KEY, EMPTY_VALUE); 358 break; 359 /** Valid methods */ 360 case Phase2.PAP: 361 case Phase2.MSCHAP: 362 case Phase2.MSCHAPV2: 363 case Phase2.GTC: 364 mFields.put(PHASE2_KEY, convertToQuotedString( 365 Phase2.PREFIX + Phase2.strings[phase2Method])); 366 break; 367 default: 368 throw new IllegalArgumentException("Unknown Phase 2 method"); 369 } 370 } 371 372 /** 373 * Get the phase 2 authentication method. 374 * @return a phase 2 method defined at {@link Phase2} 375 * */ 376 public int getPhase2Method() { 377 String phase2Method = removeDoubleQuotes(mFields.get(PHASE2_KEY)); 378 // Remove auth= prefix 379 if (phase2Method.startsWith(Phase2.PREFIX)) { 380 phase2Method = phase2Method.substring(Phase2.PREFIX.length()); 381 } 382 return getStringIndex(Phase2.strings, phase2Method, Phase2.NONE); 383 } 384 385 /** 386 * Set the identity 387 * @param identity 388 */ 389 public void setIdentity(String identity) { 390 setFieldValue(IDENTITY_KEY, identity, ""); 391 } 392 393 /** 394 * Get the identity 395 * @return the identity 396 */ 397 public String getIdentity() { 398 return getFieldValue(IDENTITY_KEY, ""); 399 } 400 401 /** 402 * Set anonymous identity. This is used as the unencrypted identity with 403 * certain EAP types 404 * @param anonymousIdentity the anonymous identity 405 */ 406 public void setAnonymousIdentity(String anonymousIdentity) { 407 setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, ""); 408 } 409 410 /** Get the anonymous identity 411 * @return anonymous identity 412 */ 413 public String getAnonymousIdentity() { 414 return getFieldValue(ANON_IDENTITY_KEY, ""); 415 } 416 417 /** 418 * Set the password. 419 * @param password the password 420 */ 421 public void setPassword(String password) { 422 setFieldValue(PASSWORD_KEY, password, ""); 423 } 424 425 /** 426 * Get the password. 427 * 428 * Returns locally set password value. For networks fetched from 429 * framework, returns "*". 430 */ 431 public String getPassword() { 432 return getFieldValue(PASSWORD_KEY, ""); 433 } 434 435 /** 436 * Encode a CA certificate alias so it does not contain illegal character. 437 * @hide 438 */ 439 public static String encodeCaCertificateAlias(String alias) { 440 byte[] bytes = alias.getBytes(StandardCharsets.UTF_8); 441 StringBuilder sb = new StringBuilder(bytes.length * 2); 442 for (byte o : bytes) { 443 sb.append(String.format("%02x", o & 0xFF)); 444 } 445 return sb.toString(); 446 } 447 448 /** 449 * Decode a previously-encoded CA certificate alias. 450 * @hide 451 */ 452 public static String decodeCaCertificateAlias(String alias) { 453 byte[] data = new byte[alias.length() >> 1]; 454 for (int n = 0, position = 0; n < alias.length(); n += 2, position++) { 455 data[position] = (byte) Integer.parseInt(alias.substring(n, n + 2), 16); 456 } 457 try { 458 return new String(data, StandardCharsets.UTF_8); 459 } catch (NumberFormatException e) { 460 e.printStackTrace(); 461 return alias; 462 } 463 } 464 465 /** 466 * Set CA certificate alias. 467 * 468 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 469 * a certificate 470 * </p> 471 * @param alias identifies the certificate 472 * @hide 473 */ 474 public void setCaCertificateAlias(String alias) { 475 setFieldValue(CA_CERT_KEY, alias, CA_CERT_PREFIX); 476 } 477 478 /** 479 * Set CA certificate aliases. When creating installing the corresponding certificate to 480 * the keystore, please use alias encoded by {@link #encodeCaCertificateAlias(String)}. 481 * 482 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 483 * a certificate. 484 * </p> 485 * @param aliases identifies the certificate 486 * @hide 487 */ 488 public void setCaCertificateAliases(@Nullable String[] aliases) { 489 if (aliases == null) { 490 setFieldValue(CA_CERT_KEY, null, CA_CERT_PREFIX); 491 } else if (aliases.length == 1) { 492 // Backwards compatibility: use the original cert prefix if setting only one alias. 493 setCaCertificateAlias(aliases[0]); 494 } else { 495 // Use KEYSTORES_URI which supports multiple aliases. 496 StringBuilder sb = new StringBuilder(); 497 for (int i = 0; i < aliases.length; i++) { 498 if (i > 0) { 499 sb.append(CA_CERT_ALIAS_DELIMITER); 500 } 501 sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i])); 502 } 503 setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI); 504 } 505 } 506 507 /** 508 * Get CA certificate alias 509 * @return alias to the CA certificate 510 * @hide 511 */ 512 public String getCaCertificateAlias() { 513 return getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); 514 } 515 516 /** 517 * Get CA certificate aliases 518 * @return alias to the CA certificate 519 * @hide 520 */ 521 @Nullable public String[] getCaCertificateAliases() { 522 String value = getFieldValue(CA_CERT_KEY, ""); 523 if (value.startsWith(CA_CERT_PREFIX)) { 524 // Backwards compatibility: parse the original alias prefix. 525 return new String[] {getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX)}; 526 } else if (value.startsWith(KEYSTORES_URI)) { 527 String values = value.substring(KEYSTORES_URI.length()); 528 529 String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER); 530 for (int i = 0; i < aliases.length; i++) { 531 aliases[i] = decodeCaCertificateAlias(aliases[i]); 532 if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) { 533 aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length()); 534 } 535 } 536 return aliases.length != 0 ? aliases : null; 537 } else { 538 return TextUtils.isEmpty(value) ? null : new String[] {value}; 539 } 540 } 541 542 /** 543 * Specify a X.509 certificate that identifies the server. 544 * 545 * <p>A default name is automatically assigned to the certificate and used 546 * with this configuration. The framework takes care of installing the 547 * certificate when the config is saved and removing the certificate when 548 * the config is removed. 549 * 550 * @param cert X.509 CA certificate 551 * @throws IllegalArgumentException if not a CA certificate 552 */ 553 public void setCaCertificate(@Nullable X509Certificate cert) { 554 if (cert != null) { 555 if (cert.getBasicConstraints() >= 0) { 556 mCaCerts = new X509Certificate[] {cert}; 557 } else { 558 throw new IllegalArgumentException("Not a CA certificate"); 559 } 560 } else { 561 mCaCerts = null; 562 } 563 } 564 565 /** 566 * Get CA certificate. If multiple CA certificates are configured previously, 567 * return the first one. 568 * @return X.509 CA certificate 569 */ 570 @Nullable public X509Certificate getCaCertificate() { 571 if (mCaCerts != null && mCaCerts.length > 0) { 572 return mCaCerts[0]; 573 } else { 574 return null; 575 } 576 } 577 578 /** 579 * Specify a list of X.509 certificates that identifies the server. The validation 580 * passes if the CA of server certificate matches one of the given certificates. 581 582 * <p>Default names are automatically assigned to the certificates and used 583 * with this configuration. The framework takes care of installing the 584 * certificates when the config is saved and removing the certificates when 585 * the config is removed. 586 * 587 * @param certs X.509 CA certificates 588 * @throws IllegalArgumentException if any of the provided certificates is 589 * not a CA certificate 590 */ 591 public void setCaCertificates(@Nullable X509Certificate[] certs) { 592 if (certs != null) { 593 X509Certificate[] newCerts = new X509Certificate[certs.length]; 594 for (int i = 0; i < certs.length; i++) { 595 if (certs[i].getBasicConstraints() >= 0) { 596 newCerts[i] = certs[i]; 597 } else { 598 throw new IllegalArgumentException("Not a CA certificate"); 599 } 600 } 601 mCaCerts = newCerts; 602 } else { 603 mCaCerts = null; 604 } 605 } 606 607 /** 608 * Get CA certificates. 609 */ 610 @Nullable public X509Certificate[] getCaCertificates() { 611 if (mCaCerts != null || mCaCerts.length > 0) { 612 return mCaCerts; 613 } else { 614 return null; 615 } 616 } 617 618 /** 619 * @hide 620 */ 621 public void resetCaCertificate() { 622 mCaCerts = null; 623 } 624 625 /** Set Client certificate alias. 626 * 627 * <p> See the {@link android.security.KeyChain} for details on installing or choosing 628 * a certificate 629 * </p> 630 * @param alias identifies the certificate 631 * @hide 632 */ 633 public void setClientCertificateAlias(String alias) { 634 setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX); 635 setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY); 636 // Also, set engine parameters 637 if (TextUtils.isEmpty(alias)) { 638 mFields.put(ENGINE_KEY, ENGINE_DISABLE); 639 mFields.put(ENGINE_ID_KEY, EMPTY_VALUE); 640 } else { 641 mFields.put(ENGINE_KEY, ENGINE_ENABLE); 642 mFields.put(ENGINE_ID_KEY, convertToQuotedString(ENGINE_ID_KEYSTORE)); 643 } 644 } 645 646 /** 647 * Get client certificate alias 648 * @return alias to the client certificate 649 * @hide 650 */ 651 public String getClientCertificateAlias() { 652 return getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); 653 } 654 655 /** 656 * Specify a private key and client certificate for client authorization. 657 * 658 * <p>A default name is automatically assigned to the key entry and used 659 * with this configuration. The framework takes care of installing the 660 * key entry when the config is saved and removing the key entry when 661 * the config is removed. 662 663 * @param privateKey 664 * @param clientCertificate 665 * @throws IllegalArgumentException for an invalid key or certificate. 666 */ 667 public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { 668 if (clientCertificate != null) { 669 if (clientCertificate.getBasicConstraints() != -1) { 670 throw new IllegalArgumentException("Cannot be a CA certificate"); 671 } 672 if (privateKey == null) { 673 throw new IllegalArgumentException("Client cert without a private key"); 674 } 675 if (privateKey.getEncoded() == null) { 676 throw new IllegalArgumentException("Private key cannot be encoded"); 677 } 678 } 679 680 mClientPrivateKey = privateKey; 681 mClientCertificate = clientCertificate; 682 } 683 684 /** 685 * Get client certificate 686 * 687 * @return X.509 client certificate 688 */ 689 public X509Certificate getClientCertificate() { 690 return mClientCertificate; 691 } 692 693 /** 694 * @hide 695 */ 696 public void resetClientKeyEntry() { 697 mClientPrivateKey = null; 698 mClientCertificate = null; 699 } 700 701 /** 702 * @hide 703 */ 704 public PrivateKey getClientPrivateKey() { 705 return mClientPrivateKey; 706 } 707 708 /** 709 * Set subject match (deprecated). This is the substring to be matched against the subject of 710 * the authentication server certificate. 711 * @param subjectMatch substring to be matched 712 * @deprecated in favor of altSubjectMatch 713 */ 714 public void setSubjectMatch(String subjectMatch) { 715 setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, ""); 716 } 717 718 /** 719 * Get subject match (deprecated) 720 * @return the subject match string 721 * @deprecated in favor of altSubjectMatch 722 */ 723 public String getSubjectMatch() { 724 return getFieldValue(SUBJECT_MATCH_KEY, ""); 725 } 726 727 /** 728 * Set alternate subject match. This is the substring to be matched against the 729 * alternate subject of the authentication server certificate. 730 * @param altSubjectMatch substring to be matched, for example 731 * DNS:server.example.com;EMAIL:server@example.com 732 */ 733 public void setAltSubjectMatch(String altSubjectMatch) { 734 setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch, ""); 735 } 736 737 /** 738 * Get alternate subject match 739 * @return the alternate subject match string 740 */ 741 public String getAltSubjectMatch() { 742 return getFieldValue(ALTSUBJECT_MATCH_KEY, ""); 743 } 744 745 /** 746 * Set the domain_suffix_match directive on wpa_supplicant. This is the parameter to use 747 * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2, 748 * second paragraph. 749 * 750 * From wpa_supplicant documentation: 751 * Constraint for server domain name. If set, this FQDN is used as a suffix match requirement 752 * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is 753 * found, this constraint is met. If no dNSName values are present, this constraint is matched 754 * against SubjectName CN using same suffix match comparison. 755 * Suffix match here means that the host/domain name is compared one label at a time starting 756 * from the top-level domain and all the labels in domain_suffix_match shall be included in the 757 * certificate. The certificate may include additional sub-level labels in addition to the 758 * required labels. 759 * For example, domain_suffix_match=example.com would match test.example.com but would not 760 * match test-example.com. 761 * @param domain The domain value 762 */ 763 public void setDomainSuffixMatch(String domain) { 764 setFieldValue(DOM_SUFFIX_MATCH_KEY, domain); 765 } 766 767 /** 768 * Get the domain_suffix_match value. See setDomSuffixMatch. 769 * @return The domain value. 770 */ 771 public String getDomainSuffixMatch() { 772 return getFieldValue(DOM_SUFFIX_MATCH_KEY, ""); 773 } 774 775 /** 776 * Set realm for passpoint credential; realm identifies a set of networks where your 777 * passpoint credential can be used 778 * @param realm the realm 779 */ 780 public void setRealm(String realm) { 781 setFieldValue(REALM_KEY, realm, ""); 782 } 783 784 /** 785 * Get realm for passpoint credential; see {@link #setRealm(String)} for more information 786 * @return the realm 787 */ 788 public String getRealm() { 789 return getFieldValue(REALM_KEY, ""); 790 } 791 792 /** 793 * Set plmn (Public Land Mobile Network) of the provider of passpoint credential 794 * @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code) 795 */ 796 public void setPlmn(String plmn) { 797 setFieldValue(PLMN_KEY, plmn, ""); 798 } 799 800 /** 801 * Get plmn (Public Land Mobile Network) for passpoint credential; see {@link #setPlmn 802 * (String)} for more information 803 * @return the plmn 804 */ 805 public String getPlmn() { 806 return getFieldValue(PLMN_KEY, ""); 807 } 808 809 /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */ 810 String getKeyId(WifiEnterpriseConfig current) { 811 String eap = mFields.get(EAP_KEY); 812 String phase2 = mFields.get(PHASE2_KEY); 813 814 // If either eap or phase2 are not initialized, use current config details 815 if (TextUtils.isEmpty((eap))) { 816 eap = current.mFields.get(EAP_KEY); 817 } 818 if (TextUtils.isEmpty(phase2)) { 819 phase2 = current.mFields.get(PHASE2_KEY); 820 } 821 return eap + "_" + phase2; 822 } 823 824 private String removeDoubleQuotes(String string) { 825 if (TextUtils.isEmpty(string)) return ""; 826 int length = string.length(); 827 if ((length > 1) && (string.charAt(0) == '"') 828 && (string.charAt(length - 1) == '"')) { 829 return string.substring(1, length - 1); 830 } 831 return string; 832 } 833 834 private String convertToQuotedString(String string) { 835 return "\"" + string + "\""; 836 } 837 838 /** Returns the index at which the toBeFound string is found in the array. 839 * @param arr array of strings 840 * @param toBeFound string to be found 841 * @param defaultIndex default index to be returned when string is not found 842 * @return the index into array 843 */ 844 private int getStringIndex(String arr[], String toBeFound, int defaultIndex) { 845 if (TextUtils.isEmpty(toBeFound)) return defaultIndex; 846 for (int i = 0; i < arr.length; i++) { 847 if (toBeFound.equals(arr[i])) return i; 848 } 849 return defaultIndex; 850 } 851 852 /** Returns the field value for the key. 853 * @param key into the hash 854 * @param prefix is the prefix that the value may have 855 * @return value 856 * @hide 857 */ 858 public String getFieldValue(String key, String prefix) { 859 String value = mFields.get(key); 860 // Uninitialized or known to be empty after reading from supplicant 861 if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return ""; 862 863 value = removeDoubleQuotes(value); 864 if (value.startsWith(prefix)) { 865 return value.substring(prefix.length()); 866 } else { 867 return value; 868 } 869 } 870 871 /** Set a value with an optional prefix at key 872 * @param key into the hash 873 * @param value to be set 874 * @param prefix an optional value to be prefixed to actual value 875 * @hide 876 */ 877 public void setFieldValue(String key, String value, String prefix) { 878 if (TextUtils.isEmpty(value)) { 879 mFields.put(key, EMPTY_VALUE); 880 } else { 881 mFields.put(key, convertToQuotedString(prefix + value)); 882 } 883 } 884 885 886 /** Set a value with an optional prefix at key 887 * @param key into the hash 888 * @param value to be set 889 * @param prefix an optional value to be prefixed to actual value 890 * @hide 891 */ 892 public void setFieldValue(String key, String value) { 893 if (TextUtils.isEmpty(value)) { 894 mFields.put(key, EMPTY_VALUE); 895 } else { 896 mFields.put(key, convertToQuotedString(value)); 897 } 898 } 899 900 @Override 901 public String toString() { 902 StringBuffer sb = new StringBuffer(); 903 for (String key : mFields.keySet()) { 904 // Don't display password in toString(). 905 String value = (key == PASSWORD_KEY) ? "<removed>" : mFields.get(key); 906 sb.append(key).append(" ").append(value).append("\n"); 907 } 908 return sb.toString(); 909 } 910} 911