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