1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.security; 19 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.io.NotActiveException; 23 import java.util.ArrayList; 24 import java.util.Collection; 25 import java.util.Collections; 26 import java.util.Enumeration; 27 import java.util.HashMap; 28 import java.util.Iterator; 29 import java.util.LinkedHashMap; 30 import java.util.LinkedHashSet; 31 import java.util.List; 32 import java.util.Locale; 33 import java.util.Map; 34 import java.util.Properties; 35 import java.util.Set; 36 import org.apache.harmony.security.fortress.Services; 37 38 /** 39 * {@code Provider} is the abstract superclass for all security providers in the 40 * Java security infrastructure. 41 */ 42 public abstract class Provider extends Properties { 43 private static final long serialVersionUID = -4298000515446427739L; 44 45 private String name; 46 47 private double version; 48 49 // String representation of the provider version number. 50 private transient String versionString; 51 52 private String info; 53 54 //The provider preference order number. 55 // Equals -1 for non registered provider. 56 private transient int providerNumber = -1; 57 58 // Contains "Service.Algorithm" and Provider.Service classes added using 59 // putService() 60 private transient LinkedHashMap<String, Service> serviceTable; 61 62 // Contains "Service.Alias" and Provider.Service classes added using 63 // putService() 64 private transient LinkedHashMap<String, Service> aliasTable; 65 66 // Contains "Service.Algorithm" and Provider.Service classes added using 67 // put() 68 private transient LinkedHashMap<String, Service> propertyServiceTable; 69 70 // Contains "Service.Alias" and Provider.Service classes added using put() 71 private transient LinkedHashMap<String, Service> propertyAliasTable; 72 73 // The properties changed via put() 74 private transient LinkedHashMap<Object, Object> changedProperties; 75 76 // For getService(String type, String algorithm) optimization: 77 // previous result 78 private transient Provider.Service returnedService; 79 // previous parameters 80 private transient String lastAlgorithm; 81 // last name 82 private transient String lastServiceName; 83 84 // For getServices() optimization: 85 private transient Set<Service> lastServicesSet; 86 87 // For getService(String type) optimization: 88 private transient String lastType; 89 // last Service found by type 90 private transient Provider.Service lastServicesByType; 91 92 /** 93 * Constructs a new instance of {@code Provider} with its name, version and 94 * description. 95 * 96 * @param name 97 * the name of the provider. 98 * @param version 99 * the version of the provider. 100 * @param info 101 * a description of the provider. 102 */ Provider(String name, double version, String info)103 protected Provider(String name, double version, String info) { 104 this.name = name; 105 this.version = version; 106 this.info = info; 107 versionString = String.valueOf(version); 108 putProviderInfo(); 109 } 110 111 /** 112 * Returns the name of this provider. 113 * 114 * @return the name of this provider. 115 */ getName()116 public String getName() { 117 return name; 118 } 119 120 /** 121 * Returns the version number for the services being provided. 122 * 123 * @return the version number for the services being provided. 124 */ getVersion()125 public double getVersion() { 126 return version; 127 } 128 129 /** 130 * Returns a description of the services being provided. 131 * 132 * @return a description of the services being provided. 133 */ getInfo()134 public String getInfo() { 135 return info; 136 } 137 138 /** 139 * Returns a string containing a concise, human-readable description of 140 * this {@code Provider} including its name and its version. 141 * 142 * @return a printable representation for this {@code Provider}. 143 */ 144 @Override toString()145 public String toString() { 146 return name + " version " + version; 147 } 148 149 /** 150 * Clears all properties used to look up services implemented by this 151 * {@code Provider}. 152 */ 153 @Override clear()154 public synchronized void clear() { 155 super.clear(); 156 if (serviceTable != null) { 157 serviceTable.clear(); 158 } 159 if (propertyServiceTable != null) { 160 propertyServiceTable.clear(); 161 } 162 if (aliasTable != null) { 163 aliasTable.clear(); 164 } 165 if (propertyAliasTable != null) { 166 propertyAliasTable.clear(); 167 } 168 changedProperties = null; 169 putProviderInfo(); 170 if (providerNumber != -1) { 171 // if registered then refresh Services 172 Services.setNeedRefresh(); 173 } 174 servicesChanged(); 175 } 176 177 @Override load(InputStream inStream)178 public synchronized void load(InputStream inStream) throws IOException { 179 Properties tmp = new Properties(); 180 tmp.load(inStream); 181 myPutAll(tmp); 182 } 183 184 /** 185 * Copies all from the provided map to this {@code Provider}. 186 * @param t 187 * the mappings to copy to this provider. 188 */ 189 @Override putAll(Map<?,?> t)190 public synchronized void putAll(Map<?,?> t) { 191 myPutAll(t); 192 } 193 myPutAll(Map<?,?> t)194 private void myPutAll(Map<?,?> t) { 195 if (changedProperties == null) { 196 changedProperties = new LinkedHashMap<Object, Object>(); 197 } 198 Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator(); 199 Object key; 200 Object value; 201 while (it.hasNext()) { 202 Map.Entry<?, ?> entry = it.next(); 203 key = entry.getKey(); 204 if (key instanceof String && ((String) key).startsWith("Provider.")) { 205 // Provider service type is reserved 206 continue; 207 } 208 value = entry.getValue(); 209 super.put(key, value); 210 if (changedProperties.remove(key) == null) { 211 removeFromPropertyServiceTable(key); 212 } 213 changedProperties.put(key, value); 214 } 215 if (providerNumber != -1) { 216 // if registered then refresh Services 217 Services.setNeedRefresh(); 218 } 219 } 220 221 @Override entrySet()222 public synchronized Set<Map.Entry<Object,Object>> entrySet() { 223 return Collections.unmodifiableSet(super.entrySet()); 224 } 225 226 @Override keySet()227 public Set<Object> keySet() { 228 return Collections.unmodifiableSet(super.keySet()); 229 } 230 231 @Override values()232 public Collection<Object> values() { 233 return Collections.unmodifiableCollection(super.values()); 234 } 235 236 /** 237 * Maps the specified {@code key} property name to the specified {@code 238 * value}. 239 * 240 * @param key 241 * the name of the property. 242 * @param value 243 * the value of the property. 244 * @return the value that was previously mapped to the specified {@code key} 245 * ,or {@code null} if it did not have one. 246 */ 247 @Override put(Object key, Object value)248 public synchronized Object put(Object key, Object value) { 249 if (key instanceof String && ((String) key).startsWith("Provider.")) { 250 // Provider service type is reserved 251 return null; 252 } 253 if (providerNumber != -1) { 254 // if registered then refresh Services 255 Services.setNeedRefresh(); 256 } 257 if (changedProperties != null && changedProperties.remove(key) == null) { 258 removeFromPropertyServiceTable(key); 259 } 260 if (changedProperties == null) { 261 changedProperties = new LinkedHashMap<Object, Object>(); 262 } 263 changedProperties.put(key, value); 264 return super.put(key, value); 265 } 266 267 /** 268 * Removes the specified {@code key} and its associated value from this 269 * {@code Provider}. 270 * 271 * @param key 272 * the name of the property 273 * @return the value that was mapped to the specified {@code key} ,or 274 * {@code null} if no mapping was present 275 */ 276 @Override remove(Object key)277 public synchronized Object remove(Object key) { 278 if (key instanceof String && ((String) key).startsWith("Provider.")) { 279 // Provider service type is reserved 280 return null; 281 } 282 if (providerNumber != -1) { 283 // if registered then refresh Services 284 Services.setNeedRefresh(); 285 } 286 if (changedProperties != null && changedProperties.remove(key) == null) { 287 removeFromPropertyServiceTable(key); 288 if (changedProperties.size() == 0) { 289 changedProperties = null; 290 } 291 } 292 return super.remove(key); 293 } 294 295 /** 296 * Returns true if this provider implements the given algorithm. Caller 297 * must specify the cryptographic service and specify constraints via the 298 * attribute name and value. 299 * 300 * @param serv 301 * Crypto service. 302 * @param alg 303 * Algorithm or type. 304 * @param attribute 305 * The attribute name or {@code null}. 306 * @param val 307 * The attribute value. 308 * @return 309 */ implementsAlg(String serv, String alg, String attribute, String val)310 boolean implementsAlg(String serv, String alg, String attribute, String val) { 311 String servAlg = serv + "." + alg; 312 String prop = getPropertyIgnoreCase(servAlg); 313 if (prop == null) { 314 alg = getPropertyIgnoreCase("Alg.Alias." + servAlg); 315 if (alg != null) { 316 servAlg = serv + "." + alg; 317 prop = getPropertyIgnoreCase(servAlg); 318 } 319 } 320 if (prop != null) { 321 if (attribute == null) { 322 return true; 323 } 324 return checkAttribute(servAlg, attribute, val); 325 } 326 return false; 327 } 328 329 /** 330 * Returns true if this provider has the same value as is given for the 331 * given attribute 332 */ checkAttribute(String servAlg, String attribute, String val)333 private boolean checkAttribute(String servAlg, String attribute, String val) { 334 335 String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute); 336 if (attributeValue != null) { 337 if (attribute.equalsIgnoreCase("KeySize")) { 338 if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) { 339 return true; 340 } 341 } else { // other attributes 342 if (attributeValue.equalsIgnoreCase(val)) { 343 return true; 344 } 345 } 346 } 347 return false; 348 } 349 350 /** 351 * 352 * Set the provider preference order number. 353 * 354 * @param n 355 */ setProviderNumber(int n)356 void setProviderNumber(int n) { 357 providerNumber = n; 358 } 359 360 /** 361 * 362 * Get the provider preference order number. 363 * 364 * @return 365 */ getProviderNumber()366 int getProviderNumber() { 367 return providerNumber; 368 } 369 370 /** 371 * Get the service of the specified type 372 * 373 */ getService(String type)374 synchronized Provider.Service getService(String type) { 375 updatePropertyServiceTable(); 376 if (lastServicesByType != null && type.equals(lastType)) { 377 return lastServicesByType; 378 } 379 Provider.Service service; 380 for (Iterator<Service> it = getServices().iterator(); it.hasNext();) { 381 service = it.next(); 382 if (type.equals(service.type)) { 383 lastType = type; 384 lastServicesByType = service; 385 return service; 386 } 387 } 388 return null; 389 } 390 391 /** 392 * Returns the service with the specified {@code type} implementing the 393 * specified {@code algorithm}, or {@code null} if no such implementation 394 * exists. 395 * <p> 396 * If two services match the requested type and algorithm, the one added 397 * with the {@link #putService(Service)} is returned (as opposed to the one 398 * added via {@link #put(Object, Object)}. 399 * 400 * @param type 401 * the type of the service (for example {@code KeyPairGenerator}) 402 * @param algorithm 403 * the algorithm name (case insensitive) 404 * @return the requested service, or {@code null} if no such implementation 405 * exists 406 */ getService(String type, String algorithm)407 public synchronized Provider.Service getService(String type, 408 String algorithm) { 409 if (type == null) { 410 throw new NullPointerException("type == null"); 411 } else if (algorithm == null) { 412 throw new NullPointerException("algorithm == null"); 413 } 414 415 if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) { 416 return returnedService; 417 } 418 419 String key = key(type, algorithm); 420 Object o = null; 421 if (serviceTable != null) { 422 o = serviceTable.get(key); 423 } 424 if (o == null && aliasTable != null) { 425 o = aliasTable.get(key); 426 } 427 if (o == null) { 428 updatePropertyServiceTable(); 429 } 430 if (o == null && propertyServiceTable != null) { 431 o = propertyServiceTable.get(key); 432 } 433 if (o == null && propertyAliasTable != null) { 434 o = propertyAliasTable.get(key); 435 } 436 437 if (o != null) { 438 lastServiceName = type; 439 lastAlgorithm = algorithm; 440 returnedService = (Provider.Service) o; 441 return returnedService; 442 } 443 return null; 444 } 445 446 /** 447 * Returns an unmodifiable {@code Set} of all services registered by this 448 * provider. 449 * 450 * @return an unmodifiable {@code Set} of all services registered by this 451 * provider 452 */ getServices()453 public synchronized Set<Provider.Service> getServices() { 454 updatePropertyServiceTable(); 455 if (lastServicesSet != null) { 456 return lastServicesSet; 457 } 458 if (serviceTable != null) { 459 lastServicesSet = new LinkedHashSet<Service>(serviceTable.values()); 460 } else { 461 lastServicesSet = new LinkedHashSet<Service>(); 462 } 463 if (propertyServiceTable != null) { 464 lastServicesSet.addAll(propertyServiceTable.values()); 465 } 466 lastServicesSet = Collections.unmodifiableSet(lastServicesSet); 467 return lastServicesSet; 468 } 469 470 /** 471 * Adds a {@code Service} to this {@code Provider}. If a service with the 472 * same name was registered via this method, it is replace. 473 * 474 * @param s 475 * the {@code Service} to register 476 */ putService(Provider.Service s)477 protected synchronized void putService(Provider.Service s) { 478 if (s == null) { 479 throw new NullPointerException("s == null"); 480 } 481 if ("Provider".equals(s.getType())) { // Provider service type cannot be added 482 return; 483 } 484 servicesChanged(); 485 if (serviceTable == null) { 486 serviceTable = new LinkedHashMap<String, Service>(128); 487 } 488 serviceTable.put(key(s.type, s.algorithm), s); 489 if (s.aliases != null) { 490 if (aliasTable == null) { 491 aliasTable = new LinkedHashMap<String, Service>(256); 492 } 493 for (String alias : s.getAliases()) { 494 aliasTable.put(key(s.type, alias), s); 495 } 496 } 497 serviceInfoToProperties(s); 498 } 499 500 /** 501 * Removes a previously registered {@code Service} from this {@code 502 * Provider}. 503 * 504 * @param s 505 * the {@code Service} to remove 506 * @throws NullPointerException 507 * if {@code s} is {@code null} 508 */ removeService(Provider.Service s)509 protected synchronized void removeService(Provider.Service s) { 510 if (s == null) { 511 throw new NullPointerException("s == null"); 512 } 513 servicesChanged(); 514 if (serviceTable != null) { 515 serviceTable.remove(key(s.type, s.algorithm)); 516 } 517 if (aliasTable != null && s.aliases != null) { 518 for (String alias: s.getAliases()) { 519 aliasTable.remove(key(s.type, alias)); 520 } 521 } 522 serviceInfoFromProperties(s); 523 } 524 525 /** 526 * Add Service information to the provider's properties. 527 */ serviceInfoToProperties(Provider.Service s)528 private void serviceInfoToProperties(Provider.Service s) { 529 super.put(s.type + "." + s.algorithm, s.className); 530 if (s.aliases != null) { 531 for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { 532 super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm); 533 } 534 } 535 if (s.attributes != null) { 536 for (Map.Entry<String, String> entry : s.attributes.entrySet()) { 537 super.put(s.type + "." + s.algorithm + " " + entry.getKey(), 538 entry.getValue()); 539 } 540 } 541 if (providerNumber != -1) { 542 // if registered then refresh Services 543 Services.setNeedRefresh(); 544 } 545 } 546 547 /** 548 * Remove Service information from the provider's properties. 549 */ serviceInfoFromProperties(Provider.Service s)550 private void serviceInfoFromProperties(Provider.Service s) { 551 super.remove(s.type + "." + s.algorithm); 552 if (s.aliases != null) { 553 for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { 554 super.remove("Alg.Alias." + s.type + "." + i.next()); 555 } 556 } 557 if (s.attributes != null) { 558 for (Map.Entry<String, String> entry : s.attributes.entrySet()) { 559 super.remove(s.type + "." + s.algorithm + " " + entry.getKey()); 560 } 561 } 562 if (providerNumber != -1) { 563 // if registered then refresh Services 564 Services.setNeedRefresh(); 565 } 566 } 567 568 // Remove property information from provider Services removeFromPropertyServiceTable(Object key)569 private void removeFromPropertyServiceTable(Object key) { 570 if (key == null || !(key instanceof String)) { 571 return; 572 } 573 String k = (String) key; 574 if (k.startsWith("Provider.")) { // Provider service type is reserved 575 return; 576 } 577 Provider.Service s; 578 String serviceName; 579 String algorithm = null; 580 String attribute = null; 581 int i; 582 if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> 583 String aliasName; 584 String service_alias = k.substring(10); 585 i = service_alias.indexOf('.'); 586 serviceName = service_alias.substring(0, i); 587 aliasName = service_alias.substring(i + 1); 588 if (propertyAliasTable != null) { 589 propertyAliasTable.remove(key(serviceName, aliasName)); 590 } 591 if (propertyServiceTable != null) { 592 for (Iterator<Service> it = propertyServiceTable.values().iterator(); it 593 .hasNext();) { 594 s = it.next(); 595 if (s.aliases.contains(aliasName)) { 596 s.aliases.remove(aliasName); 597 return; 598 } 599 } 600 } 601 return; 602 } 603 int j = k.indexOf('.'); 604 if (j == -1) { // unknown format 605 return; 606 } 607 608 i = k.indexOf(' '); 609 if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> 610 serviceName = k.substring(0, j); 611 algorithm = k.substring(j + 1); 612 if (propertyServiceTable != null) { 613 Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm)); 614 if (ser != null && propertyAliasTable != null 615 && ser.aliases != null) { 616 for (String alias : ser.aliases) { 617 propertyAliasTable.remove(key(serviceName, alias)); 618 } 619 } 620 } 621 } else { 622 // <crypto_service>.<algorithm_or_type> 623 // <attribute_name>=<attrValue> 624 attribute = k.substring(i + 1); 625 serviceName = k.substring(0, j); 626 algorithm = k.substring(j + 1, i); 627 if (propertyServiceTable != null) { 628 Object o = propertyServiceTable.get(key(serviceName, algorithm)); 629 if (o != null) { 630 s = (Provider.Service) o; 631 s.attributes.remove(attribute); 632 } 633 } 634 } 635 } 636 637 // Update provider Services if the properties was changed updatePropertyServiceTable()638 private void updatePropertyServiceTable() { 639 Object _key; 640 Object _value; 641 Provider.Service s; 642 String serviceName; 643 String algorithm; 644 if (changedProperties == null || changedProperties.isEmpty()) { 645 return; 646 } 647 for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it 648 .hasNext();) { 649 Map.Entry<Object, Object> entry = it.next(); 650 _key = entry.getKey(); 651 _value = entry.getValue(); 652 if (_key == null || _value == null || !(_key instanceof String) 653 || !(_value instanceof String)) { 654 continue; 655 } 656 String key = (String) _key; 657 String value = (String) _value; 658 if (key.startsWith("Provider")) { 659 // Provider service type is reserved 660 continue; 661 } 662 int i; 663 if (key.startsWith("Alg.Alias.")) { 664 // Alg.Alias.<crypto_service>.<aliasName>=<standardName> 665 String aliasName; 666 String service_alias = key.substring(10); 667 i = service_alias.indexOf('.'); 668 serviceName = service_alias.substring(0, i); 669 aliasName = service_alias.substring(i + 1); 670 algorithm = value; 671 String propertyServiceTableKey = key(serviceName, algorithm); 672 Object o = null; 673 if (propertyServiceTable == null) { 674 propertyServiceTable = new LinkedHashMap<String, Service>(128); 675 } else { 676 o = propertyServiceTable.get(propertyServiceTableKey); 677 } 678 if (o != null) { 679 s = (Provider.Service) o; 680 s.addAlias(aliasName); 681 if (propertyAliasTable == null) { 682 propertyAliasTable = new LinkedHashMap<String, Service>(256); 683 } 684 propertyAliasTable.put(key(serviceName, aliasName), s); 685 } else { 686 String className = (String) changedProperties 687 .get(serviceName + "." + algorithm); 688 if (className != null) { 689 List<String> l = new ArrayList<String>(); 690 l.add(aliasName); 691 s = new Provider.Service(this, serviceName, algorithm, 692 className, l, new HashMap<String, String>()); 693 propertyServiceTable.put(propertyServiceTableKey, s); 694 if (propertyAliasTable == null) { 695 propertyAliasTable = new LinkedHashMap<String, Service>(256); 696 } 697 propertyAliasTable.put(key(serviceName, aliasName), s); 698 } 699 } 700 continue; 701 } 702 int j = key.indexOf('.'); 703 if (j == -1) { // unknown format 704 continue; 705 } 706 i = key.indexOf(' '); 707 if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> 708 serviceName = key.substring(0, j); 709 algorithm = key.substring(j + 1); 710 String propertyServiceTableKey = key(serviceName, algorithm); 711 Object o = null; 712 if (propertyServiceTable != null) { 713 o = propertyServiceTable.get(propertyServiceTableKey); 714 } 715 if (o != null) { 716 s = (Provider.Service) o; 717 s.className = value; 718 } else { 719 s = new Provider.Service(this, serviceName, algorithm, 720 value, Collections.<String>emptyList(), 721 Collections.<String,String>emptyMap()); 722 if (propertyServiceTable == null) { 723 propertyServiceTable = new LinkedHashMap<String, Service>(128); 724 } 725 propertyServiceTable.put(propertyServiceTableKey, s); 726 727 } 728 } else { 729 // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue> 730 serviceName = key.substring(0, j); 731 algorithm = key.substring(j + 1, i); 732 String attribute = key.substring(i + 1); 733 String propertyServiceTableKey = key(serviceName, algorithm); 734 Object o = null; 735 if (propertyServiceTable != null) { 736 o = propertyServiceTable.get(propertyServiceTableKey); 737 } 738 if (o != null) { 739 s = (Provider.Service) o; 740 s.putAttribute(attribute, value); 741 } else { 742 String className = (String) changedProperties 743 .get(serviceName + "." + algorithm); 744 if (className != null) { 745 Map<String, String> m = new HashMap<String, String>(); 746 m.put(attribute, value); 747 s = new Provider.Service(this, serviceName, algorithm, 748 className, new ArrayList<String>(), m); 749 if (propertyServiceTable == null) { 750 propertyServiceTable = new LinkedHashMap<String, Service>(128); 751 } 752 propertyServiceTable.put(propertyServiceTableKey, s); 753 } 754 } 755 } 756 } 757 servicesChanged(); 758 changedProperties = null; 759 } 760 servicesChanged()761 private void servicesChanged() { 762 lastServicesByType = null; 763 lastServiceName = null; 764 lastServicesSet = null; 765 } 766 767 /** 768 * These attributes should be placed in each Provider object: 769 * Provider.id name, Provider.id version, Provider.id info, 770 * Provider.id className 771 */ putProviderInfo()772 private void putProviderInfo() { 773 super.put("Provider.id name", (name != null) ? name : "null"); 774 super.put("Provider.id version", versionString); 775 super.put("Provider.id info", (info != null) ? info : "null"); 776 super.put("Provider.id className", this.getClass().getName()); 777 } 778 779 /** 780 * Returns the property with the specified key in the provider properties. 781 * The name is not case-sensitive. 782 */ getPropertyIgnoreCase(String key)783 private String getPropertyIgnoreCase(String key) { 784 String res = getProperty(key); 785 if (res != null) { 786 return res; 787 } 788 for (Enumeration<?> e = propertyNames(); e.hasMoreElements(); ) { 789 String propertyName = (String) e.nextElement(); 790 if (key.equalsIgnoreCase(propertyName)) { 791 return getProperty(propertyName); 792 } 793 } 794 return null; 795 } 796 key(String type, String algorithm)797 private static String key(String type, String algorithm) { 798 return type + '.' + algorithm.toUpperCase(Locale.US); 799 } 800 801 /** 802 * {@code Service} represents a service in the Java Security infrastructure. 803 * Each service describes its type, the algorithm it implements, to which 804 * provider it belongs and other properties. 805 */ 806 public static class Service { 807 /** Attribute name of supported key classes. */ 808 private static final String ATTR_SUPPORTED_KEY_CLASSES = "SupportedKeyClasses"; 809 810 /** Attribute name of supported key formats. */ 811 private static final String ATTR_SUPPORTED_KEY_FORMATS = "SupportedKeyFormats"; 812 813 /** Whether this type supports calls to {@link #supportsParameter(Object)}. */ 814 private static final HashMap<String, Boolean> supportsParameterTypes 815 = new HashMap<String, Boolean>(); 816 static { 817 // Does not support parameter 818 supportsParameterTypes.put("AlgorithmParameterGenerator", false); 819 supportsParameterTypes.put("AlgorithmParameters", false); 820 supportsParameterTypes.put("CertificateFactory", false); 821 supportsParameterTypes.put("CertPathBuilder", false); 822 supportsParameterTypes.put("CertPathValidator", false); 823 supportsParameterTypes.put("CertStore", false); 824 supportsParameterTypes.put("KeyFactory", false); 825 supportsParameterTypes.put("KeyGenerator", false); 826 supportsParameterTypes.put("KeyManagerFactory", false); 827 supportsParameterTypes.put("KeyPairGenerator", false); 828 supportsParameterTypes.put("KeyStore", false); 829 supportsParameterTypes.put("MessageDigest", false); 830 supportsParameterTypes.put("SecretKeyFactory", false); 831 supportsParameterTypes.put("SecureRandom", false); 832 supportsParameterTypes.put("SSLContext", false); 833 supportsParameterTypes.put("TrustManagerFactory", false); 834 835 // Supports parameter 836 supportsParameterTypes.put("Cipher", true); 837 supportsParameterTypes.put("KeyAgreement", true); 838 supportsParameterTypes.put("Mac", true); 839 supportsParameterTypes.put("Signature", true); 840 } 841 842 /** Constructor argument classes for calls to {@link #newInstance(Object)}. */ 843 private static final HashMap<String, Class<?>> constructorParameterClasses = new HashMap<String, Class<?>>(); 844 static { 845 // Types that take a parameter to newInstance 846 constructorParameterClasses.put("CertStore", 847 loadClassOrThrow("java.security.cert.CertStoreParameters")); 848 849 // Types that do not take any kind of parameter 850 constructorParameterClasses.put("AlgorithmParameterGenerator", null); 851 constructorParameterClasses.put("AlgorithmParameters", null); 852 constructorParameterClasses.put("CertificateFactory", null); 853 constructorParameterClasses.put("CertPathBuilder", null); 854 constructorParameterClasses.put("CertPathValidator", null); 855 constructorParameterClasses.put("KeyFactory", null); 856 constructorParameterClasses.put("KeyGenerator", null); 857 constructorParameterClasses.put("KeyManagerFactory", null); 858 constructorParameterClasses.put("KeyPairGenerator", null); 859 constructorParameterClasses.put("KeyStore", null); 860 constructorParameterClasses.put("MessageDigest", null); 861 constructorParameterClasses.put("SecretKeyFactory", null); 862 constructorParameterClasses.put("SecureRandom", null); 863 constructorParameterClasses.put("SSLContext", null); 864 constructorParameterClasses.put("TrustManagerFactory", null); 865 constructorParameterClasses.put("Cipher", null); 866 constructorParameterClasses.put("KeyAgreement", null); 867 constructorParameterClasses.put("Mac", null); 868 constructorParameterClasses.put("Signature", null); 869 } 870 871 /** Called to load a class if it's critical that the class exists. */ loadClassOrThrow(String className)872 private static Class<?> loadClassOrThrow(String className) { 873 try { 874 return Provider.class.getClassLoader().loadClass(className); 875 } catch (Exception e) { 876 throw new AssertionError(e); 877 } 878 } 879 880 // The provider 881 private Provider provider; 882 883 // The type of this service 884 private String type; 885 886 // The algorithm name 887 private String algorithm; 888 889 // The class implementing this service 890 private String className; 891 892 // The aliases 893 private List<String> aliases; 894 895 // The attributes 896 private Map<String,String> attributes; 897 898 // Service implementation 899 private Class<?> implementation; 900 901 // For newInstance() optimization 902 private String lastClassName; 903 904 /** Indicates whether supportedKeyClasses and supportedKeyFormats. */ 905 private volatile boolean supportedKeysInitialized; 906 907 /** List of classes that this service supports. */ 908 private Class<?>[] keyClasses; 909 910 /** List of key formats this service supports. */ 911 private String[] keyFormats; 912 913 /** 914 * Constructs a new instance of {@code Service} with the given 915 * attributes. 916 * 917 * @param provider 918 * the provider to which this service belongs. 919 * @param type 920 * the type of this service (for example {@code 921 * KeyPairGenerator}). 922 * @param algorithm 923 * the algorithm this service implements. 924 * @param className 925 * the name of the class implementing this service. 926 * @param aliases 927 * {@code List} of aliases for the algorithm name, or {@code 928 * null} if the implemented algorithm has no aliases. 929 * @param attributes 930 * {@code Map} of additional attributes, or {@code null} if 931 * this {@code Service} has no attributed. 932 * @throws NullPointerException 933 * if {@code provider, type, algorithm} or {@code className} 934 * is {@code null}. 935 */ Service(Provider provider, String type, String algorithm, String className, List<String> aliases, Map<String, String> attributes)936 public Service(Provider provider, String type, String algorithm, 937 String className, List<String> aliases, Map<String, String> attributes) { 938 if (provider == null) { 939 throw new NullPointerException("provider == null"); 940 } else if (type == null) { 941 throw new NullPointerException("type == null"); 942 } else if (algorithm == null) { 943 throw new NullPointerException("algorithm == null"); 944 } else if (className == null) { 945 throw new NullPointerException("className == null"); 946 } 947 this.provider = provider; 948 this.type = type; 949 this.algorithm = algorithm; 950 this.className = className; 951 this.aliases = ((aliases != null) && (aliases.size() == 0)) 952 ? Collections.<String>emptyList() : aliases; 953 this.attributes = 954 ((attributes != null) && (attributes.size() == 0)) 955 ? Collections.<String,String>emptyMap() : attributes; 956 } 957 958 /** 959 * Adds an alias. 960 * 961 * @param alias the alias to add 962 */ addAlias(String alias)963 /*package*/ void addAlias(String alias) { 964 if ((aliases == null) || (aliases.size() == 0)) { 965 aliases = new ArrayList<String>(); 966 } 967 aliases.add(alias); 968 } 969 970 /** 971 * Puts a new attribute mapping. 972 * 973 * @param name the attribute name. 974 * @param value the attribute value. 975 */ putAttribute(String name, String value)976 /*package*/ void putAttribute(String name, String value) { 977 if ((attributes == null) || (attributes.size() == 0)) { 978 attributes = new HashMap<String,String>(); 979 } 980 attributes.put(name, value); 981 } 982 983 /** 984 * Returns the type of this {@code Service}. For example {@code 985 * KeyPairGenerator}. 986 * 987 * @return the type of this {@code Service}. 988 */ getType()989 public final String getType() { 990 return type; 991 } 992 993 /** 994 * Returns the name of the algorithm implemented by this {@code 995 * Service}. 996 * 997 * @return the name of the algorithm implemented by this {@code 998 * Service}. 999 */ getAlgorithm()1000 public final String getAlgorithm() { 1001 return algorithm; 1002 } 1003 1004 /** 1005 * Returns the {@code Provider} this {@code Service} belongs to. 1006 * 1007 * @return the {@code Provider} this {@code Service} belongs to. 1008 */ getProvider()1009 public final Provider getProvider() { 1010 return provider; 1011 } 1012 1013 /** 1014 * Returns the name of the class implementing this {@code Service}. 1015 * 1016 * @return the name of the class implementing this {@code Service}. 1017 */ getClassName()1018 public final String getClassName() { 1019 return className; 1020 } 1021 1022 /** 1023 * Returns the value of the attribute with the specified {@code name}. 1024 * 1025 * @param name 1026 * the name of the attribute. 1027 * @return the value of the attribute, or {@code null} if no attribute 1028 * with the given name is set. 1029 * @throws NullPointerException 1030 * if {@code name} is {@code null}. 1031 */ getAttribute(String name)1032 public final String getAttribute(String name) { 1033 if (name == null) { 1034 throw new NullPointerException("name == null"); 1035 } 1036 if (attributes == null) { 1037 return null; 1038 } 1039 return attributes.get(name); 1040 } 1041 getAliases()1042 List<String> getAliases() { 1043 if (aliases == null){ 1044 aliases = new ArrayList<String>(0); 1045 } 1046 return aliases; 1047 } 1048 1049 /** 1050 * Creates and returns a new instance of the implementation described by 1051 * this {@code Service}. 1052 * 1053 * @param constructorParameter 1054 * the parameter that is used by the constructor, or {@code 1055 * null} if the implementation does not declare a constructor 1056 * parameter. 1057 * @return a new instance of the implementation described by this 1058 * {@code Service}. 1059 * @throws NoSuchAlgorithmException 1060 * if the instance could not be constructed. 1061 * @throws InvalidParameterException 1062 * if the implementation does not support the specified 1063 * {@code constructorParameter}. 1064 */ newInstance(Object constructorParameter)1065 public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException { 1066 if (implementation == null || !className.equals(lastClassName)) { 1067 ClassLoader cl = provider.getClass().getClassLoader(); 1068 if (cl == null) { 1069 cl = ClassLoader.getSystemClassLoader(); 1070 } 1071 try { 1072 implementation = Class.forName(className, true, cl); 1073 lastClassName = className; 1074 } catch (Exception e) { 1075 throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found: " + e); 1076 } 1077 } 1078 1079 // We don't know whether this takes a parameter or not. 1080 if (!constructorParameterClasses.containsKey(type)) { 1081 if (constructorParameter == null) { 1082 return newInstanceNoParameter(); 1083 } else { 1084 return newInstanceWithParameter(constructorParameter, 1085 constructorParameter.getClass()); 1086 } 1087 } 1088 1089 // A known type, but it's not required to have a parameter even if a 1090 // class is specified. 1091 if (constructorParameter == null) { 1092 return newInstanceNoParameter(); 1093 } 1094 1095 // Make sure the provided constructor class is valid. 1096 final Class<?> expectedClass = constructorParameterClasses.get(type); 1097 if (expectedClass == null) { 1098 throw new IllegalArgumentException("Constructor parameter not supported for " 1099 + type); 1100 } 1101 if (!expectedClass.isAssignableFrom(constructorParameter.getClass())) { 1102 throw new IllegalArgumentException("Expecting constructor parameter of type " 1103 + expectedClass.getName() + " but was " 1104 + constructorParameter.getClass().getName()); 1105 } 1106 return newInstanceWithParameter(constructorParameter, expectedClass); 1107 } 1108 newInstanceWithParameter(Object constructorParameter, Class<?> parameterClass)1109 private Object newInstanceWithParameter(Object constructorParameter, 1110 Class<?> parameterClass) throws NoSuchAlgorithmException { 1111 try { 1112 Class<?>[] parameterTypes = { parameterClass }; 1113 Object[] initargs = { constructorParameter }; 1114 return implementation.getConstructor(parameterTypes).newInstance(initargs); 1115 } catch (Exception e) { 1116 throw new NoSuchAlgorithmException(type + " " + algorithm 1117 + " implementation not found", e); 1118 } 1119 } 1120 newInstanceNoParameter()1121 private Object newInstanceNoParameter() throws NoSuchAlgorithmException { 1122 try { 1123 return implementation.newInstance(); 1124 } catch (Exception e) { 1125 throw new NoSuchAlgorithmException(type + " " + algorithm 1126 + " implementation not found", e); 1127 } 1128 } 1129 1130 /** 1131 * Indicates whether this {@code Service} supports the specified 1132 * constructor parameter. 1133 * 1134 * @param parameter 1135 * the parameter to test. 1136 * @return {@code true} if this {@code Service} supports the specified 1137 * constructor parameter, {@code false} otherwise. 1138 */ supportsParameter(Object parameter)1139 public boolean supportsParameter(Object parameter) { 1140 Boolean supportsParameter = supportsParameterTypes.get(type); 1141 if (supportsParameter == null) { 1142 return true; 1143 } 1144 if (!supportsParameter) { 1145 throw new InvalidParameterException("Cannot use a parameter with " + type); 1146 } 1147 1148 /* 1149 * Only Key parameters are allowed, but allow null since there might 1150 * not be any listed classes or formats for this instance. 1151 */ 1152 if (parameter != null && !(parameter instanceof Key)) { 1153 throw new InvalidParameterException("Parameter should be of type Key"); 1154 } 1155 1156 ensureSupportedKeysInitialized(); 1157 1158 // No restriction specified by Provider registration. 1159 if (keyClasses == null && keyFormats == null) { 1160 return true; 1161 } 1162 1163 // Restriction specified by registration, so null is not acceptable. 1164 if (parameter == null) { 1165 return false; 1166 } 1167 1168 Key keyParam = (Key) parameter; 1169 if (keyClasses != null && isInArray(keyClasses, keyParam.getClass())) { 1170 return true; 1171 } 1172 if (keyFormats != null && isInArray(keyFormats, keyParam.getFormat())) { 1173 return true; 1174 } 1175 1176 return false; 1177 } 1178 1179 /** 1180 * Initialize the list of supported key classes and formats. 1181 */ ensureSupportedKeysInitialized()1182 private void ensureSupportedKeysInitialized() { 1183 if (supportedKeysInitialized) { 1184 return; 1185 } 1186 1187 final String supportedClassesString = getAttribute(ATTR_SUPPORTED_KEY_CLASSES); 1188 if (supportedClassesString != null) { 1189 String[] keyClassNames = supportedClassesString.split("\\|"); 1190 ArrayList<Class<?>> supportedClassList = new ArrayList<Class<?>>( 1191 keyClassNames.length); 1192 final ClassLoader classLoader = getProvider().getClass().getClassLoader(); 1193 for (String keyClassName : keyClassNames) { 1194 try { 1195 Class<?> keyClass = classLoader.loadClass(keyClassName); 1196 if (Key.class.isAssignableFrom(keyClass)) { 1197 supportedClassList.add(keyClass); 1198 } 1199 } catch (ClassNotFoundException ignored) { 1200 } 1201 } 1202 keyClasses = supportedClassList.toArray(new Class<?>[supportedClassList.size()]); 1203 } 1204 1205 final String supportedFormatString = getAttribute(ATTR_SUPPORTED_KEY_FORMATS); 1206 if (supportedFormatString != null) { 1207 keyFormats = supportedFormatString.split("\\|"); 1208 } 1209 1210 supportedKeysInitialized = true; 1211 } 1212 1213 /** 1214 * Check if an item is in the array. The array of supported key classes 1215 * and formats is usually just a length of 1, so a simple array is 1216 * faster than a Set. 1217 */ isInArray(T[] itemList, T target)1218 private static <T> boolean isInArray(T[] itemList, T target) { 1219 if (target == null) { 1220 return false; 1221 } 1222 for (T item : itemList) { 1223 if (target.equals(item)) { 1224 return true; 1225 } 1226 } 1227 return false; 1228 } 1229 1230 /** 1231 * Check if an item is in the array. The array of supported key classes 1232 * and formats is usually just a length of 1, so a simple array is 1233 * faster than a Set. 1234 */ isInArray(Class<?>[] itemList, Class<?> target)1235 private static boolean isInArray(Class<?>[] itemList, Class<?> target) { 1236 if (target == null) { 1237 return false; 1238 } 1239 for (Class<?> item : itemList) { 1240 if (item.isAssignableFrom(target)) { 1241 return true; 1242 } 1243 } 1244 return false; 1245 } 1246 1247 /** 1248 * Returns a string containing a concise, human-readable description of 1249 * this {@code Service}. 1250 * 1251 * @return a printable representation for this {@code Service}. 1252 */ 1253 @Override toString()1254 public String toString() { 1255 String result = "Provider " + provider.getName() + " Service " 1256 + type + "." + algorithm + " " + className; 1257 if (aliases != null) { 1258 result = result + "\nAliases " + aliases.toString(); 1259 } 1260 if (attributes != null) { 1261 result = result + "\nAttributes " + attributes.toString(); 1262 } 1263 return result; 1264 } 1265 } 1266 readObject(java.io.ObjectInputStream in)1267 private void readObject(java.io.ObjectInputStream in) 1268 throws NotActiveException, IOException, ClassNotFoundException { 1269 in.defaultReadObject(); 1270 versionString = String.valueOf(version); 1271 providerNumber = -1; 1272 } 1273 } 1274