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.HashSet; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Properties; 33 import java.util.Set; 34 import org.apache.harmony.luni.util.TwoKeyHashMap; 35 import org.apache.harmony.security.Util; 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 TwoKeyHashMap<String, String, Service> serviceTable; 61 62 // Contains "Service.Alias" and Provider.Service classes added using 63 // putService() 64 private transient TwoKeyHashMap<String, String, Service> aliasTable; 65 66 // Contains "Service.Algorithm" and Provider.Service classes added using 67 // put() 68 private transient TwoKeyHashMap<String, String, Service> propertyServiceTable; 69 70 // Contains "Service.Alias" and Provider.Service classes added using put() 71 private transient TwoKeyHashMap<String, String, Service> propertyAliasTable; 72 73 // The properties changed via put() 74 private transient Properties 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 */ 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 */ 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 */ 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 */ 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 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 * <p> 153 * If a {@code SecurityManager} is installed, code calling this method needs 154 * the {@code SecurityPermission} {@code clearProviderProperties.NAME} 155 * (where NAME is the provider name) to be granted, otherwise a {@code 156 * SecurityException} will be thrown. 157 * 158 * @throws SecurityException 159 * if a {@code SecurityManager} is installed and the caller does 160 * not have permission to invoke this method. 161 */ 162 @Override 163 public synchronized void clear() { 164 SecurityManager sm = System.getSecurityManager(); 165 if (sm != null) { 166 sm.checkSecurityAccess("clearProviderProperties." + name); 167 } 168 super.clear(); 169 if (serviceTable != null) { 170 serviceTable.clear(); 171 } 172 if (propertyServiceTable != null) { 173 propertyServiceTable.clear(); 174 } 175 if (aliasTable != null) { 176 aliasTable.clear(); 177 } 178 if (propertyAliasTable != null) { 179 propertyAliasTable.clear(); 180 } 181 // BEGIN android-changed 182 changedProperties = null; 183 // END android-changed 184 putProviderInfo(); 185 if (providerNumber != -1) { 186 // if registered then refresh Services 187 Services.setNeedRefresh(); 188 } 189 servicesChanged(); 190 } 191 192 @Override 193 public synchronized void load(InputStream inStream) throws IOException { 194 Properties tmp = new Properties(); 195 tmp.load(inStream); 196 myPutAll(tmp); 197 } 198 199 /** 200 * Copies all from the provided map to this {@code Provider}. 201 * <p> 202 * If a {@code SecurityManager} is installed, code calling this method needs 203 * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where 204 * NAME is the provider name) to be granted, otherwise a {@code 205 * SecurityException} will be thrown. 206 * 207 * @param t 208 * the mappings to copy to this provider. 209 * @throws SecurityException 210 * if a {@code SecurityManager} is installed and the caller does 211 * not have permission to invoke this method. 212 */ 213 @Override 214 public synchronized void putAll(Map<?,?> t) { 215 216 // Implementation note: 217 // checkSecurityAccess method call is NOT specified 218 // Do it as in put(Object key, Object value). 219 220 SecurityManager sm = System.getSecurityManager(); 221 if (sm != null) { 222 sm.checkSecurityAccess("putProviderProperty." + name); 223 } 224 myPutAll(t); 225 } 226 227 private void myPutAll(Map<?,?> t) { 228 if (changedProperties == null) { 229 changedProperties = new Properties(); 230 } 231 Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator(); 232 Object key; 233 Object value; 234 while (it.hasNext()) { 235 Map.Entry<?, ?> entry = it.next(); 236 key = entry.getKey(); 237 if (key instanceof String && ((String) key).startsWith("Provider.")) { 238 // Provider service type is reserved 239 continue; 240 } 241 value = entry.getValue(); 242 super.put(key, value); 243 if (changedProperties.remove(key) == null) { 244 removeFromPropertyServiceTable(key); 245 } 246 changedProperties.put(key, value); 247 } 248 if (providerNumber != -1) { 249 // if registered then refresh Services 250 Services.setNeedRefresh(); 251 } 252 } 253 254 @Override 255 public synchronized Set<Map.Entry<Object,Object>> entrySet() { 256 return Collections.unmodifiableSet(super.entrySet()); 257 } 258 259 @Override 260 public Set<Object> keySet() { 261 return Collections.unmodifiableSet(super.keySet()); 262 } 263 264 @Override 265 public Collection<Object> values() { 266 return Collections.unmodifiableCollection(super.values()); 267 } 268 269 /** 270 * Maps the specified {@code key} property name to the specified {@code 271 * value}. 272 * <p> 273 * If a {@code SecurityManager} is installed, code calling this method needs 274 * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where 275 * NAME is the provider name) to be granted, otherwise a {@code 276 * SecurityException} will be thrown. 277 * 278 * @param key 279 * the name of the property. 280 * @param value 281 * the value of the property. 282 * @return the value that was previously mapped to the specified {@code key} 283 * ,or {@code null} if it did not have one. 284 * @throws SecurityException 285 * if a {@code SecurityManager} is installed and the caller does 286 * not have permission to invoke this method. 287 */ 288 @Override 289 public synchronized Object put(Object key, Object value) { 290 SecurityManager sm = System.getSecurityManager(); 291 if (sm != null) { 292 sm.checkSecurityAccess("putProviderProperty." + name); 293 } 294 if (key instanceof String && ((String) key).startsWith("Provider.")) { 295 // Provider service type is reserved 296 return null; 297 } 298 if (providerNumber != -1) { 299 // if registered then refresh Services 300 Services.setNeedRefresh(); 301 } 302 if (changedProperties != null && changedProperties.remove(key) == null) { 303 removeFromPropertyServiceTable(key); 304 } 305 if (changedProperties == null) { 306 changedProperties = new Properties(); 307 } 308 changedProperties.put(key, value); 309 return super.put(key, value); 310 } 311 312 /** 313 * Removes the specified {@code key} and its associated value from this 314 * {@code Provider}. 315 * <p> 316 * If a {@code SecurityManager} is installed, code calling this method needs 317 * the {@code SecurityPermission} {@code removeProviderProperty.NAME} (where 318 * NAME is the provider name) to be granted, otherwise a {@code 319 * SecurityException} will be thrown. 320 * 321 * @param key 322 * the name of the property 323 * @return the value that was mapped to the specified {@code key} ,or 324 * {@code null} if no mapping was present 325 * @throws SecurityException 326 * if a {@code SecurityManager} is installed and the caller does 327 * not have the permission to invoke this method. 328 */ 329 @Override 330 public synchronized Object remove(Object key) { 331 SecurityManager sm = System.getSecurityManager(); 332 if (sm != null) { 333 sm.checkSecurityAccess("removeProviderProperty." + name); 334 } 335 if (key instanceof String && ((String) key).startsWith("Provider.")) { 336 // Provider service type is reserved 337 return null; 338 } 339 if (providerNumber != -1) { 340 // if registered then refresh Services 341 Services.setNeedRefresh(); 342 } 343 if (changedProperties != null && changedProperties.remove(key) == null) { 344 removeFromPropertyServiceTable(key); 345 // BEGIN android-added 346 if (changedProperties.size() == 0) { 347 changedProperties = null; 348 } 349 // END android-added 350 } 351 return super.remove(key); 352 } 353 354 /** 355 * Returns true if this provider implements the given algorithm. Caller 356 * must specify the cryptographic service and specify constraints via the 357 * attribute name and value. 358 * 359 * @param serv 360 * Crypto service. 361 * @param alg 362 * Algorithm or type. 363 * @param attribute 364 * The attribute name or {@code null}. 365 * @param val 366 * The attribute value. 367 * @return 368 */ 369 boolean implementsAlg(String serv, String alg, String attribute, String val) { 370 String servAlg = serv + "." + alg; 371 String prop = getPropertyIgnoreCase(servAlg); 372 if (prop == null) { 373 alg = getPropertyIgnoreCase("Alg.Alias." + servAlg); 374 if (alg != null) { 375 servAlg = serv + "." + alg; 376 prop = getPropertyIgnoreCase(servAlg); 377 } 378 } 379 if (prop != null) { 380 if (attribute == null) { 381 return true; 382 } 383 return checkAttribute(servAlg, attribute, val); 384 } 385 return false; 386 } 387 388 // Returns true if this provider has the same value as is given for the 389 // given attribute 390 private boolean checkAttribute(String servAlg, String attribute, String val) { 391 392 String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute); 393 if (attributeValue != null) { 394 if (Util.equalsIgnoreCase(attribute,"KeySize")) { 395 // BEGIN android-changed 396 if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) { 397 return true; 398 } 399 // END android-changed 400 } else { // other attributes 401 if (Util.equalsIgnoreCase(attributeValue, val)) { 402 return true; 403 } 404 } 405 } 406 return false; 407 } 408 409 /** 410 * 411 * Set the provider preference order number. 412 * 413 * @param n 414 */ 415 void setProviderNumber(int n) { 416 providerNumber = n; 417 } 418 419 /** 420 * 421 * Get the provider preference order number. 422 * 423 * @return 424 */ 425 int getProviderNumber() { 426 return providerNumber; 427 } 428 429 /** 430 * Get the service of the specified type 431 * 432 */ 433 synchronized Provider.Service getService(String type) { 434 updatePropertyServiceTable(); 435 if (lastServicesByType != null && type.equals(lastType)) { 436 return lastServicesByType; 437 } 438 Provider.Service service; 439 for (Iterator<Service> it = getServices().iterator(); it.hasNext();) { 440 service = it.next(); 441 if (type.equals(service.type)) { 442 lastType = type; 443 lastServicesByType = service; 444 return service; 445 } 446 } 447 return null; 448 } 449 450 /** 451 * Returns the service with the specified {@code type} implementing the 452 * specified {@code algorithm}, or {@code null} if no such implementation 453 * exists. 454 * <p> 455 * If two services match the requested type and algorithm, the one added 456 * with the {@link #putService(Service)} is returned (as opposed to the one 457 * added via {@link #put(Object, Object)}. 458 * 459 * @param type 460 * the type of the service (for example {@code KeyPairGenerator}) 461 * @param algorithm 462 * the algorithm name (case insensitive) 463 * @return the requested service, or {@code null} if no such implementation 464 * exists 465 */ 466 public synchronized Provider.Service getService(String type, 467 String algorithm) { 468 if (type == null || algorithm == null) { 469 throw new NullPointerException(); 470 } 471 472 if (type.equals(lastServiceName) 473 && Util.equalsIgnoreCase(algorithm, lastAlgorithm)) { 474 return returnedService; 475 } 476 477 String alg = Util.toUpperCase(algorithm); 478 Object o = null; 479 if (serviceTable != null) { 480 o = serviceTable.get(type, alg); 481 } 482 if (o == null && aliasTable != null) { 483 o = aliasTable.get(type, alg); 484 } 485 if (o == null) { 486 updatePropertyServiceTable(); 487 } 488 if (o == null && propertyServiceTable != null) { 489 o = propertyServiceTable.get(type, alg); 490 } 491 if (o == null && propertyAliasTable != null) { 492 o = propertyAliasTable.get(type, alg); 493 } 494 495 if (o != null) { 496 lastServiceName = type; 497 lastAlgorithm = algorithm; 498 returnedService = (Provider.Service) o; 499 return returnedService; 500 } 501 return null; 502 } 503 504 /** 505 * Returns an unmodifiable {@code Set} of all services registered by this 506 * provider. 507 * 508 * @return an unmodifiable {@code Set} of all services registered by this 509 * provider 510 */ 511 public synchronized Set<Provider.Service> getServices() { 512 updatePropertyServiceTable(); 513 if (lastServicesSet != null) { 514 return lastServicesSet; 515 } 516 if (serviceTable != null) { 517 lastServicesSet = new HashSet<Service>(serviceTable.values()); 518 } else { 519 lastServicesSet = new HashSet<Service>(); 520 } 521 if (propertyServiceTable != null) { 522 lastServicesSet.addAll(propertyServiceTable.values()); 523 } 524 lastServicesSet = Collections.unmodifiableSet(lastServicesSet); 525 return lastServicesSet; 526 } 527 528 /** 529 * Adds a {@code Service} to this {@code Provider}. If a service with the 530 * same name was registered via this method, it is replace. 531 * <p> 532 * If a {@code SecurityManager} is installed, code calling this method needs 533 * the {@code SecurityPermission} {@code putProviderProperty.NAME} (where 534 * NAME is the provider name) to be granted, otherwise a {@code 535 * SecurityException} will be thrown. 536 * 537 * @param s 538 * the {@code Service} to register 539 * @throws SecurityException 540 * if a {@code SecurityManager} is installed and the caller does 541 * not have permission to invoke this method 542 */ 543 protected synchronized void putService(Provider.Service s) { 544 if (s == null) { 545 throw new NullPointerException(); 546 } 547 SecurityManager sm = System.getSecurityManager(); 548 if (sm != null) { 549 sm.checkSecurityAccess("putProviderProperty." + name); 550 } 551 if ("Provider".equals(s.getType())) { // Provider service type cannot be added 552 return; 553 } 554 servicesChanged(); 555 if (serviceTable == null) { 556 serviceTable = new TwoKeyHashMap<String, String, Service>(128); 557 } 558 serviceTable.put(s.type, Util.toUpperCase(s.algorithm), s); 559 if (s.aliases != null) { 560 if (aliasTable == null) { 561 aliasTable = new TwoKeyHashMap<String, String, Service>(256); 562 } 563 for (String alias : s.getAliases()) { 564 aliasTable.put(s.type, Util.toUpperCase(alias), s); 565 } 566 } 567 serviceInfoToProperties(s); 568 } 569 570 /** 571 * Removes a previously registered {@code Service} from this {@code 572 * Provider}. 573 * <p> 574 * If a {@code SecurityManager} is installed, code calling this method needs 575 * the {@code SecurityPermission} {@code removeProviderProperty.NAME} (where 576 * NAME is the provider name) to be granted, otherwise a {@code 577 * SecurityException} will be thrown. 578 * 579 * @param s 580 * the {@code Service} to remove 581 * @throws SecurityException 582 * if a {@code SecurityManager} is installed and the caller does 583 * not have permission to invoke this method 584 * @throws NullPointerException 585 * if {@code s} is {@code null} 586 */ 587 protected synchronized void removeService(Provider.Service s) { 588 if (s == null) { 589 throw new NullPointerException(); 590 } 591 SecurityManager sm = System.getSecurityManager(); 592 if (sm != null) { 593 sm.checkSecurityAccess("removeProviderProperty." + name); 594 } 595 servicesChanged(); 596 if (serviceTable != null) { 597 serviceTable.remove(s.type, Util.toUpperCase(s.algorithm)); 598 } 599 if (aliasTable != null && s.aliases != null) { 600 for (String alias: s.getAliases()) { 601 aliasTable.remove(s.type, Util.toUpperCase(alias)); 602 } 603 } 604 serviceInfoFromProperties(s); 605 } 606 607 /** 608 * Add Service information to the provider's properties. 609 */ 610 private void serviceInfoToProperties(Provider.Service s) { 611 super.put(s.type + "." + s.algorithm, s.className); 612 if (s.aliases != null) { 613 for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { 614 super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm); 615 } 616 } 617 if (s.attributes != null) { 618 for (Map.Entry<String, String> entry : s.attributes.entrySet()) { 619 super.put(s.type + "." + s.algorithm + " " + entry.getKey(), 620 entry.getValue()); 621 } 622 } 623 if (providerNumber != -1) { 624 // if registered then refresh Services 625 Services.setNeedRefresh(); 626 } 627 } 628 629 /** 630 * Remove Service information from the provider's properties. 631 */ 632 private void serviceInfoFromProperties(Provider.Service s) { 633 super.remove(s.type + "." + s.algorithm); 634 if (s.aliases != null) { 635 for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) { 636 super.remove("Alg.Alias." + s.type + "." + i.next()); 637 } 638 } 639 if (s.attributes != null) { 640 for (Map.Entry<String, String> entry : s.attributes.entrySet()) { 641 super.remove(s.type + "." + s.algorithm + " " + entry.getKey()); 642 } 643 } 644 if (providerNumber != -1) { 645 // if registered then refresh Services 646 Services.setNeedRefresh(); 647 } 648 } 649 650 // Remove property information from provider Services 651 private void removeFromPropertyServiceTable(Object key) { 652 if (key == null || !(key instanceof String)) { 653 return; 654 } 655 String k = (String) key; 656 if (k.startsWith("Provider.")) { // Provider service type is reserved 657 return; 658 } 659 Provider.Service s; 660 String serviceName; 661 String algorithm = null; 662 String attribute = null; 663 int i; 664 if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName> 665 String aliasName; 666 String service_alias = k.substring(10); 667 i = service_alias.indexOf('.'); 668 serviceName = service_alias.substring(0, i); 669 aliasName = service_alias.substring(i + 1); 670 if (propertyAliasTable != null) { 671 propertyAliasTable.remove(serviceName, Util.toUpperCase(aliasName)); 672 } 673 if (propertyServiceTable != null) { 674 for (Iterator<Service> it = propertyServiceTable.values().iterator(); it 675 .hasNext();) { 676 s = it.next(); 677 if (s.aliases.contains(aliasName)) { 678 s.aliases.remove(aliasName); 679 return; 680 } 681 } 682 } 683 return; 684 } 685 int j = k.indexOf('.'); 686 if (j == -1) { // unknown format 687 return; 688 } 689 690 i = k.indexOf(' '); 691 if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> 692 serviceName = k.substring(0, j); 693 algorithm = k.substring(j + 1); 694 if (propertyServiceTable != null) { 695 Provider.Service ser = propertyServiceTable.remove(serviceName, 696 Util.toUpperCase(algorithm)); 697 if (ser != null && propertyAliasTable != null 698 && ser.aliases != null) { 699 for (Iterator<String> it = ser.aliases.iterator(); it.hasNext();) { 700 propertyAliasTable.remove(serviceName, Util.toUpperCase(it 701 .next())); 702 } 703 } 704 } 705 } else { 706 // <crypto_service>.<algorithm_or_type> 707 // <attribute_name>=<attrValue> 708 attribute = k.substring(i + 1); 709 serviceName = k.substring(0, j); 710 algorithm = k.substring(j + 1, i); 711 if (propertyServiceTable != null) { 712 Object o = propertyServiceTable.get(serviceName, Util.toUpperCase(algorithm)); 713 if (o != null) { 714 s = (Provider.Service) o; 715 s.attributes.remove(attribute); 716 } 717 } 718 } 719 } 720 721 // Update provider Services if the properties was changed 722 private void updatePropertyServiceTable() { 723 Object _key; 724 Object _value; 725 Provider.Service s; 726 String serviceName; 727 String algorithm; 728 if (changedProperties == null || changedProperties.isEmpty()) { 729 return; 730 } 731 for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it 732 .hasNext();) { 733 Map.Entry<Object, Object> entry = it.next(); 734 _key = entry.getKey(); 735 _value = entry.getValue(); 736 if (_key == null || _value == null || !(_key instanceof String) 737 || !(_value instanceof String)) { 738 continue; 739 } 740 String key = (String) _key; 741 String value = (String) _value; 742 if (key.startsWith("Provider")) { 743 // Provider service type is reserved 744 continue; 745 } 746 int i; 747 if (key.startsWith("Alg.Alias.")) { 748 // Alg.Alias.<crypto_service>.<aliasName>=<standardName> 749 String aliasName; 750 String service_alias = key.substring(10); 751 i = service_alias.indexOf('.'); 752 serviceName = service_alias.substring(0, i); 753 aliasName = service_alias.substring(i + 1); 754 algorithm = value; 755 String algUp = Util.toUpperCase(algorithm); 756 Object o = null; 757 if (propertyServiceTable == null) { 758 propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); 759 } else { 760 o = propertyServiceTable.get(serviceName, algUp); 761 } 762 if (o != null) { 763 s = (Provider.Service) o; 764 // BEGIN android-changed 765 s.addAlias(aliasName); 766 // END android-changed 767 if (propertyAliasTable == null) { 768 propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256); 769 } 770 propertyAliasTable.put(serviceName, 771 Util.toUpperCase(aliasName), s); 772 } else { 773 String className = (String) changedProperties 774 .get(serviceName + "." + algorithm); 775 if (className != null) { 776 List<String> l = new ArrayList<String>(); 777 l.add(aliasName); 778 s = new Provider.Service(this, serviceName, algorithm, 779 className, l, new HashMap<String, String>()); 780 propertyServiceTable.put(serviceName, algUp, s); 781 if (propertyAliasTable == null) { 782 propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256); 783 } 784 propertyAliasTable.put(serviceName, Util.toUpperCase(aliasName 785 ), s); 786 } 787 } 788 continue; 789 } 790 int j = key.indexOf('.'); 791 if (j == -1) { // unknown format 792 continue; 793 } 794 i = key.indexOf(' '); 795 if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> 796 serviceName = key.substring(0, j); 797 algorithm = key.substring(j + 1); 798 String alg = Util.toUpperCase(algorithm); 799 Object o = null; 800 if (propertyServiceTable != null) { 801 o = propertyServiceTable.get(serviceName, alg); 802 } 803 if (o != null) { 804 s = (Provider.Service) o; 805 s.className = value; 806 } else { 807 // BEGIN android-changed 808 s = new Provider.Service(this, serviceName, algorithm, 809 value, Collections.<String>emptyList(), 810 Collections.<String,String>emptyMap()); 811 // END android-changed 812 if (propertyServiceTable == null) { 813 propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); 814 } 815 propertyServiceTable.put(serviceName, alg, s); 816 817 } 818 } else { 819 // <crypto_service>.<algorithm_or_type> 820 // <attribute_name>=<attrValue> 821 serviceName = key.substring(0, j); 822 algorithm = key.substring(j + 1, i); 823 String attribute = key.substring(i + 1); 824 String alg = Util.toUpperCase(algorithm); 825 Object o = null; 826 if (propertyServiceTable != null) { 827 o = propertyServiceTable.get(serviceName, alg); 828 } 829 if (o != null) { 830 s = (Provider.Service) o; 831 // BEGIN android-changed 832 s.putAttribute(attribute, value); 833 // END android-changed 834 } else { 835 String className = (String) changedProperties 836 .get(serviceName + "." + algorithm); 837 if (className != null) { 838 Map<String, String> m = new HashMap<String, String>(); 839 m.put(attribute, value); 840 s = new Provider.Service(this, serviceName, algorithm, 841 className, new ArrayList<String>(), m); 842 if (propertyServiceTable == null) { 843 propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); 844 } 845 propertyServiceTable.put(serviceName, alg, s); 846 } 847 } 848 } 849 } 850 servicesChanged(); 851 // BEGIN android-changed 852 changedProperties = null; 853 // END android-changed 854 } 855 856 private void servicesChanged() { 857 lastServicesByType = null; 858 lastServiceName = null; 859 lastServicesSet = null; 860 } 861 862 /** 863 * These attributes should be placed in each Provider object: 864 * Provider.id name, Provider.id version, Provider.id info, 865 * Provider.id className 866 */ 867 @SuppressWarnings("nls") 868 private void putProviderInfo() { 869 super.put("Provider.id name", null != name ? name : "null"); 870 super.put("Provider.id version", versionString); 871 super.put("Provider.id info", null != info ? info : "null"); 872 super.put("Provider.id className", this.getClass().getName()); 873 } 874 875 /** 876 * Searches for the property with the specified key in the 877 * provider properties. Key is not case-sensitive. 878 * 879 * @param prop 880 * @return the property value with the specified key value. 881 */ 882 private String getPropertyIgnoreCase(String key) { 883 String res = getProperty(key); 884 if (res != null) { 885 return res; 886 } 887 for (Enumeration<?> e = propertyNames(); e.hasMoreElements();) { 888 String pname = (String) e.nextElement(); 889 if (Util.equalsIgnoreCase(key, pname)) { 890 return getProperty(pname); 891 } 892 } 893 return null; 894 } 895 896 /** 897 * {@code Service} represents a service in the Java Security infrastructure. 898 * Each service describes its type, the algorithm it implements, to which 899 * provider it belongs and other properties. 900 */ 901 public static class Service { 902 // The provider 903 private Provider provider; 904 905 // The type of this service 906 private String type; 907 908 // The algorithm name 909 private String algorithm; 910 911 // The class implementing this service 912 private String className; 913 914 // The aliases 915 private List<String> aliases; 916 917 // The attributes 918 private Map<String,String> attributes; 919 920 // Service implementation 921 private Class<?> implementation; 922 923 // For newInstance() optimization 924 private String lastClassName; 925 926 /** 927 * Constructs a new instance of {@code Service} with the given 928 * attributes. 929 * 930 * @param provider 931 * the provider to which this service belongs. 932 * @param type 933 * the type of this service (for example {@code 934 * KeyPairGenerator}). 935 * @param algorithm 936 * the algorithm this service implements. 937 * @param className 938 * the name of the class implementing this service. 939 * @param aliases 940 * {@code List} of aliases for the algorithm name, or {@code 941 * null} if the implemented algorithm has no aliases. 942 * @param attributes 943 * {@code Map} of additional attributes, or {@code null} if 944 * this {@code Service} has no attributed. 945 * @throws NullPointerException 946 * if {@code provider, type, algorithm} or {@code className} 947 * is {@code null}. 948 */ 949 public Service(Provider provider, String type, String algorithm, 950 String className, List<String> aliases, Map<String, String> attributes) { 951 if (provider == null || type == null || algorithm == null 952 || className == null) { 953 throw new NullPointerException(); 954 } 955 this.provider = provider; 956 this.type = type; 957 this.algorithm = algorithm; 958 this.className = className; 959 // BEGIN android-changed 960 this.aliases = ((aliases != null) && (aliases.size() == 0)) 961 ? Collections.<String>emptyList() : aliases; 962 this.attributes = 963 ((attributes != null) && (attributes.size() == 0)) 964 ? Collections.<String,String>emptyMap() : attributes; 965 // END android-changed 966 } 967 968 // BEGIN android-added 969 /** 970 * Adds an alias. 971 * 972 * @param alias the alias to add 973 */ 974 /*package*/ void addAlias(String alias) { 975 if ((aliases == null) || (aliases.size() == 0)) { 976 aliases = new ArrayList<String>(); 977 } 978 aliases.add(alias); 979 } 980 981 /** 982 * Puts a new attribute mapping. 983 * 984 * @param name the attribute name. 985 * @param value the attribute value. 986 */ 987 /*package*/ void putAttribute(String name, String value) { 988 if ((attributes == null) || (attributes.size() == 0)) { 989 attributes = new HashMap<String,String>(); 990 } 991 attributes.put(name, value); 992 } 993 // END android-added 994 995 /** 996 * Returns the type of this {@code Service}. For example {@code 997 * KeyPairGenerator}. 998 * 999 * @return the type of this {@code Service}. 1000 */ 1001 public final String getType() { 1002 return type; 1003 } 1004 1005 /** 1006 * Returns the name of the algorithm implemented by this {@code 1007 * Service}. 1008 * 1009 * @return the name of the algorithm implemented by this {@code 1010 * Service}. 1011 */ 1012 public final String getAlgorithm() { 1013 return algorithm; 1014 } 1015 1016 /** 1017 * Returns the {@code Provider} this {@code Service} belongs to. 1018 * 1019 * @return the {@code Provider} this {@code Service} belongs to. 1020 */ 1021 public final Provider getProvider() { 1022 return provider; 1023 } 1024 1025 /** 1026 * Returns the name of the class implementing this {@code Service}. 1027 * 1028 * @return the name of the class implementing this {@code Service}. 1029 */ 1030 public final String getClassName() { 1031 return className; 1032 } 1033 1034 /** 1035 * Returns the value of the attribute with the specified {@code name}. 1036 * 1037 * @param name 1038 * the name of the attribute. 1039 * @return the value of the attribute, or {@code null} if no attribute 1040 * with the given name is set. 1041 * @throws NullPointerException 1042 * if {@code name} is {@code null}. 1043 */ 1044 public final String getAttribute(String name) { 1045 if (name == null) { 1046 throw new NullPointerException(); 1047 } 1048 if (attributes == null) { 1049 return null; 1050 } 1051 return attributes.get(name); 1052 } 1053 1054 List<String> getAliases() { 1055 if (aliases == null){ 1056 aliases = new ArrayList<String>(0); 1057 } 1058 return aliases; 1059 } 1060 1061 /** 1062 * Creates and returns a new instance of the implementation described by 1063 * this {@code Service}. 1064 * 1065 * @param constructorParameter 1066 * the parameter that is used by the constructor, or {@code 1067 * null} if the implementation does not declare a constructor 1068 * parameter. 1069 * @return a new instance of the implementation described by this 1070 * {@code Service}. 1071 * @throws NoSuchAlgorithmException 1072 * if the instance could not be constructed. 1073 * @throws InvalidParameterException 1074 * if the implementation does not support the specified 1075 * {@code constructorParameter}. 1076 */ 1077 public Object newInstance(Object constructorParameter) 1078 throws NoSuchAlgorithmException { 1079 if (implementation == null || !className.equals(lastClassName)) { 1080 NoSuchAlgorithmException result = AccessController 1081 .doPrivileged(new PrivilegedAction<NoSuchAlgorithmException>() { 1082 public NoSuchAlgorithmException run() { 1083 ClassLoader cl = provider.getClass() 1084 .getClassLoader(); 1085 if (cl == null) { 1086 cl = ClassLoader.getSystemClassLoader(); 1087 } 1088 try { 1089 implementation = Class.forName(className, 1090 true, cl); 1091 } catch (Exception e) { 1092 return new NoSuchAlgorithmException( 1093 type + " " + algorithm 1094 + " implementation not found: " + e); 1095 } 1096 lastClassName = className; 1097 return null; 1098 } 1099 }); 1100 if (result != null) { 1101 throw result; 1102 } 1103 } 1104 if (constructorParameter == null) { 1105 try { 1106 return implementation.newInstance(); 1107 } catch (Exception e) { 1108 throw new NoSuchAlgorithmException( 1109 type + " " + algorithm + " implementation not found", e); 1110 } 1111 } 1112 if (!supportsParameter(constructorParameter)) { 1113 throw new InvalidParameterException(type + ": service cannot use the parameter"); 1114 } 1115 1116 Class[] parameterTypes = new Class[1]; 1117 Object[] initargs = { constructorParameter }; 1118 try { 1119 if (Util.equalsIgnoreCase(type,"CertStore")) { 1120 parameterTypes[0] = Class 1121 .forName("java.security.cert.CertStoreParameters"); 1122 } else { 1123 parameterTypes[0] = constructorParameter.getClass(); 1124 } 1125 return implementation.getConstructor(parameterTypes) 1126 .newInstance(initargs); 1127 } catch (Exception e) { 1128 throw new NoSuchAlgorithmException(type + " " + algorithm 1129 + " implementation not found", e); 1130 } 1131 } 1132 1133 /** 1134 * Indicates whether this {@code Service} supports the specified 1135 * constructor parameter. 1136 * 1137 * @param parameter 1138 * the parameter to test. 1139 * @return {@code true} if this {@code Service} supports the specified 1140 * constructor parameter, {@code false} otherwise. 1141 */ 1142 public boolean supportsParameter(Object parameter) { 1143 return true; 1144 } 1145 1146 /** 1147 * Returns a string containing a concise, human-readable description of 1148 * this {@code Service}. 1149 * 1150 * @return a printable representation for this {@code Service}. 1151 */ 1152 @Override 1153 public String toString() { 1154 String result = "Provider " + provider.getName() + " Service " 1155 + type + "." + algorithm + " " + className; 1156 if (aliases != null) { 1157 result = result + "\nAliases " + aliases.toString(); 1158 } 1159 if (attributes != null) { 1160 result = result + "\nAttributes " + attributes.toString(); 1161 } 1162 return result; 1163 } 1164 } 1165 1166 private void readObject(java.io.ObjectInputStream in) 1167 throws NotActiveException, IOException, ClassNotFoundException { 1168 in.defaultReadObject(); 1169 versionString = String.valueOf(version); 1170 providerNumber = -1; 1171 } 1172 } 1173