1 /*
   2  * Copyright (c) 1996, 2006, 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 
  26 package java.security;
  27 
  28 import java.io.*;
  29 import java.util.*;
  30 import static java.util.Locale.ENGLISH;
  31 import java.lang.ref.*;
  32 import java.lang.reflect.*;
  33 
  34 import java.security.cert.CertStoreParameters;
  35 import javax.security.auth.login.Configuration;
  36 
  37 /**
  38  * This class represents a "provider" for the
  39  * Java Security API, where a provider implements some or all parts of
  40  * Java Security. Services that a provider may implement include:
  41  *
  42  * <ul>
  43  *
  44  * <li>Algorithms (such as DSA, RSA, MD5 or SHA-1).
  45  *
  46  * <li>Key generation, conversion, and management facilities (such as for
  47  * algorithm-specific keys).
  48  *
  49  *</ul>
  50  *
  51  * <p>Each provider has a name and a version number, and is configured
  52  * in each runtime it is installed in.
  53  *
  54  * <p>See <a href =
  55  * "../../../technotes/guides/security/crypto/CryptoSpec.html#Provider">The Provider Class</a>
  56  * in the "Java Cryptography Architecture API Specification &amp; Reference"
  57  * for information about how a particular type of provider, the
  58  * cryptographic service provider, works and is installed. However,
  59  * please note that a provider can be used to implement any security
  60  * service in Java that uses a pluggable architecture with a choice
  61  * of implementations that fit underneath.
  62  *
  63  * <p>Some provider implementations may encounter unrecoverable internal
  64  * errors during their operation, for example a failure to communicate with a
  65  * security token. A {@link ProviderException} should be used to indicate
  66  * such errors.
  67  *
  68  * <p>The service type <code>Provider</code> is reserved for use by the
  69  * security framework. Services of this type cannot be added, removed,
  70  * or modified by applications.
  71  * The following attributes are automatically placed in each Provider object:
  72  * <table cellspacing=4>
  73  * <tr><th>Name</th><th>Value</th>
  74  * <tr><td><code>Provider.id name</code></td>
  75   *    <td><code>String.valueOf(provider.getName())</code></td>
  76  * <tr><td><code>Provider.id version</code></td>
  77  *     <td><code>String.valueOf(provider.getVersion())</code></td>
  78  * <tr><td><code>Provider.id info</code></td>
  79        <td><code>String.valueOf(provider.getInfo())</code></td>
  80  * <tr><td><code>Provider.id className</code></td>
  81  *     <td><code>provider.getClass().getName()</code></td>
  82  * </table>
  83  *
  84  * @author Benjamin Renaud
  85  * @author Andreas Sterbenz
  86  */
  87 public abstract class Provider extends Properties {
  88 
  89     // Declare serialVersionUID to be compatible with JDK1.1
  90     static final long serialVersionUID = -4298000515446427739L;
  91 
  92     private static final sun.security.util.Debug debug =
  93         sun.security.util.Debug.getInstance
  94         ("provider", "Provider");
  95 
  96     /**
  97      * The provider name.
  98      *
  99      * @serial
 100      */
 101     private String name;
 102 
 103     /**
 104      * A description of the provider and its services.
 105      *
 106      * @serial
 107      */
 108     private String info;
 109 
 110     /**
 111      * The provider version number.
 112      *
 113      * @serial
 114      */
 115     private double version;
 116 
 117 
 118     private transient Set<Map.Entry<Object,Object>> entrySet = null;
 119     private transient int entrySetCallCount = 0;
 120 
 121     private transient boolean initialized;
 122 
 123     /**
 124      * Constructs a provider with the specified name, version number,
 125      * and information.
 126      *
 127      * @param name the provider name.
 128      *
 129      * @param version the provider version number.
 130      *
 131      * @param info a description of the provider and its services.
 132      */
 133     protected Provider(String name, double version, String info) {
 134         this.name = name;
 135         this.version = version;
 136         this.info = info;
 137         putId();
 138         initialized = true;
 139     }
 140 
 141     /**
 142      * Returns the name of this provider.
 143      *
 144      * @return the name of this provider.
 145      */
 146     public String getName() {
 147         return name;
 148     }
 149 
 150     /**
 151      * Returns the version number for this provider.
 152      *
 153      * @return the version number for this provider.
 154      */
 155     public double getVersion() {
 156         return version;
 157     }
 158 
 159     /**
 160      * Returns a human-readable description of the provider and its
 161      * services.  This may return an HTML page, with relevant links.
 162      *
 163      * @return a description of the provider and its services.
 164      */
 165     public String getInfo() {
 166         return info;
 167     }
 168 
 169     /**
 170      * Returns a string with the name and the version number
 171      * of this provider.
 172      *
 173      * @return the string with the name and the version number
 174      * for this provider.
 175      */
 176     public String toString() {
 177         return name + " version " + version;
 178     }
 179 
 180     /*
 181      * override the following methods to ensure that provider
 182      * information can only be changed if the caller has the appropriate
 183      * permissions.
 184      */
 185 
 186     /**
 187      * Clears this provider so that it no longer contains the properties
 188      * used to look up facilities implemented by the provider.
 189      *
 190      * <p>First, if there is a security manager, its
 191      * <code>checkSecurityAccess</code> method is called with the string
 192      * <code>"clearProviderProperties."+name</code> (where <code>name</code>
 193      * is the provider name) to see if it's ok to clear this provider.
 194      * If the default implementation of <code>checkSecurityAccess</code>
 195      * is used (that is, that method is not overriden), then this results in
 196      * a call to the security manager's <code>checkPermission</code> method
 197      * with a <code>SecurityPermission("clearProviderProperties."+name)</code>
 198      * permission.
 199      *
 200      * @throws  SecurityException
 201      *          if a security manager exists and its <code>{@link
 202      *          java.lang.SecurityManager#checkSecurityAccess}</code> method
 203      *          denies access to clear this provider
 204      *
 205      * @since 1.2
 206      */
 207     public synchronized void clear() {
 208         check("clearProviderProperties."+name);
 209         if (debug != null) {
 210             debug.println("Remove " + name + " provider properties");
 211         }
 212         implClear();
 213     }
 214 
 215     /**
 216      * Reads a property list (key and element pairs) from the input stream.
 217      *
 218      * @param inStream   the input stream.
 219      * @exception  IOException  if an error occurred when reading from the
 220      *               input stream.
 221      * @see java.util.Properties#load
 222      */
 223     public synchronized void load(InputStream inStream) throws IOException {
 224         check("putProviderProperty."+name);
 225         if (debug != null) {
 226             debug.println("Load " + name + " provider properties");
 227         }
 228         Properties tempProperties = new Properties();
 229         tempProperties.load(inStream);
 230         implPutAll(tempProperties);
 231     }
 232 
 233     /**
 234      * Copies all of the mappings from the specified Map to this provider.
 235      * These mappings will replace any properties that this provider had
 236      * for any of the keys currently in the specified Map.
 237      *
 238      * @since 1.2
 239      */
 240     public synchronized void putAll(Map<?,?> t) {
 241         check("putProviderProperty."+name);
 242         if (debug != null) {
 243             debug.println("Put all " + name + " provider properties");
 244         }
 245         implPutAll(t);
 246     }
 247 
 248     /**
 249      * Returns an unmodifiable Set view of the property entries contained
 250      * in this Provider.
 251      *
 252      * @see   java.util.Map.Entry
 253      * @since 1.2
 254      */
 255     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
 256         checkInitialized();
 257         if (entrySet == null) {
 258             if (entrySetCallCount++ == 0)  // Initial call
 259                 entrySet = Collections.unmodifiableMap(this).entrySet();
 260             else
 261                 return super.entrySet();   // Recursive call
 262         }
 263 
 264         // This exception will be thrown if the implementation of
 265         // Collections.unmodifiableMap.entrySet() is changed such that it
 266         // no longer calls entrySet() on the backing Map.  (Provider's
 267         // entrySet implementation depends on this "implementation detail",
 268         // which is unlikely to change.
 269         if (entrySetCallCount != 2)
 270             throw new RuntimeException("Internal error.");
 271 
 272         return entrySet;
 273     }
 274 
 275     /**
 276      * Returns an unmodifiable Set view of the property keys contained in
 277      * this provider.
 278      *
 279      * @since 1.2
 280      */
 281     public Set<Object> keySet() {
 282         checkInitialized();
 283         return Collections.unmodifiableSet(super.keySet());
 284     }
 285 
 286     /**
 287      * Returns an unmodifiable Collection view of the property values
 288      * contained in this provider.
 289      *
 290      * @since 1.2
 291      */
 292     public Collection<Object> values() {
 293         checkInitialized();
 294         return Collections.unmodifiableCollection(super.values());
 295     }
 296 
 297     /**
 298      * Sets the <code>key</code> property to have the specified
 299      * <code>value</code>.
 300      *
 301      * <p>First, if there is a security manager, its
 302      * <code>checkSecurityAccess</code> method is called with the string
 303      * <code>"putProviderProperty."+name</code>, where <code>name</code> is the
 304      * provider name, to see if it's ok to set this provider's property values.
 305      * If the default implementation of <code>checkSecurityAccess</code>
 306      * is used (that is, that method is not overriden), then this results in
 307      * a call to the security manager's <code>checkPermission</code> method
 308      * with a <code>SecurityPermission("putProviderProperty."+name)</code>
 309      * permission.
 310      *
 311      * @param key the property key.
 312      *
 313      * @param value the property value.
 314      *
 315      * @return the previous value of the specified property
 316      * (<code>key</code>), or null if it did not have one.
 317      *
 318      * @throws  SecurityException
 319      *          if a security manager exists and its <code>{@link
 320      *          java.lang.SecurityManager#checkSecurityAccess}</code> method
 321      *          denies access to set property values.
 322      *
 323      * @since 1.2
 324      */
 325     public synchronized Object put(Object key, Object value) {
 326         check("putProviderProperty."+name);
 327         if (debug != null) {
 328             debug.println("Set " + name + " provider property [" +
 329                           key + "/" + value +"]");
 330         }
 331         return implPut(key, value);
 332     }
 333 
 334     /**
 335      * Removes the <code>key</code> property (and its corresponding
 336      * <code>value</code>).
 337      *
 338      * <p>First, if there is a security manager, its
 339      * <code>checkSecurityAccess</code> method is called with the string
 340      * <code>"removeProviderProperty."+name</code>, where <code>name</code> is
 341      * the provider name, to see if it's ok to remove this provider's
 342      * properties. If the default implementation of
 343      * <code>checkSecurityAccess</code> is used (that is, that method is not
 344      * overriden), then this results in a call to the security manager's
 345      * <code>checkPermission</code> method with a
 346      * <code>SecurityPermission("removeProviderProperty."+name)</code>
 347      * permission.
 348      *
 349      * @param key the key for the property to be removed.
 350      *
 351      * @return the value to which the key had been mapped,
 352      * or null if the key did not have a mapping.
 353      *
 354      * @throws  SecurityException
 355      *          if a security manager exists and its <code>{@link
 356      *          java.lang.SecurityManager#checkSecurityAccess}</code> method
 357      *          denies access to remove this provider's properties.
 358      *
 359      * @since 1.2
 360      */
 361     public synchronized Object remove(Object key) {
 362         check("removeProviderProperty."+name);
 363         if (debug != null) {
 364             debug.println("Remove " + name + " provider property " + key);
 365         }
 366         return implRemove(key);
 367     }
 368 
 369     // let javadoc show doc from superclass
 370     public Object get(Object key) {
 371         checkInitialized();
 372         return super.get(key);
 373     }
 374 
 375     // let javadoc show doc from superclass
 376     public Enumeration<Object> keys() {
 377         checkInitialized();
 378         return super.keys();
 379     }
 380 
 381     // let javadoc show doc from superclass
 382     public Enumeration<Object> elements() {
 383         checkInitialized();
 384         return super.elements();
 385     }
 386 
 387     // let javadoc show doc from superclass
 388     public String getProperty(String key) {
 389         checkInitialized();
 390         return super.getProperty(key);
 391     }
 392 
 393     private void checkInitialized() {
 394         if (!initialized) {
 395             throw new IllegalStateException();
 396         }
 397     }
 398 
 399     private void check(String directive) {
 400         checkInitialized();
 401         SecurityManager security = System.getSecurityManager();
 402         if (security != null) {
 403             security.checkSecurityAccess(directive);
 404         }
 405     }
 406 
 407     // legacy properties changed since last call to any services method?
 408     private transient boolean legacyChanged;
 409     // serviceMap changed since last call to getServices()
 410     private transient boolean servicesChanged;
 411 
 412     // Map<String,String>
 413     private transient Map<String,String> legacyStrings;
 414 
 415     // Map<ServiceKey,Service>
 416     // used for services added via putService(), initialized on demand
 417     private transient Map<ServiceKey,Service> serviceMap;
 418 
 419     // Map<ServiceKey,Service>
 420     // used for services added via legacy methods, init on demand
 421     private transient Map<ServiceKey,Service> legacyMap;
 422 
 423     // Set<Service>
 424     // Unmodifiable set of all services. Initialized on demand.
 425     private transient Set<Service> serviceSet;
 426 
 427     // register the id attributes for this provider
 428     // this is to ensure that equals() and hashCode() do not incorrectly
 429     // report to different provider objects as the same
 430     private void putId() {
 431         // note: name and info may be null
 432         super.put("Provider.id name", String.valueOf(name));
 433         super.put("Provider.id version", String.valueOf(version));
 434         super.put("Provider.id info", String.valueOf(info));
 435         super.put("Provider.id className", this.getClass().getName());
 436     }
 437 
 438     private void readObject(ObjectInputStream in)
 439                 throws IOException, ClassNotFoundException {
 440         Map<Object,Object> copy = new HashMap<>();
 441         for (Map.Entry<Object,Object> entry : super.entrySet()) {
 442             copy.put(entry.getKey(), entry.getValue());
 443         }
 444         defaults = null;
 445         in.defaultReadObject();
 446         implClear();
 447         initialized = true;
 448         putAll(copy);
 449     }
 450 
 451     /**
 452      * Copies all of the mappings from the specified Map to this provider.
 453      * Internal method to be called AFTER the security check has been
 454      * performed.
 455      */
 456     private void implPutAll(Map t) {
 457         for (Map.Entry e : ((Map<?,?>)t).entrySet()) {
 458             implPut(e.getKey(), e.getValue());
 459         }
 460     }
 461 
 462     private Object implRemove(Object key) {
 463         if (key instanceof String) {
 464             String keyString = (String)key;
 465             if (keyString.startsWith("Provider.")) {
 466                 return null;
 467             }
 468             legacyChanged = true;
 469             if (legacyStrings == null) {
 470                 legacyStrings = new LinkedHashMap<String,String>();
 471             }
 472             legacyStrings.remove(keyString);
 473         }
 474         return super.remove(key);
 475     }
 476 
 477     private Object implPut(Object key, Object value) {
 478         if ((key instanceof String) && (value instanceof String)) {
 479             String keyString = (String)key;
 480             if (keyString.startsWith("Provider.")) {
 481                 return null;
 482             }
 483             legacyChanged = true;
 484             if (legacyStrings == null) {
 485                 legacyStrings = new LinkedHashMap<String,String>();
 486             }
 487             legacyStrings.put(keyString, (String)value);
 488         }
 489         return super.put(key, value);
 490     }
 491 
 492     private void implClear() {
 493         if (legacyStrings != null) {
 494             legacyStrings.clear();
 495         }
 496         if (legacyMap != null) {
 497             legacyMap.clear();
 498         }
 499         if (serviceMap != null) {
 500             serviceMap.clear();
 501         }
 502         legacyChanged = false;
 503         servicesChanged = false;
 504         serviceSet = null;
 505         super.clear();
 506         putId();
 507     }
 508 
 509     // used as key in the serviceMap and legacyMap HashMaps
 510     private static class ServiceKey {
 511         private final String type;
 512         private final String algorithm;
 513         private final String originalAlgorithm;
 514         private ServiceKey(String type, String algorithm, boolean intern) {
 515             this.type = type;
 516             this.originalAlgorithm = algorithm;
 517             algorithm = algorithm.toUpperCase(ENGLISH);
 518             this.algorithm = intern ? algorithm.intern() : algorithm;
 519         }
 520         public int hashCode() {
 521             return type.hashCode() + algorithm.hashCode();
 522         }
 523         public boolean equals(Object obj) {
 524             if (this == obj) {
 525                 return true;
 526             }
 527             if (obj instanceof ServiceKey == false) {
 528                 return false;
 529             }
 530             ServiceKey other = (ServiceKey)obj;
 531             return this.type.equals(other.type)
 532                 && this.algorithm.equals(other.algorithm);
 533         }
 534         boolean matches(String type, String algorithm) {
 535             return (this.type == type) && (this.originalAlgorithm == algorithm);
 536         }
 537     }
 538 
 539     /**
 540      * Ensure all the legacy String properties are fully parsed into
 541      * service objects.
 542      */
 543     private void ensureLegacyParsed() {
 544         if ((legacyChanged == false) || (legacyStrings == null)) {
 545             return;
 546         }
 547         serviceSet = null;
 548         if (legacyMap == null) {
 549             legacyMap = new LinkedHashMap<ServiceKey,Service>();
 550         } else {
 551             legacyMap.clear();
 552         }
 553         for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
 554             parseLegacyPut(entry.getKey(), entry.getValue());
 555         }
 556         removeInvalidServices(legacyMap);
 557         legacyChanged = false;
 558     }
 559 
 560     /**
 561      * Remove all invalid services from the Map. Invalid services can only
 562      * occur if the legacy properties are inconsistent or incomplete.
 563      */
 564     private void removeInvalidServices(Map<ServiceKey,Service> map) {
 565         for (Iterator t = map.entrySet().iterator(); t.hasNext(); ) {
 566             Map.Entry entry = (Map.Entry)t.next();
 567             Service s = (Service)entry.getValue();
 568             if (s.isValid() == false) {
 569                 t.remove();
 570             }
 571         }
 572     }
 573 
 574     private String[] getTypeAndAlgorithm(String key) {
 575         int i = key.indexOf(".");
 576         if (i < 1) {
 577             if (debug != null) {
 578                 debug.println("Ignoring invalid entry in provider "
 579                         + name + ":" + key);
 580             }
 581             return null;
 582         }
 583         String type = key.substring(0, i);
 584         String alg = key.substring(i + 1);
 585         return new String[] {type, alg};
 586     }
 587 
 588     private final static String ALIAS_PREFIX = "Alg.Alias.";
 589     private final static String ALIAS_PREFIX_LOWER = "alg.alias.";
 590     private final static int ALIAS_LENGTH = ALIAS_PREFIX.length();
 591 
 592     private void parseLegacyPut(String name, String value) {
 593         if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
 594             // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
 595             // aliasKey ~ MessageDigest.SHA
 596             String stdAlg = value;
 597             String aliasKey = name.substring(ALIAS_LENGTH);
 598             String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
 599             if (typeAndAlg == null) {
 600                 return;
 601             }
 602             String type = getEngineName(typeAndAlg[0]);
 603             String aliasAlg = typeAndAlg[1].intern();
 604             ServiceKey key = new ServiceKey(type, stdAlg, true);
 605             Service s = legacyMap.get(key);
 606             if (s == null) {
 607                 s = new Service(this);
 608                 s.type = type;
 609                 s.algorithm = stdAlg;
 610                 legacyMap.put(key, s);
 611             }
 612             legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
 613             s.addAlias(aliasAlg);
 614         } else {
 615             String[] typeAndAlg = getTypeAndAlgorithm(name);
 616             if (typeAndAlg == null) {
 617                 return;
 618             }
 619             int i = typeAndAlg[1].indexOf(' ');
 620             if (i == -1) {
 621                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
 622                 String type = getEngineName(typeAndAlg[0]);
 623                 String stdAlg = typeAndAlg[1].intern();
 624                 String className = value;
 625                 ServiceKey key = new ServiceKey(type, stdAlg, true);
 626                 Service s = legacyMap.get(key);
 627                 if (s == null) {
 628                     s = new Service(this);
 629                     s.type = type;
 630                     s.algorithm = stdAlg;
 631                     legacyMap.put(key, s);
 632                 }
 633                 s.className = className;
 634             } else { // attribute
 635                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
 636                 String attributeValue = value;
 637                 String type = getEngineName(typeAndAlg[0]);
 638                 String attributeString = typeAndAlg[1];
 639                 String stdAlg = attributeString.substring(0, i).intern();
 640                 String attributeName = attributeString.substring(i + 1);
 641                 // kill additional spaces
 642                 while (attributeName.startsWith(" ")) {
 643                     attributeName = attributeName.substring(1);
 644                 }
 645                 attributeName = attributeName.intern();
 646                 ServiceKey key = new ServiceKey(type, stdAlg, true);
 647                 Service s = legacyMap.get(key);
 648                 if (s == null) {
 649                     s = new Service(this);
 650                     s.type = type;
 651                     s.algorithm = stdAlg;
 652                     legacyMap.put(key, s);
 653                 }
 654                 s.addAttribute(attributeName, attributeValue);
 655             }
 656         }
 657     }
 658 
 659     /**
 660      * Get the service describing this Provider's implementation of the
 661      * specified type of this algorithm or alias. If no such
 662      * implementation exists, this method returns null. If there are two
 663      * matching services, one added to this provider using
 664      * {@link #putService putService()} and one added via {@link #put put()},
 665      * the service added via {@link #putService putService()} is returned.
 666      *
 667      * @param type the type of {@link Service service} requested
 668      * (for example, <code>MessageDigest</code>)
 669      * @param algorithm the case insensitive algorithm name (or alternate
 670      * alias) of the service requested (for example, <code>SHA-1</code>)
 671      *
 672      * @return the service describing this Provider's matching service
 673      * or null if no such service exists
 674      *
 675      * @throws NullPointerException if type or algorithm is null
 676      *
 677      * @since 1.5
 678      */
 679     public synchronized Service getService(String type, String algorithm) {
 680         checkInitialized();
 681         // avoid allocating a new key object if possible
 682         ServiceKey key = previousKey;
 683         if (key.matches(type, algorithm) == false) {
 684             key = new ServiceKey(type, algorithm, false);
 685             previousKey = key;
 686         }
 687         if (serviceMap != null) {
 688             Service service = serviceMap.get(key);
 689             if (service != null) {
 690                 return service;
 691             }
 692         }
 693         ensureLegacyParsed();
 694         return (legacyMap != null) ? legacyMap.get(key) : null;
 695     }
 696 
 697     // ServiceKey from previous getService() call
 698     // by re-using it if possible we avoid allocating a new object
 699     // and the toUpperCase() call.
 700     // re-use will occur e.g. as the framework traverses the provider
 701     // list and queries each provider with the same values until it finds
 702     // a matching service
 703     private static volatile ServiceKey previousKey =
 704                                             new ServiceKey("", "", false);
 705 
 706     /**
 707      * Get an unmodifiable Set of all services supported by
 708      * this Provider.
 709      *
 710      * @return an unmodifiable Set of all services supported by
 711      * this Provider
 712      *
 713      * @since 1.5
 714      */
 715     public synchronized Set<Service> getServices() {
 716         checkInitialized();
 717         if (legacyChanged || servicesChanged) {
 718             serviceSet = null;
 719         }
 720         if (serviceSet == null) {
 721             ensureLegacyParsed();
 722             Set<Service> set = new LinkedHashSet<>();
 723             if (serviceMap != null) {
 724                 set.addAll(serviceMap.values());
 725             }
 726             if (legacyMap != null) {
 727                 set.addAll(legacyMap.values());
 728             }
 729             serviceSet = Collections.unmodifiableSet(set);
 730             servicesChanged = false;
 731         }
 732         return serviceSet;
 733     }
 734 
 735     /**
 736      * Add a service. If a service of the same type with the same algorithm
 737      * name exists and it was added using {@link #putService putService()},
 738      * it is replaced by the new service.
 739      * This method also places information about this service
 740      * in the provider's Hashtable values in the format described in the
 741      * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
 742      * Java Cryptography Architecture API Specification &amp; Reference </a>.
 743      *
 744      * <p>Also, if there is a security manager, its
 745      * <code>checkSecurityAccess</code> method is called with the string
 746      * <code>"putProviderProperty."+name</code>, where <code>name</code> is
 747      * the provider name, to see if it's ok to set this provider's property
 748      * values. If the default implementation of <code>checkSecurityAccess</code>
 749      * is used (that is, that method is not overriden), then this results in
 750      * a call to the security manager's <code>checkPermission</code> method with
 751      * a <code>SecurityPermission("putProviderProperty."+name)</code>
 752      * permission.
 753      *
 754      * @param s the Service to add
 755      *
 756      * @throws SecurityException
 757      *      if a security manager exists and its <code>{@link
 758      *      java.lang.SecurityManager#checkSecurityAccess}</code> method denies
 759      *      access to set property values.
 760      * @throws NullPointerException if s is null
 761      *
 762      * @since 1.5
 763      */
 764     protected synchronized void putService(Service s) {
 765         check("putProviderProperty." + name);
 766         if (debug != null) {
 767             debug.println(name + ".putService(): " + s);
 768         }
 769         if (s == null) {
 770             throw new NullPointerException();
 771         }
 772         if (s.getProvider() != this) {
 773             throw new IllegalArgumentException
 774                     ("service.getProvider() must match this Provider object");
 775         }
 776         if (serviceMap == null) {
 777             serviceMap = new LinkedHashMap<ServiceKey,Service>();
 778         }
 779         servicesChanged = true;
 780         String type = s.getType();
 781         String algorithm = s.getAlgorithm();
 782         ServiceKey key = new ServiceKey(type, algorithm, true);
 783         // remove existing service
 784         implRemoveService(serviceMap.get(key));
 785         serviceMap.put(key, s);
 786         for (String alias : s.getAliases()) {
 787             serviceMap.put(new ServiceKey(type, alias, true), s);
 788         }
 789         putPropertyStrings(s);
 790     }
 791 
 792     /**
 793      * Put the string properties for this Service in this Provider's
 794      * Hashtable.
 795      */
 796     private void putPropertyStrings(Service s) {
 797         String type = s.getType();
 798         String algorithm = s.getAlgorithm();
 799         // use super() to avoid permission check and other processing
 800         super.put(type + "." + algorithm, s.getClassName());
 801         for (String alias : s.getAliases()) {
 802             super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
 803         }
 804         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
 805             String key = type + "." + algorithm + " " + entry.getKey();
 806             super.put(key, entry.getValue());
 807         }
 808     }
 809 
 810     /**
 811      * Remove the string properties for this Service from this Provider's
 812      * Hashtable.
 813      */
 814     private void removePropertyStrings(Service s) {
 815         String type = s.getType();
 816         String algorithm = s.getAlgorithm();
 817         // use super() to avoid permission check and other processing
 818         super.remove(type + "." + algorithm);
 819         for (String alias : s.getAliases()) {
 820             super.remove(ALIAS_PREFIX + type + "." + alias);
 821         }
 822         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
 823             String key = type + "." + algorithm + " " + entry.getKey();
 824             super.remove(key);
 825         }
 826     }
 827 
 828     /**
 829      * Remove a service previously added using
 830      * {@link #putService putService()}. The specified service is removed from
 831      * this provider. It will no longer be returned by
 832      * {@link #getService getService()} and its information will be removed
 833      * from this provider's Hashtable.
 834      *
 835      * <p>Also, if there is a security manager, its
 836      * <code>checkSecurityAccess</code> method is called with the string
 837      * <code>"removeProviderProperty."+name</code>, where <code>name</code> is
 838      * the provider name, to see if it's ok to remove this provider's
 839      * properties. If the default implementation of
 840      * <code>checkSecurityAccess</code> is used (that is, that method is not
 841      * overriden), then this results in a call to the security manager's
 842      * <code>checkPermission</code> method with a
 843      * <code>SecurityPermission("removeProviderProperty."+name)</code>
 844      * permission.
 845      *
 846      * @param s the Service to be removed
 847      *
 848      * @throws  SecurityException
 849      *          if a security manager exists and its <code>{@link
 850      *          java.lang.SecurityManager#checkSecurityAccess}</code> method denies
 851      *          access to remove this provider's properties.
 852      * @throws NullPointerException if s is null
 853      *
 854      * @since 1.5
 855      */
 856     protected synchronized void removeService(Service s) {
 857         check("removeProviderProperty." + name);
 858         if (debug != null) {
 859             debug.println(name + ".removeService(): " + s);
 860         }
 861         if (s == null) {
 862             throw new NullPointerException();
 863         }
 864         implRemoveService(s);
 865     }
 866 
 867     private void implRemoveService(Service s) {
 868         if ((s == null) || (serviceMap == null)) {
 869             return;
 870         }
 871         String type = s.getType();
 872         String algorithm = s.getAlgorithm();
 873         ServiceKey key = new ServiceKey(type, algorithm, false);
 874         Service oldService = serviceMap.get(key);
 875         if (s != oldService) {
 876             return;
 877         }
 878         servicesChanged = true;
 879         serviceMap.remove(key);
 880         for (String alias : s.getAliases()) {
 881             serviceMap.remove(new ServiceKey(type, alias, false));
 882         }
 883         removePropertyStrings(s);
 884     }
 885 
 886     // Wrapped String that behaves in a case insensitive way for equals/hashCode
 887     private static class UString {
 888         final String string;
 889         final String lowerString;
 890 
 891         UString(String s) {
 892             this.string = s;
 893             this.lowerString = s.toLowerCase(ENGLISH);
 894         }
 895 
 896         public int hashCode() {
 897             return lowerString.hashCode();
 898         }
 899 
 900         public boolean equals(Object obj) {
 901             if (this == obj) {
 902                 return true;
 903             }
 904             if (obj instanceof UString == false) {
 905                 return false;
 906             }
 907             UString other = (UString)obj;
 908             return lowerString.equals(other.lowerString);
 909         }
 910 
 911         public String toString() {
 912             return string;
 913         }
 914     }
 915 
 916     // describe relevant properties of a type of engine
 917     private static class EngineDescription {
 918         final String name;
 919         final boolean supportsParameter;
 920         final String constructorParameterClassName;
 921         private volatile Class constructorParameterClass;
 922 
 923         EngineDescription(String name, boolean sp, String paramName) {
 924             this.name = name;
 925             this.supportsParameter = sp;
 926             this.constructorParameterClassName = paramName;
 927         }
 928         Class getConstructorParameterClass() throws ClassNotFoundException {
 929             Class clazz = constructorParameterClass;
 930             if (clazz == null) {
 931                 clazz = Class.forName(constructorParameterClassName);
 932                 constructorParameterClass = clazz;
 933             }
 934             return clazz;
 935         }
 936     }
 937 
 938     // built in knowledge of the engine types shipped as part of the JDK
 939     private static final Map<String,EngineDescription> knownEngines;
 940 
 941     private static void addEngine(String name, boolean sp, String paramName) {
 942         EngineDescription ed = new EngineDescription(name, sp, paramName);
 943         // also index by canonical name to avoid toLowerCase() for some lookups
 944         knownEngines.put(name.toLowerCase(ENGLISH), ed);
 945         knownEngines.put(name, ed);
 946     }
 947 
 948     static {
 949         knownEngines = new HashMap<String,EngineDescription>();
 950         // JCA
 951         addEngine("AlgorithmParameterGenerator",        false, null);
 952         addEngine("AlgorithmParameters",                false, null);
 953         addEngine("KeyFactory",                         false, null);
 954         addEngine("KeyPairGenerator",                   false, null);
 955         addEngine("KeyStore",                           false, null);
 956         addEngine("MessageDigest",                      false, null);
 957         addEngine("SecureRandom",                       false, null);
 958         addEngine("Signature",                          true,  null);
 959         addEngine("CertificateFactory",                 false, null);
 960         addEngine("CertPathBuilder",                    false, null);
 961         addEngine("CertPathValidator",                  false, null);
 962         addEngine("CertStore",                          false,
 963                             "java.security.cert.CertStoreParameters");
 964         // JCE
 965         addEngine("Cipher",                             true,  null);
 966         addEngine("ExemptionMechanism",                 false, null);
 967         addEngine("Mac",                                true,  null);
 968         addEngine("KeyAgreement",                       true,  null);
 969         addEngine("KeyGenerator",                       false, null);
 970         addEngine("SecretKeyFactory",                   false, null);
 971         // JSSE
 972         addEngine("KeyManagerFactory",                  false, null);
 973         addEngine("SSLContext",                         false, null);
 974         addEngine("TrustManagerFactory",                false, null);
 975         // JGSS
 976         addEngine("GssApiMechanism",                    false, null);
 977         // SASL
 978         addEngine("SaslClientFactory",                  false, null);
 979         addEngine("SaslServerFactory",                  false, null);
 980         // POLICY
 981         addEngine("Policy",                             false,
 982                             "java.security.Policy$Parameters");
 983         // CONFIGURATION
 984         addEngine("Configuration",                      false,
 985                             "javax.security.auth.login.Configuration$Parameters");
 986         // XML DSig
 987         addEngine("XMLSignatureFactory",                false, null);
 988         addEngine("KeyInfoFactory",                     false, null);
 989         addEngine("TransformService",                   false, null);
 990         // Smart Card I/O
 991         addEngine("TerminalFactory",                    false,
 992                             "java.lang.Object");
 993     }
 994 
 995     // get the "standard" (mixed-case) engine name for arbitary case engine name
 996     // if there is no known engine by that name, return s
 997     private static String getEngineName(String s) {
 998         // try original case first, usually correct
 999         EngineDescription e = knownEngines.get(s);
1000         if (e == null) {
1001             e = knownEngines.get(s.toLowerCase(ENGLISH));
1002         }
1003         return (e == null) ? s : e.name;
1004     }
1005 
1006     /**
1007      * The description of a security service. It encapsulates the properties
1008      * of a service and contains a factory method to obtain new implementation
1009      * instances of this service.
1010      *
1011      * <p>Each service has a provider that offers the service, a type,
1012      * an algorithm name, and the name of the class that implements the
1013      * service. Optionally, it also includes a list of alternate algorithm
1014      * names for this service (aliases) and attributes, which are a map of
1015      * (name, value) String pairs.
1016      *
1017      * <p>This class defines the methods {@link #supportsParameter
1018      * supportsParameter()} and {@link #newInstance newInstance()}
1019      * which are used by the Java security framework when it searches for
1020      * suitable services and instantes them. The valid arguments to those
1021      * methods depend on the type of service. For the service types defined
1022      * within Java SE, see the
1023      * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1024      * Java Cryptography Architecture API Specification &amp; Reference </a>
1025      * for the valid values.
1026      * Note that components outside of Java SE can define additional types of
1027      * services and their behavior.
1028      *
1029      * <p>Instances of this class are immutable.
1030      *
1031      * @since 1.5
1032      */
1033     public static class Service {
1034 
1035         private String type, algorithm, className;
1036         private final Provider provider;
1037         private List<String> aliases;
1038         private Map<UString,String> attributes;
1039 
1040         // Reference to the cached implementation Class object
1041         private volatile Reference<Class> classRef;
1042 
1043         // flag indicating whether this service has its attributes for
1044         // supportedKeyFormats or supportedKeyClasses set
1045         // if null, the values have not been initialized
1046         // if TRUE, at least one of supportedFormats/Classes is non null
1047         private volatile Boolean hasKeyAttributes;
1048 
1049         // supported encoding formats
1050         private String[] supportedFormats;
1051 
1052         // names of the supported key (super) classes
1053         private Class[] supportedClasses;
1054 
1055         // whether this service has been registered with the Provider
1056         private boolean registered;
1057 
1058         private static final Class[] CLASS0 = new Class[0];
1059 
1060         // this constructor and these methods are used for parsing
1061         // the legacy string properties.
1062 
1063         private Service(Provider provider) {
1064             this.provider = provider;
1065             aliases = Collections.<String>emptyList();
1066             attributes = Collections.<UString,String>emptyMap();
1067         }
1068 
1069         private boolean isValid() {
1070             return (type != null) && (algorithm != null) && (className != null);
1071         }
1072 
1073         private void addAlias(String alias) {
1074             if (aliases.isEmpty()) {
1075                 aliases = new ArrayList<String>(2);
1076             }
1077             aliases.add(alias);
1078         }
1079 
1080         void addAttribute(String type, String value) {
1081             if (attributes.isEmpty()) {
1082                 attributes = new HashMap<UString,String>(8);
1083             }
1084             attributes.put(new UString(type), value);
1085         }
1086 
1087         /**
1088          * Construct a new service.
1089          *
1090          * @param provider the provider that offers this service
1091          * @param type the type of this service
1092          * @param algorithm the algorithm name
1093          * @param className the name of the class implementing this service
1094          * @param aliases List of aliases or null if algorithm has no aliases
1095          * @param attributes Map of attributes or null if this implementation
1096          *                   has no attributes
1097          *
1098          * @throws NullPointerException if provider, type, algorithm, or
1099          * className is null
1100          */
1101         public Service(Provider provider, String type, String algorithm,
1102                 String className, List<String> aliases,
1103                 Map<String,String> attributes) {
1104             if ((provider == null) || (type == null) ||
1105                     (algorithm == null) || (className == null)) {
1106                 throw new NullPointerException();
1107             }
1108             this.provider = provider;
1109             this.type = getEngineName(type);
1110             this.algorithm = algorithm;
1111             this.className = className;
1112             if (aliases == null) {
1113                 this.aliases = Collections.<String>emptyList();
1114             } else {
1115                 this.aliases = new ArrayList<String>(aliases);
1116             }
1117             if (attributes == null) {
1118                 this.attributes = Collections.<UString,String>emptyMap();
1119             } else {
1120                 this.attributes = new HashMap<UString,String>();
1121                 for (Map.Entry<String,String> entry : attributes.entrySet()) {
1122                     this.attributes.put(new UString(entry.getKey()), entry.getValue());
1123                 }
1124             }
1125         }
1126 
1127         /**
1128          * Get the type of this service. For example, <code>MessageDigest</code>.
1129          *
1130          * @return the type of this service
1131          */
1132         public final String getType() {
1133             return type;
1134         }
1135 
1136         /**
1137          * Return the name of the algorithm of this service. For example,
1138          * <code>SHA-1</code>.
1139          *
1140          * @return the algorithm of this service
1141          */
1142         public final String getAlgorithm() {
1143             return algorithm;
1144         }
1145 
1146         /**
1147          * Return the Provider of this service.
1148          *
1149          * @return the Provider of this service
1150          */
1151         public final Provider getProvider() {
1152             return provider;
1153         }
1154 
1155         /**
1156          * Return the name of the class implementing this service.
1157          *
1158          * @return the name of the class implementing this service
1159          */
1160         public final String getClassName() {
1161             return className;
1162         }
1163 
1164         // internal only
1165         private final List<String> getAliases() {
1166             return aliases;
1167         }
1168 
1169         /**
1170          * Return the value of the specified attribute or null if this
1171          * attribute is not set for this Service.
1172          *
1173          * @param name the name of the requested attribute
1174          *
1175          * @return the value of the specified attribute or null if the
1176          *         attribute is not present
1177          *
1178          * @throws NullPointerException if name is null
1179          */
1180         public final String getAttribute(String name) {
1181             if (name == null) {
1182                 throw new NullPointerException();
1183             }
1184             return attributes.get(new UString(name));
1185         }
1186 
1187         /**
1188          * Return a new instance of the implementation described by this
1189          * service. The security provider framework uses this method to
1190          * construct implementations. Applications will typically not need
1191          * to call it.
1192          *
1193          * <p>The default implementation uses reflection to invoke the
1194          * standard constructor for this type of service.
1195          * Security providers can override this method to implement
1196          * instantiation in a different way.
1197          * For details and the values of constructorParameter that are
1198          * valid for the various types of services see the
1199          * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1200          * Java Cryptography Architecture API Specification &amp;
1201          * Reference</a>.
1202          *
1203          * @param constructorParameter the value to pass to the constructor,
1204          * or null if this type of service does not use a constructorParameter.
1205          *
1206          * @return a new implementation of this service
1207          *
1208          * @throws InvalidParameterException if the value of
1209          * constructorParameter is invalid for this type of service.
1210          * @throws NoSuchAlgorithmException if instantation failed for
1211          * any other reason.
1212          */
1213         public Object newInstance(Object constructorParameter)
1214                 throws NoSuchAlgorithmException {
1215             if (registered == false) {
1216                 if (provider.getService(type, algorithm) != this) {
1217                     throw new NoSuchAlgorithmException
1218                         ("Service not registered with Provider "
1219                         + provider.getName() + ": " + this);
1220                 }
1221                 registered = true;
1222             }
1223             try {
1224                 EngineDescription cap = knownEngines.get(type);
1225                 if (cap == null) {
1226                     // unknown engine type, use generic code
1227                     // this is the code path future for non-core
1228                     // optional packages
1229                     return newInstanceGeneric(constructorParameter);
1230                 }
1231                 if (cap.constructorParameterClassName == null) {
1232                     if (constructorParameter != null) {
1233                         throw new InvalidParameterException
1234                             ("constructorParameter not used with " + type
1235                             + " engines");
1236                     }
1237                     Class clazz = getImplClass();
1238                     return clazz.newInstance();
1239                 } else {
1240                     Class paramClass = cap.getConstructorParameterClass();
1241                     if (constructorParameter != null) {
1242                         Class argClass = constructorParameter.getClass();
1243                         if (paramClass.isAssignableFrom(argClass) == false) {
1244                             throw new InvalidParameterException
1245                             ("constructorParameter must be instanceof "
1246                             + cap.constructorParameterClassName.replace('$', '.')
1247                             + " for engine type " + type);
1248                         }
1249                     }
1250                     Class clazz = getImplClass();
1251                     Constructor cons = clazz.getConstructor(paramClass);
1252                     return cons.newInstance(constructorParameter);
1253                 }
1254             } catch (NoSuchAlgorithmException e) {
1255                 throw e;
1256             } catch (InvocationTargetException e) {
1257                 throw new NoSuchAlgorithmException
1258                     ("Error constructing implementation (algorithm: "
1259                     + algorithm + ", provider: " + provider.getName()
1260                     + ", class: " + className + ")", e.getCause());
1261             } catch (Exception e) {
1262                 throw new NoSuchAlgorithmException
1263                     ("Error constructing implementation (algorithm: "
1264                     + algorithm + ", provider: " + provider.getName()
1265                     + ", class: " + className + ")", e);
1266             }
1267         }
1268 
1269         // return the implementation Class object for this service
1270         private Class getImplClass() throws NoSuchAlgorithmException {
1271             try {
1272                 Reference<Class> ref = classRef;
1273                 Class clazz = (ref == null) ? null : ref.get();
1274                 if (clazz == null) {
1275                     ClassLoader cl = provider.getClass().getClassLoader();
1276                     if (cl == null) {
1277                         clazz = Class.forName(className);
1278                     } else {
1279                         clazz = cl.loadClass(className);
1280                     }
1281                     classRef = new WeakReference<Class>(clazz);
1282                 }
1283                 return clazz;
1284             } catch (ClassNotFoundException e) {
1285                 throw new NoSuchAlgorithmException
1286                     ("class configured for " + type + "(provider: " +
1287                     provider.getName() + ")" + "cannot be found.", e);
1288             }
1289         }
1290 
1291         /**
1292          * Generic code path for unknown engine types. Call the
1293          * no-args constructor if constructorParameter is null, otherwise
1294          * use the first matching constructor.
1295          */
1296         private Object newInstanceGeneric(Object constructorParameter)
1297                 throws Exception {
1298             Class clazz = getImplClass();
1299             if (constructorParameter == null) {
1300                 Object o = clazz.newInstance();
1301                 return o;
1302             }
1303             Class argClass = constructorParameter.getClass();
1304             Constructor[] cons = clazz.getConstructors();
1305             // find first public constructor that can take the
1306             // argument as parameter
1307             for (int i = 0; i < cons.length; i++) {
1308                 Constructor con = cons[i];
1309                 Class[] paramTypes = con.getParameterTypes();
1310                 if (paramTypes.length != 1) {
1311                     continue;
1312                 }
1313                 if (paramTypes[0].isAssignableFrom(argClass) == false) {
1314                     continue;
1315                 }
1316                 Object o = con.newInstance(new Object[] {constructorParameter});
1317                 return o;
1318             }
1319             throw new NoSuchAlgorithmException("No constructor matching "
1320                 + argClass.getName() + " found in class " + className);
1321         }
1322 
1323         /**
1324          * Test whether this Service can use the specified parameter.
1325          * Returns false if this service cannot use the parameter. Returns
1326          * true if this service can use the parameter, if a fast test is
1327          * infeasible, or if the status is unknown.
1328          *
1329          * <p>The security provider framework uses this method with
1330          * some types of services to quickly exclude non-matching
1331          * implementations for consideration.
1332          * Applications will typically not need to call it.
1333          *
1334          * <p>For details and the values of parameter that are valid for the
1335          * various types of services see the top of this class and the
1336          * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1337          * Java Cryptography Architecture API Specification &amp;
1338          * Reference</a>.
1339          * Security providers can override it to implement their own test.
1340          *
1341          * @param parameter the parameter to test
1342          *
1343          * @return false if this this service cannot use the specified
1344          * parameter; true if it can possibly use the parameter
1345          *
1346          * @throws InvalidParameterException if the value of parameter is
1347          * invalid for this type of service or if this method cannot be
1348          * used with this type of service
1349          */
1350         public boolean supportsParameter(Object parameter) {
1351             EngineDescription cap = knownEngines.get(type);
1352             if (cap == null) {
1353                 // unknown engine type, return true by default
1354                 return true;
1355             }
1356             if (cap.supportsParameter == false) {
1357                 throw new InvalidParameterException("supportsParameter() not "
1358                     + "used with " + type + " engines");
1359             }
1360             // allow null for keys without attributes for compatibility
1361             if ((parameter != null) && (parameter instanceof Key == false)) {
1362                 throw new InvalidParameterException
1363                     ("Parameter must be instanceof Key for engine " + type);
1364             }
1365             if (hasKeyAttributes() == false) {
1366                 return true;
1367             }
1368             if (parameter == null) {
1369                 return false;
1370             }
1371             Key key = (Key)parameter;
1372             if (supportsKeyFormat(key)) {
1373                 return true;
1374             }
1375             if (supportsKeyClass(key)) {
1376                 return true;
1377             }
1378             return false;
1379         }
1380 
1381         /**
1382          * Return whether this service has its Supported* properties for
1383          * keys defined. Parses the attributes if not yet initialized.
1384          */
1385         private boolean hasKeyAttributes() {
1386             Boolean b = hasKeyAttributes;
1387             if (b == null) {
1388                 synchronized (this) {
1389                     String s;
1390                     s = getAttribute("SupportedKeyFormats");
1391                     if (s != null) {
1392                         supportedFormats = s.split("\\|");
1393                     }
1394                     s = getAttribute("SupportedKeyClasses");
1395                     if (s != null) {
1396                         String[] classNames = s.split("\\|");
1397                         List<Class> classList =
1398                             new ArrayList<>(classNames.length);
1399                         for (String className : classNames) {
1400                             Class clazz = getKeyClass(className);
1401                             if (clazz != null) {
1402                                 classList.add(clazz);
1403                             }
1404                         }
1405                         supportedClasses = classList.toArray(CLASS0);
1406                     }
1407                     boolean bool = (supportedFormats != null)
1408                         || (supportedClasses != null);
1409                     b = Boolean.valueOf(bool);
1410                     hasKeyAttributes = b;
1411                 }
1412             }
1413             return b.booleanValue();
1414         }
1415 
1416         // get the key class object of the specified name
1417         private Class getKeyClass(String name) {
1418             try {
1419                 return Class.forName(name);
1420             } catch (ClassNotFoundException e) {
1421                 // ignore
1422             }
1423             try {
1424                 ClassLoader cl = provider.getClass().getClassLoader();
1425                 if (cl != null) {
1426                     return cl.loadClass(name);
1427                 }
1428             } catch (ClassNotFoundException e) {
1429                 // ignore
1430             }
1431             return null;
1432         }
1433 
1434         private boolean supportsKeyFormat(Key key) {
1435             if (supportedFormats == null) {
1436                 return false;
1437             }
1438             String format = key.getFormat();
1439             if (format == null) {
1440                 return false;
1441             }
1442             for (String supportedFormat : supportedFormats) {
1443                 if (supportedFormat.equals(format)) {
1444                     return true;
1445                 }
1446             }
1447             return false;
1448         }
1449 
1450         private boolean supportsKeyClass(Key key) {
1451             if (supportedClasses == null) {
1452                 return false;
1453             }
1454             Class keyClass = key.getClass();
1455             for (Class clazz : supportedClasses) {
1456                 if (clazz.isAssignableFrom(keyClass)) {
1457                     return true;
1458                 }
1459             }
1460             return false;
1461         }
1462 
1463         /**
1464          * Return a String representation of this service.
1465          *
1466          * @return a String representation of this service.
1467          */
1468         public String toString() {
1469             String aString = aliases.isEmpty()
1470                 ? "" : "\r\n  aliases: " + aliases.toString();
1471             String attrs = attributes.isEmpty()
1472                 ? "" : "\r\n  attributes: " + attributes.toString();
1473             return provider.getName() + ": " + type + "." + algorithm
1474                 + " -> " + className + aString + attrs + "\r\n";
1475         }
1476 
1477     }
1478 
1479 }