ProviderList.java revision 15381:6c96af8a34b2
1/* 2 * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.security.jca; 27 28import java.util.*; 29 30import java.security.AccessController; 31import java.security.PrivilegedAction; 32import java.security.Provider; 33import java.security.Provider.Service; 34import java.security.Security; 35 36/** 37 * List of Providers. Used to represent the provider preferences. 38 * 39 * The system starts out with a ProviderList that only has the names 40 * of the Providers. 41 * When using ServiceLoader to load the providers, Providers are created 42 * semi-eagerly as we iterate through them looking for a match. 43 * 44 * For compatibility reasons, Providers that could not be loaded are ignored 45 * and internally presented as the instance EMPTY_PROVIDER. However, those 46 * objects cannot be presented to applications. Call the convert() method 47 * to force all Providers to be loaded and to obtain a ProviderList with 48 * invalid entries removed. All this is handled by the Security class. 49 * 50 * Note that all indices used by this class are 0-based per general Java 51 * convention. These must be converted to the 1-based indices used by the 52 * Security class externally when needed. 53 * 54 * Instances of this class are immutable. This eliminates the need for 55 * cloning and synchronization in consumers. The add() and remove() style 56 * methods are static in order to avoid confusion about the immutability. 57 * 58 * @author Andreas Sterbenz 59 * @since 1.5 60 */ 61public final class ProviderList { 62 63 static final sun.security.util.Debug debug = 64 sun.security.util.Debug.getInstance("jca", "ProviderList"); 65 66 private static final ProviderConfig[] PC0 = new ProviderConfig[0]; 67 68 private static final Provider[] P0 = new Provider[0]; 69 70 // constant for an ProviderList with no elements 71 static final ProviderList EMPTY = new ProviderList(PC0, true); 72 73 // list of all jdk.security.provider.preferred entries 74 static private PreferredList preferredPropList = null; 75 76 // dummy provider object to use during initialization 77 // used to avoid explicit null checks in various places 78 private static final Provider EMPTY_PROVIDER = 79 new Provider("##Empty##", "1.0", "initialization in progress") { 80 private static final long serialVersionUID = 1151354171352296389L; 81 // override getService() to return null slightly faster 82 public Service getService(String type, String algorithm) { 83 return null; 84 } 85 }; 86 87 // construct a ProviderList from the security properties 88 // (static provider configuration in the java.security file) 89 static ProviderList fromSecurityProperties() { 90 // doPrivileged() because of Security.getProperty() 91 return AccessController.doPrivileged( 92 new PrivilegedAction<ProviderList>() { 93 public ProviderList run() { 94 return new ProviderList(); 95 } 96 }); 97 } 98 99 public static ProviderList add(ProviderList providerList, Provider p) { 100 return insertAt(providerList, p, -1); 101 } 102 103 public static ProviderList insertAt(ProviderList providerList, Provider p, 104 int position) { 105 if (providerList.getProvider(p.getName()) != null) { 106 return providerList; 107 } 108 List<ProviderConfig> list = new ArrayList<> 109 (Arrays.asList(providerList.configs)); 110 int n = list.size(); 111 if ((position < 0) || (position > n)) { 112 position = n; 113 } 114 list.add(position, new ProviderConfig(p)); 115 return new ProviderList(list.toArray(PC0), true); 116 } 117 118 public static ProviderList remove(ProviderList providerList, String name) { 119 // make sure provider exists 120 if (providerList.getProvider(name) == null) { 121 return providerList; 122 } 123 // copy all except matching to new list 124 ProviderConfig[] configs = new ProviderConfig[providerList.size() - 1]; 125 int j = 0; 126 for (ProviderConfig config : providerList.configs) { 127 if (config.getProvider().getName().equals(name) == false) { 128 configs[j++] = config; 129 } 130 } 131 return new ProviderList(configs, true); 132 } 133 134 // Create a new ProviderList from the specified Providers. 135 // This method is for use by SunJSSE. 136 public static ProviderList newList(Provider ... providers) { 137 ProviderConfig[] configs = new ProviderConfig[providers.length]; 138 for (int i = 0; i < providers.length; i++) { 139 configs[i] = new ProviderConfig(providers[i]); 140 } 141 return new ProviderList(configs, true); 142 } 143 144 // configuration of the providers 145 private final ProviderConfig[] configs; 146 147 // flag indicating whether all configs have been loaded successfully 148 private volatile boolean allLoaded; 149 150 // List returned by providers() 151 private final List<Provider> userList = new AbstractList<Provider>() { 152 public int size() { 153 return configs.length; 154 } 155 public Provider get(int index) { 156 return getProvider(index); 157 } 158 }; 159 160 /** 161 * Create a new ProviderList from an array of configs 162 */ 163 private ProviderList(ProviderConfig[] configs, boolean allLoaded) { 164 this.configs = configs; 165 this.allLoaded = allLoaded; 166 } 167 168 /** 169 * Return a new ProviderList parsed from the java.security Properties. 170 */ 171 private ProviderList() { 172 List<ProviderConfig> configList = new ArrayList<>(); 173 String entry; 174 int i = 1; 175 176 while ((entry = Security.getProperty("security.provider." + i)) != null) { 177 entry = entry.trim(); 178 if (entry.length() == 0) { 179 System.err.println("invalid entry for " + 180 "security.provider." + i); 181 break; 182 } 183 int k = entry.indexOf(' '); 184 ProviderConfig config; 185 if (k == -1) { 186 config = new ProviderConfig(entry); 187 } else { 188 String provName = entry.substring(0, k); 189 String argument = entry.substring(k + 1).trim(); 190 config = new ProviderConfig(provName, argument); 191 } 192 193 // Get rid of duplicate providers. 194 if (configList.contains(config) == false) { 195 configList.add(config); 196 } 197 i++; 198 } 199 configs = configList.toArray(PC0); 200 201 // Load config entries for use when getInstance is called 202 entry = Security.getProperty("jdk.security.provider.preferred"); 203 if (entry != null && (entry = entry.trim()).length() > 0) { 204 String[] entries = entry.split(","); 205 if (ProviderList.preferredPropList == null) { 206 ProviderList.preferredPropList = new PreferredList(); 207 } 208 209 for (String e : entries) { 210 i = e.indexOf(':'); 211 if (i < 0) { 212 if (debug != null) { 213 debug.println("invalid preferred entry skipped. " + 214 "Missing colon delimiter \"" + e + "\""); 215 } 216 continue; 217 } 218 ProviderList.preferredPropList.add(new PreferredEntry( 219 e.substring(0, i).trim(), e.substring(i + 1).trim())); 220 } 221 } 222 223 if (debug != null) { 224 debug.println("provider configuration: " + configList); 225 debug.println("config configuration: " + 226 ProviderList.preferredPropList); 227 } 228 } 229 230 /** 231 * Construct a special ProviderList for JAR verification. It consists 232 * of the providers specified via jarClassNames, which must be on the 233 * bootclasspath and cannot be in signed JAR files. This is to avoid 234 * possible recursion and deadlock during verification. 235 */ 236 ProviderList getJarList(String[] jarProvNames) { 237 List<ProviderConfig> newConfigs = new ArrayList<>(); 238 for (String provName : jarProvNames) { 239 ProviderConfig newConfig = new ProviderConfig(provName); 240 for (ProviderConfig config : configs) { 241 // if the equivalent object is present in this provider list, 242 // use the old object rather than the new object. 243 // this ensures that when the provider is loaded in the 244 // new thread local list, it will also become available 245 // in this provider list 246 if (config.equals(newConfig)) { 247 newConfig = config; 248 break; 249 } 250 } 251 newConfigs.add(newConfig); 252 } 253 ProviderConfig[] configArray = newConfigs.toArray(PC0); 254 return new ProviderList(configArray, false); 255 } 256 257 public int size() { 258 return configs.length; 259 } 260 261 /** 262 * Return the Provider at the specified index. Returns EMPTY_PROVIDER 263 * if the provider could not be loaded at this time. 264 */ 265 Provider getProvider(int index) { 266 Provider p = configs[index].getProvider(); 267 return (p != null) ? p : EMPTY_PROVIDER; 268 } 269 270 /** 271 * Return an unmodifiable List of all Providers in this List. The 272 * individual Providers are loaded on demand. Elements that could not 273 * be initialized are replaced with EMPTY_PROVIDER. 274 */ 275 public List<Provider> providers() { 276 return userList; 277 } 278 279 private ProviderConfig getProviderConfig(String name) { 280 int index = getIndex(name); 281 return (index != -1) ? configs[index] : null; 282 } 283 284 // return the Provider with the specified name or null 285 public Provider getProvider(String name) { 286 ProviderConfig config = getProviderConfig(name); 287 return (config == null) ? null : config.getProvider(); 288 } 289 290 /** 291 * Return the index at which the provider with the specified name is 292 * installed or -1 if it is not present in this ProviderList. 293 */ 294 public int getIndex(String name) { 295 for (int i = 0; i < configs.length; i++) { 296 Provider p = getProvider(i); 297 if (p.getName().equals(name)) { 298 return i; 299 } 300 } 301 return -1; 302 } 303 304 // attempt to load all Providers not already loaded 305 private int loadAll() { 306 if (allLoaded) { 307 return configs.length; 308 } 309 if (debug != null) { 310 debug.println("Loading all providers"); 311 new Exception("Call trace").printStackTrace(); 312 } 313 int n = 0; 314 for (int i = 0; i < configs.length; i++) { 315 Provider p = configs[i].getProvider(); 316 if (p != null) { 317 n++; 318 } 319 } 320 if (n == configs.length) { 321 allLoaded = true; 322 } 323 return n; 324 } 325 326 /** 327 * Try to load all Providers and return the ProviderList. If one or 328 * more Providers could not be loaded, a new ProviderList with those 329 * entries removed is returned. Otherwise, the method returns this. 330 */ 331 ProviderList removeInvalid() { 332 int n = loadAll(); 333 if (n == configs.length) { 334 return this; 335 } 336 ProviderConfig[] newConfigs = new ProviderConfig[n]; 337 for (int i = 0, j = 0; i < configs.length; i++) { 338 ProviderConfig config = configs[i]; 339 if (config.isLoaded()) { 340 newConfigs[j++] = config; 341 } 342 } 343 return new ProviderList(newConfigs, true); 344 } 345 346 // return the providers as an array 347 public Provider[] toArray() { 348 return providers().toArray(P0); 349 } 350 351 // return a String representation of this ProviderList 352 public String toString() { 353 return Arrays.asList(configs).toString(); 354 } 355 356 /** 357 * Return a Service describing an implementation of the specified 358 * algorithm from the Provider with the highest precedence that 359 * supports that algorithm. Return null if no Provider supports this 360 * algorithm. 361 */ 362 public Service getService(String type, String name) { 363 ArrayList<PreferredEntry> pList = null; 364 int i; 365 366 // Preferred provider list 367 if (preferredPropList != null && 368 (pList = preferredPropList.getAll(type, name)) != null) { 369 for (i = 0; i < pList.size(); i++) { 370 Provider p = getProvider(pList.get(i).provider); 371 Service s = p.getService(type, name); 372 if (s != null) { 373 return s; 374 } 375 } 376 } 377 378 for (i = 0; i < configs.length; i++) { 379 Provider p = getProvider(i); 380 Service s = p.getService(type, name); 381 if (s != null) { 382 return s; 383 } 384 } 385 return null; 386 } 387 388 /** 389 * Return a List containing all the Services describing implementations 390 * of the specified algorithms in precedence order. If no implementation 391 * exists, this method returns an empty List. 392 * 393 * The elements of this list are determined lazily on demand. 394 * 395 * The List returned is NOT thread safe. 396 */ 397 public List<Service> getServices(String type, String algorithm) { 398 return new ServiceList(type, algorithm); 399 } 400 401 /** 402 * This method exists for compatibility with JCE only. It will be removed 403 * once JCE has been changed to use the replacement method. 404 * @deprecated use {@code getServices(List<ServiceId>)} instead 405 */ 406 @Deprecated 407 public List<Service> getServices(String type, List<String> algorithms) { 408 List<ServiceId> ids = new ArrayList<>(); 409 for (String alg : algorithms) { 410 ids.add(new ServiceId(type, alg)); 411 } 412 return getServices(ids); 413 } 414 415 public List<Service> getServices(List<ServiceId> ids) { 416 return new ServiceList(ids); 417 } 418 419 /** 420 * Inner class for a List of Services. Custom List implementation in 421 * order to delay Provider initialization and lookup. 422 * Not thread safe. 423 */ 424 private final class ServiceList extends AbstractList<Service> { 425 426 // type and algorithm for simple lookup 427 // avoid allocating/traversing the ServiceId list for these lookups 428 private final String type; 429 private final String algorithm; 430 431 // list of ids for parallel lookup 432 // if ids is non-null, type and algorithm are null 433 private final List<ServiceId> ids; 434 435 // first service we have found 436 // it is stored in a separate variable so that we can avoid 437 // allocating the services list if we do not need the second service. 438 // this is the case if we don't failover (failovers are typically rare) 439 private Service firstService; 440 441 // list of the services we have found so far 442 private List<Service> services; 443 444 // index into config[] of the next provider we need to query 445 private int providerIndex = 0; 446 447 // Matching preferred provider list for this ServiceList 448 ArrayList<PreferredEntry> preferredList = null; 449 private int preferredIndex = 0; 450 451 ServiceList(String type, String algorithm) { 452 this.type = type; 453 this.algorithm = algorithm; 454 this.ids = null; 455 } 456 457 ServiceList(List<ServiceId> ids) { 458 this.type = null; 459 this.algorithm = null; 460 this.ids = ids; 461 } 462 463 private void addService(Service s) { 464 if (firstService == null) { 465 firstService = s; 466 } else { 467 if (services == null) { 468 services = new ArrayList<Service>(4); 469 services.add(firstService); 470 } 471 services.add(s); 472 } 473 } 474 475 private Service tryGet(int index) { 476 Provider p; 477 478 // If preferred providers are configured, check for matches with 479 // the requested service. 480 if (preferredPropList != null && preferredList == null) { 481 preferredList = preferredPropList.getAll(this); 482 } 483 484 while (true) { 485 if ((index == 0) && (firstService != null)) { 486 return firstService; 487 } else if ((services != null) && (services.size() > index)) { 488 return services.get(index); 489 } 490 if (providerIndex >= configs.length) { 491 return null; 492 } 493 494 // If there were matches with a preferred provider, iterate 495 // through the list first before going through the 496 // ordered list (java.security.provider.#) 497 if (preferredList != null && 498 preferredIndex < preferredList.size()) { 499 PreferredEntry entry = preferredList.get(preferredIndex++); 500 // Look for the provider name in the PreferredEntry 501 p = getProvider(entry.provider); 502 if (p == null) { 503 if (debug != null) { 504 debug.println("No provider found with name: " + 505 entry.provider); 506 } 507 continue; 508 } 509 } else { 510 // check all algorithms in this provider before moving on 511 p = getProvider(providerIndex++); 512 } 513 514 if (type != null) { 515 // simple lookup 516 Service s = p.getService(type, algorithm); 517 if (s != null) { 518 addService(s); 519 } 520 } else { 521 // parallel lookup 522 for (ServiceId id : ids) { 523 Service s = p.getService(id.type, id.algorithm); 524 if (s != null) { 525 addService(s); 526 } 527 } 528 } 529 } 530 } 531 532 public Service get(int index) { 533 Service s = tryGet(index); 534 if (s == null) { 535 throw new IndexOutOfBoundsException(); 536 } 537 return s; 538 } 539 540 public int size() { 541 int n; 542 if (services != null) { 543 n = services.size(); 544 } else { 545 n = (firstService != null) ? 1 : 0; 546 } 547 while (tryGet(n) != null) { 548 n++; 549 } 550 return n; 551 } 552 553 // override isEmpty() and iterator() to not call size() 554 // this avoids loading + checking all Providers 555 556 public boolean isEmpty() { 557 return (tryGet(0) == null); 558 } 559 560 public Iterator<Service> iterator() { 561 return new Iterator<Service>() { 562 int index; 563 564 public boolean hasNext() { 565 return tryGet(index) != null; 566 } 567 568 public Service next() { 569 Service s = tryGet(index); 570 if (s == null) { 571 throw new NoSuchElementException(); 572 } 573 index++; 574 return s; 575 } 576 577 public void remove() { 578 throw new UnsupportedOperationException(); 579 } 580 }; 581 } 582 } 583 584 // Provider list defined by jdk.security.provider.preferred entry 585 static final class PreferredList { 586 ArrayList<PreferredEntry> list = new ArrayList<PreferredEntry>(); 587 588 /* 589 * Return a list of all preferred entries that match the passed 590 * ServiceList. 591 */ 592 ArrayList<PreferredEntry> getAll(ServiceList s) { 593 if (s.ids == null) { 594 return getAll(s.type, s.algorithm); 595 596 } 597 598 ArrayList<PreferredEntry> l = new ArrayList<PreferredEntry>(); 599 for (ServiceId id : s.ids) { 600 implGetAll(l, id.type, id.algorithm); 601 } 602 603 return l; 604 } 605 606 /* 607 * Return a list of all preferred entries that match the passed 608 * type and algorithm. 609 */ 610 ArrayList<PreferredEntry> getAll(String type, String algorithm) { 611 ArrayList<PreferredEntry> l = new ArrayList<PreferredEntry>(); 612 implGetAll(l, type, algorithm); 613 return l; 614 } 615 616 /* 617 * Compare each preferred entry against the passed type and 618 * algorithm, putting any matches in the passed ArrayList. 619 */ 620 private void implGetAll(ArrayList<PreferredEntry> l, String type, 621 String algorithm) { 622 PreferredEntry e; 623 624 for (int i = 0; i < size(); i++) { 625 e = list.get(i); 626 if (e.match(type, algorithm)) { 627 l.add(e); 628 } 629 } 630 } 631 632 public PreferredEntry get(int i) { 633 return list.get(i); 634 } 635 636 public int size() { 637 return list.size(); 638 } 639 640 public boolean add(PreferredEntry e) { 641 return list.add(e); 642 } 643 644 public String toString() { 645 String s = ""; 646 for (PreferredEntry e: list) { 647 s += e.toString(); 648 } 649 return s; 650 } 651 } 652 653 /* Defined Groups for jdk.security.provider.preferred */ 654 private static final String SHA2Group[] = { "SHA-224", "SHA-256", 655 "SHA-384", "SHA-512", "SHA-512/224", "SHA-512/256" }; 656 private static final String HmacSHA2Group[] = { "HmacSHA224", 657 "HmacSHA256", "HmacSHA384", "HmacSHA512"}; 658 private static final String SHA2RSAGroup[] = { "SHA224withRSA", 659 "SHA256withRSA", "SHA384withRSA", "SHA512withRSA"}; 660 private static final String SHA2DSAGroup[] = { "SHA224withDSA", 661 "SHA256withDSA", "SHA384withDSA", "SHA512withDSA"}; 662 private static final String SHA2ECDSAGroup[] = { "SHA224withECDSA", 663 "SHA256withECDSA", "SHA384withECDSA", "SHA512withECDSA"}; 664 private static final String SHA3Group[] = { "SHA3-224", "SHA3-256", 665 "SHA3-384", "SHA3-512" }; 666 private static final String HmacSHA3Group[] = { "HmacSHA3-224", 667 "HmacSHA3-256", "HmacSHA3-384", "HmacSHA3-512"}; 668 669 // Individual preferred property entry from jdk.security.provider.preferred 670 private static class PreferredEntry { 671 private String type = null; 672 private String algorithm; 673 private String provider; 674 private String alternateNames[] = null; 675 private boolean group = false; 676 677 PreferredEntry(String t, String p) { 678 int i = t.indexOf('.'); 679 if (i > 0) { 680 type = t.substring(0, i); 681 algorithm = t.substring(i + 1); 682 } else { 683 algorithm = t; 684 } 685 686 provider = p; 687 // Group definitions 688 if (type != null && type.compareToIgnoreCase("Group") == 0) { 689 // Currently intrinsic algorithm groups 690 if (algorithm.compareToIgnoreCase("SHA2") == 0) { 691 alternateNames = SHA2Group; 692 } else if (algorithm.compareToIgnoreCase("HmacSHA2") == 0) { 693 alternateNames = HmacSHA2Group; 694 } else if (algorithm.compareToIgnoreCase("SHA2RSA") == 0) { 695 alternateNames = SHA2RSAGroup; 696 } else if (algorithm.compareToIgnoreCase("SHA2DSA") == 0) { 697 alternateNames = SHA2DSAGroup; 698 } else if (algorithm.compareToIgnoreCase("SHA2ECDSA") == 0) { 699 alternateNames = SHA2ECDSAGroup; 700 } else if (algorithm.compareToIgnoreCase("SHA3") == 0) { 701 alternateNames = SHA3Group; 702 } else if (algorithm.compareToIgnoreCase("HmacSHA3") == 0) { 703 alternateNames = HmacSHA3Group; 704 } 705 if (alternateNames != null) { 706 group = true; 707 } 708 709 // If the algorithm name given is SHA1 710 } else if (algorithm.compareToIgnoreCase("SHA1") == 0) { 711 alternateNames = new String[] { "SHA-1" }; 712 } else if (algorithm.compareToIgnoreCase("SHA-1") == 0) { 713 alternateNames = new String[] { "SHA1" }; 714 } 715 } 716 717 boolean match(String t, String a) { 718 if (debug != null) { 719 debug.println("Config check: " + toString() + " == " + 720 print(t, a, null)); 721 } 722 723 // Compare service type if configured 724 if (type != null && !group && type.compareToIgnoreCase(t) != 0) { 725 return false; 726 } 727 728 // Compare the algorithm string. 729 if (!group && a.compareToIgnoreCase(algorithm) == 0) { 730 if (debug != null) { 731 debug.println("Config entry matched: " + toString()); 732 } 733 return true; 734 } 735 736 if (alternateNames != null) { 737 for (String alt : alternateNames) { 738 if (debug != null) { 739 debug.println("AltName check: " + print(type, alt, 740 provider)); 741 } 742 if (a.compareToIgnoreCase(alt) == 0) { 743 if (debug != null) { 744 debug.println("AltName entry matched: " + 745 provider); 746 } 747 return true; 748 } 749 } 750 } 751 752 // No match 753 return false; 754 } 755 756 // Print debugging output of PreferredEntry 757 private String print(String t, String a, String p) { 758 return "[" + ((t != null) ? t : "" ) + ", " + a + 759 ((p != null) ? " : " + p : "" ) + "] "; 760 } 761 762 public String toString() { 763 return print(type, algorithm, provider); 764 } 765 } 766 767} 768