       1 /*
       2  * Copyright (c) 1996, 2021, 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 import java.util.function.BiConsumer;
      34 import java.util.function.BiFunction;
      35 import java.util.function.Function;
      36 import java.util.concurrent.ConcurrentHashMap;
      37
      38 /**
      39  * This class represents a "provider" for the
      40  * Java Security API, where a provider implements some or all parts of
      41  * Java Security. Services that a provider may implement include:
      42  *
      43  * <ul>
      44  *
      45  * <li>Algorithms (such as DSA, RSA, or SHA-256).
      46  *
      47  * <li>Key generation, conversion, and management facilities (such as for
      48  * algorithm-specific keys).
      49  *
      50  * </ul>
      51  *
      52  * <p>Some provider implementations may encounter unrecoverable internal
      53  * errors during their operation, for example a failure to communicate with a
      54  * security token. A {@link ProviderException} should be used to indicate
      55  * such errors.
      56  *
      57  * <p>Please note that a provider can be used to implement any security
      58  * service in Java that uses a pluggable architecture with a choice
      59  * of implementations that fit underneath.
      60  *
      61  * <p>The service type {@code Provider} is reserved for use by the
      62  * security framework. Services of this type cannot be added, removed,
      63  * or modified by applications.
      64  * The following attributes are automatically placed in each Provider object:
      65  * <table class="striped">
      66  * <caption><b>Attributes Automatically Placed in a Provider Object</b></caption>
      67  * <thead>
      68  * <tr><th scope="col">Name</th><th scope="col">Value</th>
      69  * </thead>
      70  * <tbody style="text-align:left">
      71  * <tr><th scope="row">{@code Provider.id name}</th>
      72  *     <td>{@code String.valueOf(provider.getName())}</td>
      73  * <tr><th scope="row">{@code Provider.id version}</th>
      74  *     <td>{@code String.valueOf(provider.getVersionStr())}</td>
      75  * <tr><th scope="row">{@code Provider.id info}</th>
      76  *     <td>{@code String.valueOf(provider.getInfo())}</td>
      77  * <tr><th scope="row">{@code Provider.id className}</th>
      78  *     <td>{@code provider.getClass().getName()}</td>
      79  * </tbody>
      80  * </table>
      81  *
      82  * <p>Each provider has a name and a version string. A provider normally
      83  * identifies itself with a file named {@code java.security.Provider}
      84  * in the resource directory {@code META-INF/services}.
      85  * Security providers are looked up via the {@link ServiceLoader} mechanism
      86  * using the {@link ClassLoader#getSystemClassLoader application class loader}.
      87  *
      88  * <p>Providers may be configured such that they are automatically
      89  * installed and made available at runtime via the
      90  * {@link Security#getProviders() Security.getProviders()} method.
      91  * The mechanism for configuring and installing security providers is
      92  * implementation-specific.
      93  *
      94  * @implNote
      95  * The JDK implementation supports static registration of the security
      96  * providers via the {@code conf/security/java.security} file in the Java
      97  * installation directory. These providers are automatically installed by
      98  * the JDK runtime, see {@extLink security_guide_jca_provider
      99  * The Provider Class}
     100  * in the Java Cryptography Architecture (JCA) Reference Guide
     101  * for information about how a particular type of provider, the cryptographic
     102  * service provider, works and is installed.
     103  *
     104  * @author Benjamin Renaud
     105  * @author Andreas Sterbenz
     106  * @since 1.1
     107  */
     108 public abstract class Provider extends Properties {
     109
     110     // Declare serialVersionUID to be compatible with JDK1.1
     111     @java.io.Serial
     112     private static final long serialVersionUID = -4298000515446427739L;
     113
     114     private static final sun.security.util.Debug debug =
     115         sun.security.util.Debug.getInstance("provider", "Provider");
     116
     117     /**
     118      * The provider name.
     119      *
     120      * @serial
     121      */
     122     private String name;
     123
     124     /**
     125      * A description of the provider and its services.
     126      *
     127      * @serial
     128      */
     129     private String info;
     130
     131     /**
     132      * The provider version number.
     133      *
     134      * @serial
     135      */
     136     private double version;
     137
     138     /**
     139      * The provider version string.
     140      *
     141      * @serial
     142      */
     143     private String versionStr;
     144
     145     private transient Set<Map.Entry<Object,Object>> entrySet = null;
     146     private transient int entrySetCallCount = 0;
     147
     148     private transient boolean initialized;
     149
     150     private static final Object[] EMPTY = new Object[0];
     151
     152     private static double parseVersionStr(String s) {
     153         try {
     154             int firstDotIdx = s.indexOf('.');
     155             int nextDotIdx = s.indexOf('.', firstDotIdx + 1);
     156             if (nextDotIdx != -1) {
     157                 s = s.substring(0, nextDotIdx);
     158             }
     159             int endIdx = s.indexOf('-');
     160             if (endIdx > 0) {
     161                 s = s.substring(0, endIdx);
     162             }
     163             endIdx = s.indexOf('+');
     164             if (endIdx > 0) {
     165                 s = s.substring(0, endIdx);
     166             }
     167             return Double.parseDouble(s);
     168         } catch (NullPointerException | NumberFormatException e) {
     169             return 0d;
     170         }
     171     }
     172
     173     /**
     174      * Constructs a provider with the specified name, version number,
     175      * and information. Calling this constructor is equivalent to call the
     176      * {@link #Provider(String, String, String)} with {@code name}
     177      * name, {@code Double.toString(version)}, and {@code info}.
     178      *
     179      * @param name the provider name.
     180      *
     181      * @param version the provider version number.
     182      *
     183      * @param info a description of the provider and its services.
     184      *
     185      * @deprecated use {@link #Provider(String, String, String)} instead.
     186      */
     187     @Deprecated(since="9")
     188     protected Provider(String name, double version, String info) {
     189         this.name = name;
     190         this.version = version;
     191         this.versionStr = Double.toString(version);
     192         this.info = info;
     193         this.serviceMap = new ConcurrentHashMap<>();
     194         putId();
     195         initialized = true;
     196     }
     197
     198     /**
     199      * Constructs a provider with the specified name, version string,
     200      * and information.
     201      *
     202      * <p>The version string contains a version number optionally followed
     203      * by other information separated by one of the characters of '+', '-'.
     204      *
     205      * The format for the version number is:
     206      *
     207      * <blockquote><pre>
     208      *     ^[0-9]+(\.[0-9]+)*
     209      * </pre></blockquote>
     210      *
     211      * <p>In order to return the version number in a double, when there are
     212      * more than two components (separated by '.' as defined above), only
     213      * the first two components are retained. The resulting string is then
     214      * passed to {@link Double#valueOf(String)} to generate version number,
     215      * i.e. {@link #getVersion}.
     216      * <p>If the conversion failed, value 0 will be used.
     217      *
     218      * @param name the provider name.
     219      *
     220      * @param versionStr the provider version string.
     221      *
     222      * @param info a description of the provider and its services.
     223      *
     224      * @since 9
     225      */
     226     protected Provider(String name, String versionStr, String info) {
     227         this.name = name;
     228         this.versionStr = versionStr;
     229         this.version = parseVersionStr(versionStr);
     230         this.info = info;
     231         this.serviceMap = new ConcurrentHashMap<>();
     232         putId();
     233         initialized = true;
     234     }
     235
     236     /**
     237      * Apply the supplied configuration argument to this provider instance
     238      * and return the configured provider. Note that if this provider cannot
     239      * be configured in-place, a new provider will be created and returned.
     240      * Therefore, callers should always use the returned provider.
     241      *
     242      * @implSpec
     243      * The default implementation throws {@code UnsupportedOperationException}.
     244      * Subclasses should override this method only if a configuration argument
     245      * is supported.
     246      *
     247      * @param configArg the configuration information for configuring this
     248      *         provider.
     249      *
     250      * @throws UnsupportedOperationException if a configuration argument is
     251      *         not supported.
     252      * @throws NullPointerException if the supplied configuration argument is
     253      *         null.
     254      * @throws InvalidParameterException if the supplied configuration argument
     255      *         is invalid.
     256      * @return a provider configured with the supplied configuration argument.
     257      *
     258      * @since 9
     259      */
     260     public Provider configure(String configArg) {
     261         throw new UnsupportedOperationException("configure is not supported");
     262     }
     263
     264     /**
     265      * Check if this provider instance has been configured.
     266      *
     267      * @implSpec
     268      * The default implementation returns true.
     269      * Subclasses should override this method if the provider instance requires
     270      * an explicit {@code configure} call after being constructed.
     271      *
     272      * @return true if no further configuration is needed, false otherwise.
     273      *
     274      * @since 9
     275      */
     276     public boolean isConfigured() {
     277         return true;
     278     }
     279
     280
     281     /**
     282      * Returns the name of this provider.
     283      *
     284      * @return the name of this provider.
     285      */
     286     public String getName() {
     287         return name;
     288     }
     289
     290     /**
     291      * Returns the version number for this provider.
     292      *
     293      * @return the version number for this provider.
     294      *
     295      * @deprecated use {@link #getVersionStr} instead.
     296      */
     297     @Deprecated(since="9")
     298     public double getVersion() {
     299         return version;
     300     }
     301
     302     /**
     303      * Returns the version string for this provider.
     304      *
     305      * @return the version string for this provider.
     306      *
     307      * @since 9
     308      */
     309     public String getVersionStr() {
     310         return versionStr;
     311     }
     312
     313     /**
     314      * Returns a human-readable description of the provider and its
     315      * services.  This may return an HTML page, with relevant links.
     316      *
     317      * @return a description of the provider and its services.
     318      */
     319     public String getInfo() {
     320         return info;
     321     }
     322
     323     /**
     324      * Returns a string with the name and the version string
     325      * of this provider.
     326      *
     327      * @return the string with the name and the version string
     328      * for this provider.
     329      */
     330     public String toString() {
     331         return name + " version " + versionStr;
     332     }
     333
     334     /*
     335      * override the following methods to ensure that provider
     336      * information can only be changed if the caller has the appropriate
     337      * permissions.
     338      */
     339
     340     /**
     341      * Clears this provider so that it no longer contains the properties
     342      * used to look up facilities implemented by the provider.
     343      *
     344      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     345      * method is called with the string {@code "clearProviderProperties."+name}
     346      * (where {@code name} is the provider name) to see if it's ok to clear
     347      * this provider.
     348      *
     349      * @throws  SecurityException
     350      *          if a security manager exists and its {@link
     351      *          java.lang.SecurityManager#checkSecurityAccess} method
     352      *          denies access to clear this provider
     353      *
     354      * @since 1.2
     355      */
     356     @Override
     357     public synchronized void clear() {
     358         check("clearProviderProperties."+name);
     359         if (debug != null) {
     360             debug.println("Remove " + name + " provider properties");
     361         }
     362         implClear();
     363     }
     364
     365     /**
     366      * Reads a property list (key and element pairs) from the input stream.
     367      *
     368      * @param inStream the input stream.
     369      * @throws    IOException if an error occurred when reading from the
     370      *               input stream.
     371      * @see java.util.Properties#load
     372      */
     373     @Override
     374     public synchronized void load(InputStream inStream) throws IOException {
     375         check("putProviderProperty."+name);
     376         if (debug != null) {
     377             debug.println("Load " + name + " provider properties");
     378         }
     379         Properties tempProperties = new Properties();
     380         tempProperties.load(inStream);
     381         implPutAll(tempProperties);
     382     }
     383
     384     /**
     385      * Copies all of the mappings from the specified Map to this provider.
     386      * These mappings will replace any properties that this provider had
     387      * for any of the keys currently in the specified Map.
     388      *
     389      * @since 1.2
     390      */
     391     @Override
     392     public synchronized void putAll(Map<?,?> t) {
     393         check("putProviderProperty."+name);
     394         if (debug != null) {
     395             debug.println("Put all " + name + " provider properties");
     396         }
     397         implPutAll(t);
     398     }
     399
     400     /**
     401      * Returns an unmodifiable Set view of the property entries contained
     402      * in this Provider.
     403      *
     404      * @see   java.util.Map.Entry
     405      * @since 1.2
     406      */
     407     @Override
     408     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
     409         checkInitialized();
     410         if (entrySet == null) {
     411             if (entrySetCallCount++ == 0)  // Initial call
     412                 entrySet = Collections.unmodifiableMap(this).entrySet();
     413             else
     414                 return super.entrySet();   // Recursive call
     415         }
     416
     417         // This exception will be thrown if the implementation of
     418         // Collections.unmodifiableMap.entrySet() is changed such that it
     419         // no longer calls entrySet() on the backing Map.  (Provider's
     420         // entrySet implementation depends on this "implementation detail",
     421         // which is unlikely to change.
     422         if (entrySetCallCount != 2)
     423             throw new RuntimeException("Internal error.");
     424
     425         return entrySet;
     426     }
     427
     428     /**
     429      * Returns an unmodifiable Set view of the property keys contained in
     430      * this provider.
     431      *
     432      * @since 1.2
     433      */
     434     @Override
     435     public Set<Object> keySet() {
     436         checkInitialized();
     437         return Collections.unmodifiableSet(super.keySet());
     438     }
     439
     440     /**
     441      * Returns an unmodifiable Collection view of the property values
     442      * contained in this provider.
     443      *
     444      * @since 1.2
     445      */
     446     @Override
     447     public Collection<Object> values() {
     448         checkInitialized();
     449         return Collections.unmodifiableCollection(super.values());
     450     }
     451
     452     /**
     453      * Sets the {@code key} property to have the specified
     454      * {@code value}.
     455      *
     456      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     457      * method is called with the string {@code "putProviderProperty."+name},
     458      * where {@code name} is the provider name, to see if it's ok to set this
     459      * provider's property values.
     460      *
     461      * @throws  SecurityException
     462      *          if a security manager exists and its {@link
     463      *          java.lang.SecurityManager#checkSecurityAccess} method
     464      *          denies access to set property values.
     465      *
     466      * @since 1.2
     467      */
     468     @Override
     469     public synchronized Object put(Object key, Object value) {
     470         check("putProviderProperty."+name);
     471         if (debug != null) {
     472             debug.println("Set " + name + " provider property [" +
     473                           key + "/" + value +"]");
     474         }
     475         return implPut(key, value);
     476     }
     477
     478     /**
     479      * If the specified key is not already associated with a value (or is mapped
     480      * to {@code null}) associates it with the given value and returns
     481      * {@code null}, else returns the current value.
     482      *
     483      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     484      * method is called with the string {@code "putProviderProperty."+name},
     485      * where {@code name} is the provider name, to see if it's ok to set this
     486      * provider's property values.
     487      *
     488      * @throws  SecurityException
     489      *          if a security manager exists and its {@link
     490      *          java.lang.SecurityManager#checkSecurityAccess} method
     491      *          denies access to set property values.
     492      *
     493      * @since 1.8
     494      */
     495     @Override
     496     public synchronized Object putIfAbsent(Object key, Object value) {
     497         check("putProviderProperty."+name);
     498         if (debug != null) {
     499             debug.println("Set " + name + " provider property [" +
     500                           key + "/" + value +"]");
     501         }
     502         return implPutIfAbsent(key, value);
     503     }
     504
     505     /**
     506      * Removes the {@code key} property (and its corresponding
     507      * {@code value}).
     508      *
     509      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     510      * method is called with the string {@code "removeProviderProperty."+name},
     511      * where {@code name} is the provider name, to see if it's ok to remove this
     512      * provider's properties.
     513      *
     514      * @throws  SecurityException
     515      *          if a security manager exists and its {@link
     516      *          java.lang.SecurityManager#checkSecurityAccess} method
     517      *          denies access to remove this provider's properties.
     518      *
     519      * @since 1.2
     520      */
     521     @Override
     522     public synchronized Object remove(Object key) {
     523         check("removeProviderProperty."+name);
     524         if (debug != null) {
     525             debug.println("Remove " + name + " provider property " + key);
     526         }
     527         return implRemove(key);
     528     }
     529
     530     /**
     531      * Removes the entry for the specified key only if it is currently
     532      * mapped to the specified value.
     533      *
     534      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     535      * method is called with the string {@code "removeProviderProperty."+name},
     536      * where {@code name} is the provider name, to see if it's ok to remove this
     537      * provider's properties.
     538      *
     539      * @throws  SecurityException
     540      *          if a security manager exists and its {@link
     541      *          java.lang.SecurityManager#checkSecurityAccess} method
     542      *          denies access to remove this provider's properties.
     543      *
     544      * @since 1.8
     545      */
     546     @Override
     547     public synchronized boolean remove(Object key, Object value) {
     548         check("removeProviderProperty."+name);
     549         if (debug != null) {
     550             debug.println("Remove " + name + " provider property " + key);
     551         }
     552         return implRemove(key, value);
     553     }
     554
     555     /**
     556      * Replaces the entry for the specified key only if currently
     557      * mapped to the specified value.
     558      *
     559      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     560      * method is called with the string {@code "putProviderProperty."+name},
     561      * where {@code name} is the provider name, to see if it's ok to set this
     562      * provider's property values.
     563      *
     564      * @throws  SecurityException
     565      *          if a security manager exists and its {@link
     566      *          java.lang.SecurityManager#checkSecurityAccess} method
     567      *          denies access to set property values.
     568      *
     569      * @since 1.8
     570      */
     571     @Override
     572     public synchronized boolean replace(Object key, Object oldValue,
     573             Object newValue) {
     574         check("putProviderProperty." + name);
     575
     576         if (debug != null) {
     577             debug.println("Replace " + name + " provider property " + key);
     578         }
     579         return implReplace(key, oldValue, newValue);
     580     }
     581
     582     /**
     583      * Replaces the entry for the specified key only if it is
     584      * currently mapped to some value.
     585      *
     586      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     587      * method is called with the string {@code "putProviderProperty."+name},
     588      * where {@code name} is the provider name, to see if it's ok to set this
     589      * provider's property values.
     590      *
     591      * @throws  SecurityException
     592      *          if a security manager exists and its {@link
     593      *          java.lang.SecurityManager#checkSecurityAccess} method
     594      *          denies access to set property values.
     595      *
     596      * @since 1.8
     597      */
     598     @Override
     599     public synchronized Object replace(Object key, Object value) {
     600         check("putProviderProperty." + name);
     601
     602         if (debug != null) {
     603             debug.println("Replace " + name + " provider property " + key);
     604         }
     605         return implReplace(key, value);
     606     }
     607
     608     /**
     609      * Replaces each entry's value with the result of invoking the given
     610      * function on that entry, in the order entries are returned by an entry
     611      * set iterator, until all entries have been processed or the function
     612      * throws an exception.
     613      *
     614      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     615      * method is called with the string {@code "putProviderProperty."+name},
     616      * where {@code name} is the provider name, to see if it's ok to set this
     617      * provider's property values.
     618      *
     619      * @throws  SecurityException
     620      *          if a security manager exists and its {@link
     621      *          java.lang.SecurityManager#checkSecurityAccess} method
     622      *          denies access to set property values.
     623      *
     624      * @since 1.8
     625      */
     626     @Override
     627     public synchronized void replaceAll(BiFunction<? super Object,
     628             ? super Object, ? extends Object> function) {
     629         check("putProviderProperty." + name);
     630
     631         if (debug != null) {
     632             debug.println("ReplaceAll " + name + " provider property ");
     633         }
     634         implReplaceAll(function);
     635     }
     636
     637     /**
     638      * Attempts to compute a mapping for the specified key and its
     639      * current mapped value (or {@code null} if there is no current
     640      * mapping).
     641      *
     642      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     643      * method is called with the strings {@code "putProviderProperty."+name}
     644      * and {@code "removeProviderProperty."+name}, where {@code name} is the
     645      * provider name, to see if it's ok to set this provider's property values
     646      * and remove this provider's properties.
     647      *
     648      * @throws  SecurityException
     649      *          if a security manager exists and its {@link
     650      *          java.lang.SecurityManager#checkSecurityAccess} method
     651      *          denies access to set property values or remove properties.
     652      *
     653      * @since 1.8
     654      */
     655     @Override
     656     public synchronized Object compute(Object key, BiFunction<? super Object,
     657             ? super Object, ? extends Object> remappingFunction) {
     658         check("putProviderProperty." + name);
     659         check("removeProviderProperty." + name);
     660
     661         if (debug != null) {
     662             debug.println("Compute " + name + " provider property " + key);
     663         }
     664         return implCompute(key, remappingFunction);
     665     }
     666
     667     /**
     668      * If the specified key is not already associated with a value (or
     669      * is mapped to {@code null}), attempts to compute its value using
     670      * the given mapping function and enters it into this map unless
     671      * {@code null}.
     672      *
     673      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     674      * method is called with the strings {@code "putProviderProperty."+name}
     675      * and {@code "removeProviderProperty."+name}, where {@code name} is the
     676      * provider name, to see if it's ok to set this provider's property values
     677      * and remove this provider's properties.
     678      *
     679      * @throws  SecurityException
     680      *          if a security manager exists and its {@link
     681      *          java.lang.SecurityManager#checkSecurityAccess} method
     682      *          denies access to set property values and remove properties.
     683      *
     684      * @since 1.8
     685      */
     686     @Override
     687     public synchronized Object computeIfAbsent(Object key, Function<? super Object,
     688             ? extends Object> mappingFunction) {
     689         check("putProviderProperty." + name);
     690         check("removeProviderProperty." + name);
     691
     692         if (debug != null) {
     693             debug.println("ComputeIfAbsent " + name + " provider property " +
     694                     key);
     695         }
     696         return implComputeIfAbsent(key, mappingFunction);
     697     }
     698
     699     /**
     700      * If the value for the specified key is present and non-null, attempts to
     701      * compute a new mapping given the key and its current mapped value.
     702      *
     703      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     704      * method is called with the strings {@code "putProviderProperty."+name}
     705      * and {@code "removeProviderProperty."+name}, where {@code name} is the
     706      * provider name, to see if it's ok to set this provider's property values
     707      * and remove this provider's properties.
     708      *
     709      * @throws  SecurityException
     710      *          if a security manager exists and its {@link
     711      *          java.lang.SecurityManager#checkSecurityAccess} method
     712      *          denies access to set property values or remove properties.
     713      *
     714      * @since 1.8
     715      */
     716     @Override
     717     public synchronized Object computeIfPresent(Object key, BiFunction<? super Object,
     718             ? super Object, ? extends Object> remappingFunction) {
     719         check("putProviderProperty." + name);
     720         check("removeProviderProperty." + name);
     721
     722         if (debug != null) {
     723             debug.println("ComputeIfPresent " + name + " provider property " +
     724                     key);
     725         }
     726         return implComputeIfPresent(key, remappingFunction);
     727     }
     728
     729     /**
     730      * If the specified key is not already associated with a value or is
     731      * associated with null, associates it with the given value. Otherwise,
     732      * replaces the value with the results of the given remapping function,
     733      * or removes if the result is null. This method may be of use when
     734      * combining multiple mapped values for a key.
     735      *
     736      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
     737      * method is called with the strings {@code "putProviderProperty."+name}
     738      * and {@code "removeProviderProperty."+name}, where {@code name} is the
     739      * provider name, to see if it's ok to set this provider's property values
     740      * and remove this provider's properties.
     741      *
     742      * @throws  SecurityException
     743      *          if a security manager exists and its {@link
     744      *          java.lang.SecurityManager#checkSecurityAccess} method
     745      *          denies access to set property values or remove properties.
     746      *
     747      * @since 1.8
     748      */
     749     @Override
     750     public synchronized Object merge(Object key, Object value,  BiFunction<? super Object,
     751             ? super Object, ? extends Object>  remappingFunction) {
     752         check("putProviderProperty." + name);
     753         check("removeProviderProperty." + name);
     754
     755         if (debug != null) {
     756             debug.println("Merge " + name + " provider property " + key);
     757         }
     758         return implMerge(key, value, remappingFunction);
     759     }
     760
     761     // let javadoc show doc from superclass
     762     @Override
     763     public Object get(Object key) {
     764         checkInitialized();
     765         return super.get(key);
     766     }
     767     /**
     768      * @since 1.8
     769      */
     770     @Override
     771     public synchronized Object getOrDefault(Object key, Object defaultValue) {
     772         checkInitialized();
     773         return super.getOrDefault(key, defaultValue);
     774     }
     775
     776     /**
     777      * @since 1.8
     778      */
     779     @Override
     780     public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
     781         checkInitialized();
     782         super.forEach(action);
     783     }
     784
     785     // let javadoc show doc from superclass
     786     @Override
     787     public Enumeration<Object> keys() {
     788         checkInitialized();
     789         return super.keys();
     790     }
     791
     792     // let javadoc show doc from superclass
     793     @Override
     794     public Enumeration<Object> elements() {
     795         checkInitialized();
     796         return super.elements();
     797     }
     798
     799     // let javadoc show doc from superclass
     800     public String getProperty(String key) {
     801         checkInitialized();
     802         return super.getProperty(key);
     803     }
     804
     805     private void checkInitialized() {
     806         if (!initialized) {
     807             throw new IllegalStateException();
     808         }
     809     }
     810
     811     private void check(String directive) {
     812         checkInitialized();
     813         SecurityManager security = System.getSecurityManager();
     814         if (security != null) {
     815             security.checkSecurityAccess(directive);
     816         }
     817     }
     818
     819     // legacy properties changed since last call to any services method?
     820     private transient boolean legacyChanged;
     821     // serviceMap changed since last call to getServices()
     822     private volatile transient boolean servicesChanged;
     823
     824     // Map<String,String> used to keep track of legacy registration
     825     private transient Map<String,String> legacyStrings;
     826
     827     // Map<ServiceKey,Service>
     828     // used for services added via putService(), initialized on demand
     829     private transient Map<ServiceKey,Service> serviceMap;
     830
     831     // For backward compatibility, the registration ordering of
     832     // SecureRandom (RNG) algorithms needs to be preserved for
     833     // "new SecureRandom()" calls when this provider is used
     834     private transient Set<String> prngAlgos;
     835
     836     // Map<ServiceKey,Service>
     837     // used for services added via legacy methods, init on demand
     838     private transient Map<ServiceKey,Service> legacyMap;
     839
     840     // Set<Service>
     841     // Unmodifiable set of all services. Initialized on demand.
     842     private transient Set<Service> serviceSet;
     843
     844     // register the id attributes for this provider
     845     // this is to ensure that equals() and hashCode() do not incorrectly
     846     // report to different provider objects as the same
     847     private void putId() {
     848         // note: name and info may be null
     849         super.put("Provider.id name", String.valueOf(name));
     850         super.put("Provider.id version", String.valueOf(versionStr));
     851         super.put("Provider.id info", String.valueOf(info));
     852         super.put("Provider.id className", this.getClass().getName());
     853     }
     854
     855    /**
     856     * Reads the {@code ObjectInputStream} for the default serializable fields.
     857     * If the serialized field {@code versionStr} is found in the STREAM FIELDS,
     858     * its String value will be used to populate both the version string and
     859     * version number. If {@code versionStr} is not found, but {@code version}
     860     * is, then its double value will be used to populate both fields.
     861     *
     862     * @param in the {@code ObjectInputStream} to read
     863     * @throws IOException if an I/O error occurs
     864     * @throws ClassNotFoundException if a serialized class cannot be loaded
     865     * @serial
     866     */
     867     @java.io.Serial
     868     private void readObject(ObjectInputStream in)
     869                 throws IOException, ClassNotFoundException {
     870         Map<Object,Object> copy = new HashMap<>();
     871         for (Map.Entry<Object,Object> entry : super.entrySet()) {
     872             copy.put(entry.getKey(), entry.getValue());
     873         }
     874         defaults = null;
     875         in.defaultReadObject();
     876         if (this.versionStr == null) {
     877             // set versionStr based on version when not found in serialized bytes
     878             this.versionStr = Double.toString(this.version);
     879         } else {
     880             // otherwise, set version based on versionStr
     881             this.version = parseVersionStr(this.versionStr);
     882         }
     883         this.serviceMap = new ConcurrentHashMap<>();
     884         implClear();
     885         initialized = true;
     886         putAll(copy);
     887     }
     888
     889     // check whether to update 'legacyString' with the specified key
     890     private boolean checkLegacy(Object key) {
     891         String keyString = (String)key;
     892         if (keyString.startsWith("Provider.")) {
     893             return false;
     894         }
     895
     896         legacyChanged = true;
     897         if (legacyStrings == null) {
     898             legacyStrings = new LinkedHashMap<>();
     899         }
     900         return true;
     901     }
     902
     903     /**
     904      * Copies all of the mappings from the specified Map to this provider.
     905      * Internal method to be called AFTER the security check has been
     906      * performed.
     907      */
     908     private void implPutAll(Map<?,?> t) {
     909         for (Map.Entry<?,?> e : t.entrySet()) {
     910             implPut(e.getKey(), e.getValue());
     911         }
     912     }
     913
     914     private Object implRemove(Object key) {
     915         if (key instanceof String) {
     916             if (!checkLegacy(key)) {
     917                 return null;
     918             }
     919             legacyStrings.remove((String)key);
     920         }
     921         return super.remove(key);
     922     }
     923
     924     private boolean implRemove(Object key, Object value) {
     925         if (key instanceof String && value instanceof String) {
     926             if (!checkLegacy(key)) {
     927                 return false;
     928             }
     929             legacyStrings.remove((String)key, (String)value);
     930         }
     931         return super.remove(key, value);
     932     }
     933
     934     private boolean implReplace(Object key, Object oldValue, Object newValue) {
     935         if ((key instanceof String) && (oldValue instanceof String) &&
     936                 (newValue instanceof String)) {
     937             if (!checkLegacy(key)) {
     938                 return false;
     939             }
     940             legacyStrings.replace((String)key, (String)oldValue,
     941                     (String)newValue);
     942         }
     943         return super.replace(key, oldValue, newValue);
     944     }
     945
     946     private Object implReplace(Object key, Object value) {
     947         if ((key instanceof String) && (value instanceof String)) {
     948             if (!checkLegacy(key)) {
     949                 return null;
     950             }
     951             legacyStrings.replace((String)key, (String)value);
     952         }
     953         return super.replace(key, value);
     954     }
     955
     956     @SuppressWarnings("unchecked") // Function must actually operate over strings
     957     private void implReplaceAll(BiFunction<? super Object, ? super Object,
     958             ? extends Object> function) {
     959         legacyChanged = true;
     960         if (legacyStrings == null) {
     961             legacyStrings = new LinkedHashMap<>();
     962         } else {
     963             legacyStrings.replaceAll((BiFunction<? super String, ? super String,
     964                     ? extends String>) function);
     965         }
     966         super.replaceAll(function);
     967     }
     968
     969     @SuppressWarnings("unchecked") // Function must actually operate over strings
     970     private Object implMerge(Object key, Object value,
     971             BiFunction<? super Object, ? super Object, ? extends Object>
     972             remappingFunction) {
     973         if ((key instanceof String) && (value instanceof String)) {
     974             if (!checkLegacy(key)) {
     975                 return null;
     976             }
     977             legacyStrings.merge((String)key, (String)value,
     978                     (BiFunction<? super String, ? super String,
     979                     ? extends String>) remappingFunction);
     980         }
     981         return super.merge(key, value, remappingFunction);
     982     }
     983
     984     @SuppressWarnings("unchecked") // Function must actually operate over strings
     985     private Object implCompute(Object key, BiFunction<? super Object,
     986             ? super Object, ? extends Object> remappingFunction) {
     987         if (key instanceof String) {
     988             if (!checkLegacy(key)) {
     989                 return null;
     990             }
     991             legacyStrings.compute((String) key,
     992                     (BiFunction<? super String,? super String,
     993                     ? extends String>) remappingFunction);
     994         }
     995         return super.compute(key, remappingFunction);
     996     }
     997
     998     @SuppressWarnings("unchecked") // Function must actually operate over strings
     999     private Object implComputeIfAbsent(Object key, Function<? super Object,
    1000             ? extends Object> mappingFunction) {
    1001         if (key instanceof String) {
    1002             if (!checkLegacy(key)) {
    1003                 return null;
    1004             }
    1005             legacyStrings.computeIfAbsent((String) key,
    1006                     (Function<? super String, ? extends String>)
    1007                     mappingFunction);
    1008         }
    1009         return super.computeIfAbsent(key, mappingFunction);
    1010     }
    1011
    1012     @SuppressWarnings("unchecked") // Function must actually operate over strings
    1013     private Object implComputeIfPresent(Object key, BiFunction<? super Object,
    1014             ? super Object, ? extends Object> remappingFunction) {
    1015         if (key instanceof String) {
    1016             if (!checkLegacy(key)) {
    1017                 return null;
    1018             }
    1019             legacyStrings.computeIfPresent((String) key,
    1020                     (BiFunction<? super String, ? super String,
    1021                     ? extends String>) remappingFunction);
    1022         }
    1023         return super.computeIfPresent(key, remappingFunction);
    1024     }
    1025
    1026     private Object implPut(Object key, Object value) {
    1027         if ((key instanceof String) && (value instanceof String)) {
    1028             if (!checkLegacy(key)) {
    1029                 return null;
    1030             }
    1031             legacyStrings.put((String)key, (String)value);
    1032         }
    1033         return super.put(key, value);
    1034     }
    1035
    1036     private Object implPutIfAbsent(Object key, Object value) {
    1037         if ((key instanceof String) && (value instanceof String)) {
    1038             if (!checkLegacy(key)) {
    1039                 return null;
    1040             }
    1041             legacyStrings.putIfAbsent((String)key, (String)value);
    1042         }
    1043         return super.putIfAbsent(key, value);
    1044     }
    1045
    1046     private void implClear() {
    1047         if (legacyStrings != null) {
    1048             legacyStrings.clear();
    1049         }
    1050         if (legacyMap != null) {
    1051             legacyMap.clear();
    1052         }
    1053         serviceMap.clear();
    1054         legacyChanged = false;
    1055         servicesChanged = false;
    1056         serviceSet = null;
    1057         prngAlgos = null;
    1058         super.clear();
    1059         putId();
    1060     }
    1061
    1062     // used as key in the serviceMap and legacyMap HashMaps
    1063     private static class ServiceKey {
    1064         private final String type;
    1065         private final String algorithm;
    1066         private final String originalAlgorithm;
    1067         private ServiceKey(String type, String algorithm, boolean intern) {
    1068             this.type = type;
    1069             this.originalAlgorithm = algorithm;
    1070             algorithm = algorithm.toUpperCase(ENGLISH);
    1071             this.algorithm = intern ? algorithm.intern() : algorithm;
    1072         }
    1073         public int hashCode() {
    1074             return type.hashCode() * 31 + algorithm.hashCode();
    1075         }
    1076         public boolean equals(Object obj) {
    1077             if (this == obj) {
    1078                 return true;
    1079             }
    1080             if (!(obj instanceof ServiceKey other)) {
    1081                 return false;
    1082             }
    1083             return this.type.equals(other.type)
    1084                 && this.algorithm.equals(other.algorithm);
    1085         }
    1086         boolean matches(String type, String algorithm) {
    1087             return (this.type == type) && (this.originalAlgorithm == algorithm);
    1088         }
    1089     }
    1090
    1091     /**
    1092      * Ensure all the legacy String properties are fully parsed into
    1093      * service objects.
    1094      */
    1095     private void ensureLegacyParsed() {
    1096         if (legacyChanged == false || (legacyStrings == null)) {
    1097             return;
    1098         }
    1099         serviceSet = null;
    1100         if (legacyMap == null) {
    1101             legacyMap = new ConcurrentHashMap<>();
    1102         } else {
    1103             legacyMap.clear();
    1104         }
    1105         for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
    1106             parseLegacyPut(entry.getKey(), entry.getValue());
    1107         }
    1108         removeInvalidServices(legacyMap);
    1109         legacyChanged = false;
    1110     }
    1111
    1112     /**
    1113      * Remove all invalid services from the Map. Invalid services can only
    1114      * occur if the legacy properties are inconsistent or incomplete.
    1115      */
    1116     private void removeInvalidServices(Map<ServiceKey,Service> map) {
    1117         for (Iterator<Map.Entry<ServiceKey, Service>> t =
    1118                 map.entrySet().iterator(); t.hasNext(); ) {
    1119             Service s = t.next().getValue();
    1120             if (s.isValid() == false) {
    1121                 t.remove();
    1122             }
    1123         }
    1124     }
    1125
    1126     private static String[] getTypeAndAlgorithm(String key) {
    1127         int i = key.indexOf('.');
    1128         if (i < 1) {
    1129             if (debug != null) {
    1130                 debug.println("Ignoring invalid entry in provider: "
    1131                         + key);
    1132             }
    1133             return null;
    1134         }
    1135         String type = key.substring(0, i);
    1136         String alg = key.substring(i + 1);
    1137         return new String[] {type, alg};
    1138     }
    1139
    1140     private static final String ALIAS_PREFIX = "Alg.Alias.";
    1141     private static final String ALIAS_PREFIX_LOWER = "alg.alias.";
    1142     private static final int ALIAS_LENGTH = ALIAS_PREFIX.length();
    1143
    1144     private void parseLegacyPut(String name, String value) {
    1145         if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
    1146             // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
    1147             // aliasKey ~ MessageDigest.SHA
    1148             String stdAlg = value;
    1149             String aliasKey = name.substring(ALIAS_LENGTH);
    1150             String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
    1151             if (typeAndAlg == null) {
    1152                 return;
    1153             }
    1154             String type = getEngineName(typeAndAlg[0]);
    1155             String aliasAlg = typeAndAlg[1].intern();
    1156             ServiceKey key = new ServiceKey(type, stdAlg, true);
    1157             Service s = legacyMap.get(key);
    1158             if (s == null) {
    1159                 s = new Service(this, type, stdAlg);
    1160                 legacyMap.put(key, s);
    1161             }
    1162             legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
    1163             s.addAlias(aliasAlg);
    1164         } else {
    1165             String[] typeAndAlg = getTypeAndAlgorithm(name);
    1166             if (typeAndAlg == null) {
    1167                 return;
    1168             }
    1169             int i = typeAndAlg[1].indexOf(' ');
    1170             if (i == -1) {
    1171                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
    1172                 String type = getEngineName(typeAndAlg[0]);
    1173                 String stdAlg = typeAndAlg[1].intern();
    1174                 String className = value;
    1175                 ServiceKey key = new ServiceKey(type, stdAlg, true);
    1176                 Service s = legacyMap.get(key);
    1177                 if (s == null) {
    1178                     s = new Service(this, type, stdAlg);
    1179                     legacyMap.put(key, s);
    1180                 }
    1181                 s.className = className;
    1182
    1183                 if (type.equals("SecureRandom")) {
    1184                     updateSecureRandomEntries(true, s.algorithm);
    1185                 }
    1186             } else { // attribute
    1187                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
    1188                 String attributeValue = value;
    1189                 String type = getEngineName(typeAndAlg[0]);
    1190                 String attributeString = typeAndAlg[1];
    1191                 String stdAlg = attributeString.substring(0, i).intern();
    1192                 String attributeName = attributeString.substring(i + 1);
    1193                 // kill additional spaces
    1194                 while (attributeName.startsWith(" ")) {
    1195                     attributeName = attributeName.substring(1);
    1196                 }
    1197                 attributeName = attributeName.intern();
    1198                 ServiceKey key = new ServiceKey(type, stdAlg, true);
    1199                 Service s = legacyMap.get(key);
    1200                 if (s == null) {
    1201                     s = new Service(this, type, stdAlg);
    1202                     legacyMap.put(key, s);
    1203                 }
    1204                 s.addAttribute(attributeName, attributeValue);
    1205             }
    1206         }
    1207     }
    1208
    1209     /**
    1210      * Get the service describing this Provider's implementation of the
    1211      * specified type of this algorithm or alias. If no such
    1212      * implementation exists, this method returns null. If there are two
    1213      * matching services, one added to this provider using
    1214      * {@link #putService putService()} and one added via {@link #put put()},
    1215      * the service added via {@link #putService putService()} is returned.
    1216      *
    1217      * @param type the type of {@link Service service} requested
    1218      * (for example, {@code MessageDigest})
    1219      * @param algorithm the case insensitive algorithm name (or alternate
    1220      * alias) of the service requested (for example, {@code SHA-1})
    1221      *
    1222      * @return the service describing this Provider's matching service
    1223      * or null if no such service exists
    1224      *
    1225      * @throws NullPointerException if type or algorithm is null
    1226      *
    1227      * @since 1.5
    1228      */
    1229     public Service getService(String type, String algorithm) {
    1230         checkInitialized();
    1231
    1232         // avoid allocating a new ServiceKey object if possible
    1233         ServiceKey key = previousKey;
    1234         if (key.matches(type, algorithm) == false) {
    1235             key = new ServiceKey(type, algorithm, false);
    1236             previousKey = key;
    1237         }
    1238         if (!serviceMap.isEmpty()) {
    1239             Service s = serviceMap.get(key);
    1240             if (s != null) {
    1241                 return s;
    1242             }
    1243         }
    1244         synchronized (this) {
    1245             ensureLegacyParsed();
    1246             if (legacyMap != null && !legacyMap.isEmpty()) {
    1247                 return legacyMap.get(key);
    1248             }
    1249         }
    1250         return null;
    1251     }
    1252
    1253     // ServiceKey from previous getService() call
    1254     // by re-using it if possible we avoid allocating a new object
    1255     // and the toUpperCase() call.
    1256     // re-use will occur e.g. as the framework traverses the provider
    1257     // list and queries each provider with the same values until it finds
    1258     // a matching service
    1259     private static volatile ServiceKey previousKey =
    1260                                             new ServiceKey("", "", false);
    1261
    1262     /**
    1263      * Get an unmodifiable Set of all services supported by
    1264      * this Provider.
    1265      *
    1266      * @return an unmodifiable Set of all services supported by
    1267      * this Provider
    1268      *
    1269      * @since 1.5
    1270      */
    1271     public synchronized Set<Service> getServices() {
    1272         checkInitialized();
    1273         if (legacyChanged || servicesChanged) {
    1274             serviceSet = null;
    1275         }
    1276         if (serviceSet == null) {
    1277             ensureLegacyParsed();
    1278             Set<Service> set = new LinkedHashSet<>();
    1279             if (!serviceMap.isEmpty()) {
    1280                 set.addAll(serviceMap.values());
    1281             }
    1282             if (legacyMap != null && !legacyMap.isEmpty()) {
    1283                 set.addAll(legacyMap.values());
    1284             }
    1285             serviceSet = Collections.unmodifiableSet(set);
    1286             servicesChanged = false;
    1287         }
    1288         return serviceSet;
    1289     }
    1290
    1291     /**
    1292      * Add a service. If a service of the same type with the same algorithm
    1293      * name exists and it was added using {@link #putService putService()},
    1294      * it is replaced by the new service.
    1295      * This method also places information about this service
    1296      * in the provider's Hashtable values in the format described in the
    1297      * {@extLink security_guide_jca
    1298      * Java Cryptography Architecture (JCA) Reference Guide}.
    1299      *
    1300      * <p>Also, if there is a security manager, its
    1301      * {@code checkSecurityAccess} method is called with the string
    1302      * {@code "putProviderProperty."+name}, where {@code name} is
    1303      * the provider name, to see if it's ok to set this provider's property
    1304      * values. If the default implementation of {@code checkSecurityAccess}
    1305      * is used (that is, that method is not overridden), then this results in
    1306      * a call to the security manager's {@code checkPermission} method with
    1307      * a {@code SecurityPermission("putProviderProperty."+name)}
    1308      * permission.
    1309      *
    1310      * @param s the Service to add
    1311      *
    1312      * @throws SecurityException
    1313      *      if a security manager exists and its {@link
    1314      *      java.lang.SecurityManager#checkSecurityAccess} method denies
    1315      *      access to set property values.
    1316      * @throws NullPointerException if s is null
    1317      *
    1318      * @since 1.5
    1319      */
    1320     protected void putService(Service s) {
    1321         check("putProviderProperty." + name);
    1322         if (debug != null) {
    1323             debug.println(name + ".putService(): " + s);
    1324         }
    1325         if (s == null) {
    1326             throw new NullPointerException();
    1327         }
    1328         if (s.getProvider() != this) {
    1329             throw new IllegalArgumentException
    1330                     ("service.getProvider() must match this Provider object");
    1331         }
    1332         String type = s.getType();
    1333         String algorithm = s.getAlgorithm();
    1334         ServiceKey key = new ServiceKey(type, algorithm, true);
    1335         implRemoveService(serviceMap.get(key));
    1336         serviceMap.put(key, s);
    1337         for (String alias : s.getAliases()) {
    1338             serviceMap.put(new ServiceKey(type, alias, true), s);
    1339         }
    1340         servicesChanged = true;
    1341         synchronized (this) {
    1342             putPropertyStrings(s);
    1343             if (type.equals("SecureRandom")) {
    1344                 updateSecureRandomEntries(true, s.algorithm);
    1345             }
    1346         }
    1347     }
    1348
    1349     // keep tracks of the registered secure random algos and store them in order
    1350     private void updateSecureRandomEntries(boolean doAdd, String s) {
    1351         Objects.requireNonNull(s);
    1352         if (doAdd) {
    1353             if (prngAlgos == null) {
    1354                 prngAlgos = new LinkedHashSet<String>();
    1355             }
    1356             prngAlgos.add(s);
    1357         } else {
    1358             prngAlgos.remove(s);
    1359         }
    1360
    1361         if (debug != null) {
    1362             debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " + s);
    1363         }
    1364     }
    1365
    1366     // used by new SecureRandom() to find out the default SecureRandom
    1367     // service for this provider
    1368     synchronized Service getDefaultSecureRandomService() {
    1369         checkInitialized();
    1370
    1371         if (legacyChanged) {
    1372             prngAlgos = null;
    1373             ensureLegacyParsed();
    1374         }
    1375
    1376         if (prngAlgos != null && !prngAlgos.isEmpty()) {
    1377             // IMPORTANT: use the Service obj returned by getService(...) call
    1378             // as providers may override putService(...)/getService(...) and
    1379             // return their own Service objects
    1380             return getService("SecureRandom", prngAlgos.iterator().next());
    1381         }
    1382
    1383         return null;
    1384     }
    1385
    1386     /**
    1387      * Put the string properties for this Service in this Provider's
    1388      * Hashtable.
    1389      */
    1390     private void putPropertyStrings(Service s) {
    1391         String type = s.getType();
    1392         String algorithm = s.getAlgorithm();
    1393         // use super() to avoid permission check and other processing
    1394         super.put(type + "." + algorithm, s.getClassName());
    1395         for (String alias : s.getAliases()) {
    1396             super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
    1397         }
    1398         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
    1399             String key = type + "." + algorithm + " " + entry.getKey();
    1400             super.put(key, entry.getValue());
    1401         }
    1402     }
    1403
    1404     /**
    1405      * Remove the string properties for this Service from this Provider's
    1406      * Hashtable.
    1407      */
    1408     private void removePropertyStrings(Service s) {
    1409         String type = s.getType();
    1410         String algorithm = s.getAlgorithm();
    1411         // use super() to avoid permission check and other processing
    1412         super.remove(type + "." + algorithm);
    1413         for (String alias : s.getAliases()) {
    1414             super.remove(ALIAS_PREFIX + type + "." + alias);
    1415         }
    1416         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
    1417             String key = type + "." + algorithm + " " + entry.getKey();
    1418             super.remove(key);
    1419         }
    1420     }
    1421
    1422     /**
    1423      * Remove a service previously added using
    1424      * {@link #putService putService()}. The specified service is removed from
    1425      * this provider. It will no longer be returned by
    1426      * {@link #getService getService()} and its information will be removed
    1427      * from this provider's Hashtable.
    1428      *
    1429      * <p>Also, if there is a security manager, its
    1430      * {@code checkSecurityAccess} method is called with the string
    1431      * {@code "removeProviderProperty."+name}, where {@code name} is
    1432      * the provider name, to see if it's ok to remove this provider's
    1433      * properties. If the default implementation of
    1434      * {@code checkSecurityAccess} is used (that is, that method is not
    1435      * overridden), then this results in a call to the security manager's
    1436      * {@code checkPermission} method with a
    1437      * {@code SecurityPermission("removeProviderProperty."+name)}
    1438      * permission.
    1439      *
    1440      * @param s the Service to be removed
    1441      *
    1442      * @throws  SecurityException
    1443      *          if a security manager exists and its {@link
    1444      *          java.lang.SecurityManager#checkSecurityAccess} method denies
    1445      *          access to remove this provider's properties.
    1446      * @throws NullPointerException if s is null
    1447      *
    1448      * @since 1.5
    1449      */
    1450     protected void removeService(Service s) {
    1451         check("removeProviderProperty." + name);
    1452         if (debug != null) {
    1453             debug.println(name + ".removeService(): " + s);
    1454         }
    1455         if (s == null) {
    1456             throw new NullPointerException();
    1457         }
    1458         implRemoveService(s);
    1459     }
    1460
    1461     private void implRemoveService(Service s) {
    1462         if ((s == null) || serviceMap.isEmpty()) {
    1463             return;
    1464         }
    1465         String type = s.getType();
    1466         String algorithm = s.getAlgorithm();
    1467         ServiceKey key = new ServiceKey(type, algorithm, false);
    1468         Service oldService = serviceMap.get(key);
    1469         if (s != oldService) {
    1470             return;
    1471         }
    1472         servicesChanged = true;
    1473         serviceMap.remove(key);
    1474         for (String alias : s.getAliases()) {
    1475             serviceMap.remove(new ServiceKey(type, alias, false));
    1476         }
    1477         synchronized (this) {
    1478             removePropertyStrings(s);
    1479             if (type.equals("SecureRandom")) {
    1480                 updateSecureRandomEntries(false, s.algorithm);
    1481             }
    1482         }
    1483     }
    1484
    1485     // Wrapped String that behaves in a case insensitive way for equals/hashCode
    1486     private static class UString {
    1487         final String string;
    1488         final String lowerString;
    1489
    1490         UString(String s) {
    1491             this.string = s;
    1492             this.lowerString = s.toLowerCase(ENGLISH);
    1493         }
    1494
    1495         public int hashCode() {
    1496             return lowerString.hashCode();
    1497         }
    1498
    1499         public boolean equals(Object obj) {
    1500             if (this == obj) {
    1501                 return true;
    1502             }
    1503             if (obj instanceof UString == false) {
    1504                 return false;
    1505             }
    1506             UString other = (UString)obj;
    1507             return lowerString.equals(other.lowerString);
    1508         }
    1509
    1510         public String toString() {
    1511             return string;
    1512         }
    1513     }
    1514
    1515     // describe relevant properties of a type of engine
    1516     private static class EngineDescription {
    1517         final String name;
    1518         final boolean supportsParameter;
    1519         final String constructorParameterClassName;
    1520         private volatile Class<?> constructorParameterClass;
    1521
    1522         EngineDescription(String name, boolean sp, String paramName) {
    1523             this.name = name;
    1524             this.supportsParameter = sp;
    1525             this.constructorParameterClassName = paramName;
    1526         }
    1527         Class<?> getConstructorParameterClass() throws ClassNotFoundException {
    1528             Class<?> clazz = constructorParameterClass;
    1529             if (clazz == null) {
    1530                 clazz = Class.forName(constructorParameterClassName);
    1531                 constructorParameterClass = clazz;
    1532             }
    1533             return clazz;
    1534         }
    1535     }
    1536
    1537     // built in knowledge of the engine types shipped as part of the JDK
    1538     private static final Map<String,EngineDescription> knownEngines;
    1539
    1540     private static void addEngine(String name, boolean sp, String paramName) {
    1541         EngineDescription ed = new EngineDescription(name, sp, paramName);
    1542         // also index by canonical name to avoid toLowerCase() for some lookups
    1543         knownEngines.put(name.toLowerCase(ENGLISH), ed);
    1544         knownEngines.put(name, ed);
    1545     }
    1546
    1547     static {
    1548         knownEngines = new HashMap<>();
    1549         // JCA
    1550         addEngine("AlgorithmParameterGenerator",        false, null);
    1551         addEngine("AlgorithmParameters",                false, null);
    1552         addEngine("KeyFactory",                         false, null);
    1553         addEngine("KeyPairGenerator",                   false, null);
    1554         addEngine("KeyStore",                           false, null);
    1555         addEngine("MessageDigest",                      false, null);
    1556         addEngine("SecureRandom",                       false,
    1557                 "java.security.SecureRandomParameters");
    1558         addEngine("Signature",                          true,  null);
    1559         addEngine("CertificateFactory",                 false, null);
    1560         addEngine("CertPathBuilder",                    false, null);
    1561         addEngine("CertPathValidator",                  false, null);
    1562         addEngine("CertStore",                          false,
    1563                             "java.security.cert.CertStoreParameters");
    1564         // JCE
    1565         addEngine("Cipher",                             true,  null);
    1566         addEngine("ExemptionMechanism",                 false, null);
    1567         addEngine("Mac",                                true,  null);
    1568         addEngine("KeyAgreement",                       true,  null);
    1569         addEngine("KeyGenerator",                       false, null);
    1570         addEngine("SecretKeyFactory",                   false, null);
    1571         // JSSE
    1572         addEngine("KeyManagerFactory",                  false, null);
    1573         addEngine("SSLContext",                         false, null);
    1574         addEngine("TrustManagerFactory",                false, null);
    1575         // JGSS
    1576         addEngine("GssApiMechanism",                    false, null);
    1577         // SASL
    1578         addEngine("SaslClientFactory",                  false, null);
    1579         addEngine("SaslServerFactory",                  false, null);
    1580         // POLICY
    1581         addEngine("Policy",                             false,
    1582                             "java.security.Policy$Parameters");
    1583         // CONFIGURATION
    1584         addEngine("Configuration",                      false,
    1585                             "javax.security.auth.login.Configuration$Parameters");
    1586         // XML DSig
    1587         addEngine("XMLSignatureFactory",                false, null);
    1588         addEngine("KeyInfoFactory",                     false, null);
    1589         addEngine("TransformService",                   false, null);
    1590         // Smart Card I/O
    1591         addEngine("TerminalFactory",                    false,
    1592                             "java.lang.Object");
    1593     }
    1594
    1595     // get the "standard" (mixed-case) engine name for arbitary case engine name
    1596     // if there is no known engine by that name, return s
    1597     private static String getEngineName(String s) {
    1598         // try original case first, usually correct
    1599         EngineDescription e = knownEngines.get(s);
    1600         if (e == null) {
    1601             e = knownEngines.get(s.toLowerCase(ENGLISH));
    1602         }
    1603         return (e == null) ? s : e.name;
    1604     }
    1605
    1606     /**
    1607      * The description of a security service. It encapsulates the properties
    1608      * of a service and contains a factory method to obtain new implementation
    1609      * instances of this service.
    1610      *
    1611      * <p>Each service has a provider that offers the service, a type,
    1612      * an algorithm name, and the name of the class that implements the
    1613      * service. Optionally, it also includes a list of alternate algorithm
    1614      * names for this service (aliases) and attributes, which are a map of
    1615      * (name, value) String pairs.
    1616      *
    1617      * <p>This class defines the methods {@link #supportsParameter
    1618      * supportsParameter()} and {@link #newInstance newInstance()}
    1619      * which are used by the Java security framework when it searches for
    1620      * suitable services and instantiates them. The valid arguments to those
    1621      * methods depend on the type of service. For the service types defined
    1622      * within Java SE, see the
    1623      * {@extLink security_guide_jca
    1624      * Java Cryptography Architecture (JCA) Reference Guide}
    1625      * for the valid values.
    1626      * Note that components outside of Java SE can define additional types of
    1627      * services and their behavior.
    1628      *
    1629      * <p>Instances of this class are immutable.
    1630      *
    1631      * @since 1.5
    1632      */
    1633     public static class Service {
    1634         private final String type;
    1635         private final String algorithm;
    1636         private String className;
    1637         private final Provider provider;
    1638         private List<String> aliases;
    1639         private Map<UString,String> attributes;
    1640         private final EngineDescription engineDescription;
    1641
    1642         // Reference to the cached implementation Class object.
    1643         // Will be a Class if this service is loaded from the built-in
    1644         // classloader (unloading not possible), otherwise a WeakReference to a
    1645         // Class
    1646         private Object classCache;
    1647
    1648         // Will be a Constructor if this service is loaded from the built-in
    1649         // classloader (unloading not possible), otherwise a WeakReference to
    1650         // a Constructor
    1651         private Object constructorCache;
    1652
    1653         // flag indicating whether this service has its attributes for
    1654         // supportedKeyFormats or supportedKeyClasses set
    1655         // if null, the values have not been initialized
    1656         // if TRUE, at least one of supportedFormats/Classes is non null
    1657         private volatile Boolean hasKeyAttributes;
    1658
    1659         // supported encoding formats
    1660         private String[] supportedFormats;
    1661
    1662         // names of the supported key (super) classes
    1663         private Class<?>[] supportedClasses;
    1664
    1665         // whether this service has been registered with the Provider
    1666         private boolean registered;
    1667
    1668         private static final Class<?>[] CLASS0 = new Class<?>[0];
    1669
    1670         // this constructor and these methods are used for parsing
    1671         // the legacy string properties.
    1672
    1673         private Service(Provider provider, String type, String algorithm) {
    1674             this.provider = provider;
    1675             this.type = type;
    1676             this.algorithm = algorithm;
    1677             engineDescription = knownEngines.get(type);
    1678             aliases = Collections.<String>emptyList();
    1679             attributes = Collections.<UString,String>emptyMap();
    1680         }
    1681
    1682         private boolean isValid() {
    1683             return (type != null) && (algorithm != null) && (className != null);
    1684         }
    1685
    1686         private void addAlias(String alias) {
    1687             if (aliases.isEmpty()) {
    1688                 aliases = new ArrayList<>(2);
    1689             }
    1690             aliases.add(alias);
    1691         }
    1692
    1693         void addAttribute(String type, String value) {
    1694             if (attributes.isEmpty()) {
    1695                 attributes = new HashMap<>(8);
    1696             }
    1697             attributes.put(new UString(type), value);
    1698         }
    1699
    1700         /**
    1701          * Construct a new service.
    1702          *
    1703          * @param provider the provider that offers this service
    1704          * @param type the type of this service
    1705          * @param algorithm the algorithm name
    1706          * @param className the name of the class implementing this service
    1707          * @param aliases List of aliases or null if algorithm has no aliases
    1708          * @param attributes Map of attributes or null if this implementation
    1709          *                   has no attributes
    1710          *
    1711          * @throws NullPointerException if provider, type, algorithm, or
    1712          * className is null
    1713          */
    1714         public Service(Provider provider, String type, String algorithm,
    1715                 String className, List<String> aliases,
    1716                 Map<String,String> attributes) {
    1717             if ((provider == null) || (type == null) ||
    1718                     (algorithm == null) || (className == null)) {
    1719                 throw new NullPointerException();
    1720             }
    1721             this.provider = provider;
    1722             this.type = getEngineName(type);
    1723             engineDescription = knownEngines.get(type);
    1724             this.algorithm = algorithm;
    1725             this.className = className;
    1726             if (aliases == null) {
    1727                 this.aliases = Collections.<String>emptyList();
    1728             } else {
    1729                 this.aliases = new ArrayList<>(aliases);
    1730             }
    1731             if (attributes == null) {
    1732                 this.attributes = Collections.<UString,String>emptyMap();
    1733             } else {
    1734                 this.attributes = new HashMap<>();
    1735                 for (Map.Entry<String,String> entry : attributes.entrySet()) {
    1736                     this.attributes.put(new UString(entry.getKey()), entry.getValue());
    1737                 }
    1738             }
    1739         }
    1740
    1741         /**
    1742          * Get the type of this service. For example, {@code MessageDigest}.
    1743          *
    1744          * @return the type of this service
    1745          */
    1746         public final String getType() {
    1747             return type;
    1748         }
    1749
    1750         /**
    1751          * Return the name of the algorithm of this service. For example,
    1752          * {@code SHA-1}.
    1753          *
    1754          * @return the algorithm of this service
    1755          */
    1756         public final String getAlgorithm() {
    1757             return algorithm;
    1758         }
    1759
    1760         /**
    1761          * Return the Provider of this service.
    1762          *
    1763          * @return the Provider of this service
    1764          */
    1765         public final Provider getProvider() {
    1766             return provider;
    1767         }
    1768
    1769         /**
    1770          * Return the name of the class implementing this service.
    1771          *
    1772          * @return the name of the class implementing this service
    1773          */
    1774         public final String getClassName() {
    1775             return className;
    1776         }
    1777
    1778         // internal only
    1779         private final List<String> getAliases() {
    1780             return aliases;
    1781         }
    1782
    1783         /**
    1784          * Return the value of the specified attribute or null if this
    1785          * attribute is not set for this Service.
    1786          *
    1787          * @param name the name of the requested attribute
    1788          *
    1789          * @return the value of the specified attribute or null if the
    1790          *         attribute is not present
    1791          *
    1792          * @throws NullPointerException if name is null
    1793          */
    1794         public final String getAttribute(String name) {
    1795             if (name == null) {
    1796                 throw new NullPointerException();
    1797             }
    1798             return attributes.get(new UString(name));
    1799         }
    1800
    1801         /**
    1802          * Return a new instance of the implementation described by this
    1803          * service. The security provider framework uses this method to
    1804          * construct implementations. Applications will typically not need
    1805          * to call it.
    1806          *
    1807          * <p>The default implementation uses reflection to invoke the
    1808          * standard constructor for this type of service.
    1809          * Security providers can override this method to implement
    1810          * instantiation in a different way.
    1811          * For details and the values of constructorParameter that are
    1812          * valid for the various types of services see the
    1813          * {@extLink security_guide_jca
    1814          * Java Cryptography Architecture (JCA) Reference Guide}.
    1815          *
    1816          * @param constructorParameter the value to pass to the constructor,
    1817          * or null if this type of service does not use a constructorParameter.
    1818          *
    1819          * @return a new implementation of this service
    1820          *
    1821          * @throws InvalidParameterException if the value of
    1822          * constructorParameter is invalid for this type of service.
    1823          * @throws NoSuchAlgorithmException if instantiation failed for
    1824          * any other reason.
    1825          */
    1826         public Object newInstance(Object constructorParameter)
    1827                 throws NoSuchAlgorithmException {
    1828             if (registered == false) {
    1829                 if (provider.getService(type, algorithm) != this) {
    1830                     throw new NoSuchAlgorithmException
    1831                         ("Service not registered with Provider "
    1832                         + provider.getName() + ": " + this);
    1833                 }
    1834                 registered = true;
    1835             }
    1836             Class<?> ctrParamClz;
    1837             try {
    1838                 EngineDescription cap = engineDescription;
    1839                 if (cap == null) {
    1840                     // unknown engine type, use generic code
    1841                     // this is the code path future for non-core
    1842                     // optional packages
    1843                     ctrParamClz = constructorParameter == null?
    1844                         null : constructorParameter.getClass();
    1845                 } else {
    1846                     ctrParamClz = cap.constructorParameterClassName == null?
    1847                         null : Class.forName(cap.constructorParameterClassName);
    1848                     if (constructorParameter != null) {
    1849                         if (ctrParamClz == null) {
    1850                             throw new InvalidParameterException
    1851                                 ("constructorParameter not used with " + type
    1852                                 + " engines");
    1853                         } else {
    1854                             Class<?> argClass = constructorParameter.getClass();
    1855                             if (ctrParamClz.isAssignableFrom(argClass) == false) {
    1856                                 throw new InvalidParameterException
    1857                                     ("constructorParameter must be instanceof "
    1858                                     + cap.constructorParameterClassName.replace('$', '.')
    1859                                     + " for engine type " + type);
    1860                             }
    1861                         }
    1862                     }
    1863                 }
    1864                 // constructorParameter can be null if not provided
    1865                 return newInstanceUtil(ctrParamClz, constructorParameter);
    1866             } catch (NoSuchAlgorithmException e) {
    1867                 throw e;
    1868             } catch (InvocationTargetException e) {
    1869                 throw new NoSuchAlgorithmException
    1870                     ("Error constructing implementation (algorithm: "
    1871                     + algorithm + ", provider: " + provider.getName()
    1872                     + ", class: " + className + ")", e.getCause());
    1873             } catch (Exception e) {
    1874                 throw new NoSuchAlgorithmException
    1875                     ("Error constructing implementation (algorithm: "
    1876                     + algorithm + ", provider: " + provider.getName()
    1877                     + ", class: " + className + ")", e);
    1878             }
    1879         }
    1880
    1881         private Object newInstanceOf() throws Exception {
    1882             Constructor<?> con = getDefaultConstructor();
    1883             return con.newInstance(EMPTY);
    1884         }
    1885
    1886         private Object newInstanceUtil(Class<?> ctrParamClz, Object ctorParamObj)
    1887                 throws Exception
    1888         {
    1889             if (ctrParamClz == null) {
    1890                 return newInstanceOf();
    1891             } else {
    1892                 // Looking for the constructor with a params first and fallback
    1893                 // to one without if not found. This is to support the enhanced
    1894                 // SecureRandom where both styles of constructors are supported.
    1895                 // Before jdk9, there was no params support (only getInstance(alg))
    1896                 // and an impl only had the params-less constructor. Since jdk9,
    1897                 // there is getInstance(alg,params) and an impl can contain
    1898                 // an Impl(params) constructor.
    1899                 try {
    1900                     Constructor<?> con = getImplClass().getConstructor(ctrParamClz);
    1901                     return con.newInstance(ctorParamObj);
    1902                 } catch (NoSuchMethodException nsme) {
    1903                     // For pre-jdk9 SecureRandom implementations, they only
    1904                     // have params-less constructors which still works when
    1905                     // the input ctorParamObj is null.
    1906                     //
    1907                     // For other primitives using params, ctorParamObj should not
    1908                     // be null and nsme is thrown, just like before.
    1909                     if (ctorParamObj == null) {
    1910                         try {
    1911                             return newInstanceOf();
    1912                         } catch (NoSuchMethodException nsme2) {
    1913                             nsme.addSuppressed(nsme2);
    1914                             throw nsme;
    1915                         }
    1916                     } else {
    1917                         throw nsme;
    1918                     }
    1919                 }
    1920             }
    1921         }
    1922
    1923         // return the implementation Class object for this service
    1924         private Class<?> getImplClass() throws NoSuchAlgorithmException {
    1925             try {
    1926                 Object cache = classCache;
    1927                 if (cache instanceof Class<?> clazz) {
    1928                     return clazz;
    1929                 }
    1930                 Class<?> clazz = null;
    1931                 if (cache instanceof WeakReference<?> ref){
    1932                     clazz = (Class<?>)ref.get();
    1933                 }
    1934                 if (clazz == null) {
    1935                     ClassLoader cl = provider.getClass().getClassLoader();
    1936                     if (cl == null) {
    1937                         clazz = Class.forName(className);
    1938                     } else {
    1939                         clazz = cl.loadClass(className);
    1940                     }
    1941                     if (!Modifier.isPublic(clazz.getModifiers())) {
    1942                         throw new NoSuchAlgorithmException
    1943                             ("class configured for " + type + " (provider: " +
    1944                             provider.getName() + ") is not public.");
    1945                     }
    1946                     classCache = (cl == null) ? clazz : new WeakReference<Class<?>>(clazz);
    1947                 }
    1948                 return clazz;
    1949             } catch (ClassNotFoundException e) {
    1950                 throw new NoSuchAlgorithmException
    1951                     ("class configured for " + type + " (provider: " +
    1952                     provider.getName() + ") cannot be found.", e);
    1953             }
    1954         }
    1955
    1956         private Constructor<?> getDefaultConstructor()
    1957             throws NoSuchAlgorithmException, NoSuchMethodException
    1958         {
    1959             Object cache = constructorCache;
    1960             if (cache instanceof Constructor<?> con) {
    1961                 return con;
    1962             }
    1963             Constructor<?> con = null;
    1964             if (cache instanceof WeakReference<?> ref){
    1965                 con = (Constructor<?>)ref.get();
    1966             }
    1967             if (con == null) {
    1968                 Class<?> clazz = getImplClass();
    1969                 con = clazz.getConstructor();
    1970                 constructorCache = (clazz.getClassLoader() == null)
    1971                         ? con : new WeakReference<Constructor<?>>(con);
    1972             }
    1973             return con;
    1974         }
    1975
    1976         /**
    1977          * Test whether this Service can use the specified parameter.
    1978          * Returns false if this service cannot use the parameter. Returns
    1979          * true if this service can use the parameter, if a fast test is
    1980          * infeasible, or if the status is unknown.
    1981          *
    1982          * <p>The security provider framework uses this method with
    1983          * some types of services to quickly exclude non-matching
    1984          * implementations for consideration.
    1985          * Applications will typically not need to call it.
    1986          *
    1987          * <p>For details and the values of parameter that are valid for the
    1988          * various types of services see the top of this class and the
    1989          * {@extLink security_guide_jca
    1990          * Java Cryptography Architecture (JCA) Reference Guide}.
    1991          * Security providers can override it to implement their own test.
    1992          *
    1993          * @param parameter the parameter to test
    1994          *
    1995          * @return false if this service cannot use the specified
    1996          * parameter; true if it can possibly use the parameter
    1997          *
    1998          * @throws InvalidParameterException if the value of parameter is
    1999          * invalid for this type of service or if this method cannot be
    2000          * used with this type of service
    2001          */
    2002         public boolean supportsParameter(Object parameter) {
    2003             EngineDescription cap = engineDescription;
    2004             if (cap == null) {
    2005                 // unknown engine type, return true by default
    2006                 return true;
    2007             }
    2008             if (cap.supportsParameter == false) {
    2009                 throw new InvalidParameterException("supportsParameter() not "
    2010                     + "used with " + type + " engines");
    2011             }
    2012             // allow null for keys without attributes for compatibility
    2013             if ((parameter != null) && (parameter instanceof Key == false)) {
    2014                 throw new InvalidParameterException
    2015                     ("Parameter must be instanceof Key for engine " + type);
    2016             }
    2017             if (hasKeyAttributes() == false) {
    2018                 return true;
    2019             }
    2020             if (parameter == null) {
    2021                 return false;
    2022             }
    2023             Key key = (Key)parameter;
    2024             if (supportsKeyFormat(key)) {
    2025                 return true;
    2026             }
    2027             if (supportsKeyClass(key)) {
    2028                 return true;
    2029             }
    2030             return false;
    2031         }
    2032
    2033         /**
    2034          * Return whether this service has its supported properties for
    2035          * keys defined. Parses the attributes if not yet initialized.
    2036          */
    2037         private boolean hasKeyAttributes() {
    2038             Boolean b = hasKeyAttributes;
    2039             if (b == null) {
    2040                 synchronized (this) {
    2041                     b = hasKeyAttributes;
    2042                     if (b == null) {
    2043                         String s;
    2044                         s = getAttribute("SupportedKeyFormats");
    2045                         if (s != null) {
    2046                             supportedFormats = s.split("\\|");
    2047                         }
    2048                         s = getAttribute("SupportedKeyClasses");
    2049                         if (s != null) {
    2050                             String[] classNames = s.split("\\|");
    2051                             List<Class<?>> classList =
    2052                                 new ArrayList<>(classNames.length);
    2053                             for (String className : classNames) {
    2054                                 Class<?> clazz = getKeyClass(className);
    2055                                 if (clazz != null) {
    2056                                     classList.add(clazz);
    2057                                 }
    2058                             }
    2059                             supportedClasses = classList.toArray(CLASS0);
    2060                         }
    2061                         boolean bool = (supportedFormats != null)
    2062                             || (supportedClasses != null);
    2063                         b = Boolean.valueOf(bool);
    2064                         hasKeyAttributes = b;
    2065                     }
    2066                 }
    2067             }
    2068             return b.booleanValue();
    2069         }
    2070
    2071         // get the key class object of the specified name
    2072         private Class<?> getKeyClass(String name) {
    2073             try {
    2074                 return Class.forName(name);
    2075             } catch (ClassNotFoundException e) {
    2076                 // ignore
    2077             }
    2078             try {
    2079                 ClassLoader cl = provider.getClass().getClassLoader();
    2080                 if (cl != null) {
    2081                     return cl.loadClass(name);
    2082                 }
    2083             } catch (ClassNotFoundException e) {
    2084                 // ignore
    2085             }
    2086             return null;
    2087         }
    2088
    2089         private boolean supportsKeyFormat(Key key) {
    2090             if (supportedFormats == null) {
    2091                 return false;
    2092             }
    2093             String format = key.getFormat();
    2094             if (format == null) {
    2095                 return false;
    2096             }
    2097             for (String supportedFormat : supportedFormats) {
    2098                 if (supportedFormat.equals(format)) {
    2099                     return true;
    2100                 }
    2101             }
    2102             return false;
    2103         }
    2104
    2105         private boolean supportsKeyClass(Key key) {
    2106             if (supportedClasses == null) {
    2107                 return false;
    2108             }
    2109             Class<?> keyClass = key.getClass();
    2110             for (Class<?> clazz : supportedClasses) {
    2111                 if (clazz.isAssignableFrom(keyClass)) {
    2112                     return true;
    2113                 }
    2114             }
    2115             return false;
    2116         }
    2117
    2118         /**
    2119          * Return a String representation of this service.
    2120          *
    2121          * @return a String representation of this service.
    2122          */
    2123         public String toString() {
    2124             String aString = aliases.isEmpty()
    2125                 ? "" : "\r\n  aliases: " + aliases.toString();
    2126             String attrs = attributes.isEmpty()
    2127                 ? "" : "\r\n  attributes: " + attributes.toString();
    2128             return provider.getName() + ": " + type + "." + algorithm
    2129                 + " -> " + className + aString + attrs + "\r\n";
    2130         }
    2131     }
    2132 }
    --- EOF ---