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 
parseVersionStr(String s)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")
Provider(String name, double version, String info)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      */
Provider(String name, String versionStr, String info)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      */
configure(String configArg)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      */
isConfigured()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      */
getName()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")
getVersion()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      */
getVersionStr()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      */
getInfo()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      */
toString()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
clear()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
load(InputStream inStream)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
putAll(Map<?,?> t)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
entrySet()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
keySet()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
values()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
put(Object key, Object value)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
putIfAbsent(Object key, Object value)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
remove(Object key)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
remove(Object key, Object value)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
replace(Object key, Object oldValue, Object newValue)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
replace(Object key, Object value)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
replaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function)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
compute(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)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
computeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction)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
computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)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
merge(Object key, Object value, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)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
get(Object key)763     public Object get(Object key) {
764         checkInitialized();
765         return super.get(key);
766     }
767     /**
768      * @since 1.8
769      */
770     @Override
getOrDefault(Object key, Object defaultValue)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
forEach(BiConsumer<? super Object, ? super Object> action)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
keys()787     public Enumeration<Object> keys() {
788         checkInitialized();
789         return super.keys();
790     }
791 
792     // let javadoc show doc from superclass
793     @Override
elements()794     public Enumeration<Object> elements() {
795         checkInitialized();
796         return super.elements();
797     }
798 
799     // let javadoc show doc from superclass
getProperty(String key)800     public String getProperty(String key) {
801         checkInitialized();
802         return super.getProperty(key);
803     }
804 
checkInitialized()805     private void checkInitialized() {
806         if (!initialized) {
807             throw new IllegalStateException();
808         }
809     }
810 
check(String directive)811     private void check(String directive) {
812         checkInitialized();
813         @SuppressWarnings("removal")
814         SecurityManager security = System.getSecurityManager();
815         if (security != null) {
816             security.checkSecurityAccess(directive);
817         }
818     }
819 
820     // legacy properties changed since last call to any services method?
821     private transient boolean legacyChanged;
822     // serviceMap changed since last call to getServices()
823     private volatile transient boolean servicesChanged;
824 
825     // Map<String,String> used to keep track of legacy registration
826     private transient Map<String,String> legacyStrings;
827 
828     // Map<ServiceKey,Service>
829     // used for services added via putService(), initialized on demand
830     private transient Map<ServiceKey,Service> serviceMap;
831 
832     // For backward compatibility, the registration ordering of
833     // SecureRandom (RNG) algorithms needs to be preserved for
834     // "new SecureRandom()" calls when this provider is used
835     private transient Set<String> prngAlgos;
836 
837     // Map<ServiceKey,Service>
838     // used for services added via legacy methods, init on demand
839     private transient Map<ServiceKey,Service> legacyMap;
840 
841     // Set<Service>
842     // Unmodifiable set of all services. Initialized on demand.
843     private transient Set<Service> serviceSet;
844 
845     // register the id attributes for this provider
846     // this is to ensure that equals() and hashCode() do not incorrectly
847     // report to different provider objects as the same
putId()848     private void putId() {
849         // note: name and info may be null
850         super.put("Provider.id name", String.valueOf(name));
851         super.put("Provider.id version", String.valueOf(versionStr));
852         super.put("Provider.id info", String.valueOf(info));
853         super.put("Provider.id className", this.getClass().getName());
854     }
855 
856    /**
857     * Reads the {@code ObjectInputStream} for the default serializable fields.
858     * If the serialized field {@code versionStr} is found in the STREAM FIELDS,
859     * its String value will be used to populate both the version string and
860     * version number. If {@code versionStr} is not found, but {@code version}
861     * is, then its double value will be used to populate both fields.
862     *
863     * @param in the {@code ObjectInputStream} to read
864     * @throws IOException if an I/O error occurs
865     * @throws ClassNotFoundException if a serialized class cannot be loaded
866     * @serial
867     */
868     @java.io.Serial
readObject(ObjectInputStream in)869     private void readObject(ObjectInputStream in)
870                 throws IOException, ClassNotFoundException {
871         Map<Object,Object> copy = new HashMap<>();
872         for (Map.Entry<Object,Object> entry : super.entrySet()) {
873             copy.put(entry.getKey(), entry.getValue());
874         }
875         defaults = null;
876         in.defaultReadObject();
877         if (this.versionStr == null) {
878             // set versionStr based on version when not found in serialized bytes
879             this.versionStr = Double.toString(this.version);
880         } else {
881             // otherwise, set version based on versionStr
882             this.version = parseVersionStr(this.versionStr);
883         }
884         this.serviceMap = new ConcurrentHashMap<>();
885         implClear();
886         initialized = true;
887         putAll(copy);
888     }
889 
890     // check whether to update 'legacyString' with the specified key
checkLegacy(Object key)891     private boolean checkLegacy(Object key) {
892         String keyString = (String)key;
893         if (keyString.startsWith("Provider.")) {
894             return false;
895         }
896 
897         legacyChanged = true;
898         if (legacyStrings == null) {
899             legacyStrings = new LinkedHashMap<>();
900         }
901         return true;
902     }
903 
904     /**
905      * Copies all of the mappings from the specified Map to this provider.
906      * Internal method to be called AFTER the security check has been
907      * performed.
908      */
implPutAll(Map<?,?> t)909     private void implPutAll(Map<?,?> t) {
910         for (Map.Entry<?,?> e : t.entrySet()) {
911             implPut(e.getKey(), e.getValue());
912         }
913     }
914 
implRemove(Object key)915     private Object implRemove(Object key) {
916         if (key instanceof String) {
917             if (!checkLegacy(key)) {
918                 return null;
919             }
920             legacyStrings.remove((String)key);
921         }
922         return super.remove(key);
923     }
924 
implRemove(Object key, Object value)925     private boolean implRemove(Object key, Object value) {
926         if (key instanceof String && value instanceof String) {
927             if (!checkLegacy(key)) {
928                 return false;
929             }
930             legacyStrings.remove((String)key, (String)value);
931         }
932         return super.remove(key, value);
933     }
934 
implReplace(Object key, Object oldValue, Object newValue)935     private boolean implReplace(Object key, Object oldValue, Object newValue) {
936         if ((key instanceof String) && (oldValue instanceof String) &&
937                 (newValue instanceof String)) {
938             if (!checkLegacy(key)) {
939                 return false;
940             }
941             legacyStrings.replace((String)key, (String)oldValue,
942                     (String)newValue);
943         }
944         return super.replace(key, oldValue, newValue);
945     }
946 
implReplace(Object key, Object value)947     private Object implReplace(Object key, Object value) {
948         if ((key instanceof String) && (value instanceof String)) {
949             if (!checkLegacy(key)) {
950                 return null;
951             }
952             legacyStrings.replace((String)key, (String)value);
953         }
954         return super.replace(key, value);
955     }
956 
957     @SuppressWarnings("unchecked") // Function must actually operate over strings
implReplaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function)958     private void implReplaceAll(BiFunction<? super Object, ? super Object,
959             ? extends Object> function) {
960         legacyChanged = true;
961         if (legacyStrings == null) {
962             legacyStrings = new LinkedHashMap<>();
963         } else {
964             legacyStrings.replaceAll((BiFunction<? super String, ? super String,
965                     ? extends String>) function);
966         }
967         super.replaceAll(function);
968     }
969 
970     @SuppressWarnings("unchecked") // Function must actually operate over strings
implMerge(Object key, Object value, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)971     private Object implMerge(Object key, Object value,
972             BiFunction<? super Object, ? super Object, ? extends Object>
973             remappingFunction) {
974         if ((key instanceof String) && (value instanceof String)) {
975             if (!checkLegacy(key)) {
976                 return null;
977             }
978             legacyStrings.merge((String)key, (String)value,
979                     (BiFunction<? super String, ? super String,
980                     ? extends String>) remappingFunction);
981         }
982         return super.merge(key, value, remappingFunction);
983     }
984 
985     @SuppressWarnings("unchecked") // Function must actually operate over strings
implCompute(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)986     private Object implCompute(Object key, BiFunction<? super Object,
987             ? super Object, ? extends Object> remappingFunction) {
988         if (key instanceof String) {
989             if (!checkLegacy(key)) {
990                 return null;
991             }
992             legacyStrings.compute((String) key,
993                     (BiFunction<? super String,? super String,
994                     ? extends String>) remappingFunction);
995         }
996         return super.compute(key, remappingFunction);
997     }
998 
999     @SuppressWarnings("unchecked") // Function must actually operate over strings
implComputeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction)1000     private Object implComputeIfAbsent(Object key, Function<? super Object,
1001             ? extends Object> mappingFunction) {
1002         if (key instanceof String) {
1003             if (!checkLegacy(key)) {
1004                 return null;
1005             }
1006             legacyStrings.computeIfAbsent((String) key,
1007                     (Function<? super String, ? extends String>)
1008                     mappingFunction);
1009         }
1010         return super.computeIfAbsent(key, mappingFunction);
1011     }
1012 
1013     @SuppressWarnings("unchecked") // Function must actually operate over strings
implComputeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)1014     private Object implComputeIfPresent(Object key, BiFunction<? super Object,
1015             ? super Object, ? extends Object> remappingFunction) {
1016         if (key instanceof String) {
1017             if (!checkLegacy(key)) {
1018                 return null;
1019             }
1020             legacyStrings.computeIfPresent((String) key,
1021                     (BiFunction<? super String, ? super String,
1022                     ? extends String>) remappingFunction);
1023         }
1024         return super.computeIfPresent(key, remappingFunction);
1025     }
1026 
implPut(Object key, Object value)1027     private Object implPut(Object key, Object value) {
1028         if ((key instanceof String) && (value instanceof String)) {
1029             if (!checkLegacy(key)) {
1030                 return null;
1031             }
1032             legacyStrings.put((String)key, (String)value);
1033         }
1034         return super.put(key, value);
1035     }
1036 
implPutIfAbsent(Object key, Object value)1037     private Object implPutIfAbsent(Object key, Object value) {
1038         if ((key instanceof String) && (value instanceof String)) {
1039             if (!checkLegacy(key)) {
1040                 return null;
1041             }
1042             legacyStrings.putIfAbsent((String)key, (String)value);
1043         }
1044         return super.putIfAbsent(key, value);
1045     }
1046 
implClear()1047     private void implClear() {
1048         if (legacyStrings != null) {
1049             legacyStrings.clear();
1050         }
1051         if (legacyMap != null) {
1052             legacyMap.clear();
1053         }
1054         serviceMap.clear();
1055         legacyChanged = false;
1056         servicesChanged = false;
1057         serviceSet = null;
1058         prngAlgos = null;
1059         super.clear();
1060         putId();
1061     }
1062 
1063     // used as key in the serviceMap and legacyMap HashMaps
1064     private static class ServiceKey {
1065         private final String type;
1066         private final String algorithm;
1067         private final String originalAlgorithm;
ServiceKey(String type, String algorithm, boolean intern)1068         private ServiceKey(String type, String algorithm, boolean intern) {
1069             this.type = type;
1070             this.originalAlgorithm = algorithm;
1071             algorithm = algorithm.toUpperCase(ENGLISH);
1072             this.algorithm = intern ? algorithm.intern() : algorithm;
1073         }
hashCode()1074         public int hashCode() {
1075             return type.hashCode() * 31 + algorithm.hashCode();
1076         }
equals(Object obj)1077         public boolean equals(Object obj) {
1078             if (this == obj) {
1079                 return true;
1080             }
1081             return obj instanceof ServiceKey other
1082                 && this.type.equals(other.type)
1083                 && this.algorithm.equals(other.algorithm);
1084         }
matches(String type, String algorithm)1085         boolean matches(String type, String algorithm) {
1086             return (this.type == type) && (this.originalAlgorithm == algorithm);
1087         }
1088     }
1089 
1090     /**
1091      * Ensure all the legacy String properties are fully parsed into
1092      * service objects.
1093      */
ensureLegacyParsed()1094     private void ensureLegacyParsed() {
1095         if (legacyChanged == false || (legacyStrings == null)) {
1096             return;
1097         }
1098         serviceSet = null;
1099         if (legacyMap == null) {
1100             legacyMap = new ConcurrentHashMap<>();
1101         } else {
1102             legacyMap.clear();
1103         }
1104         for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
1105             parseLegacyPut(entry.getKey(), entry.getValue());
1106         }
1107         removeInvalidServices(legacyMap);
1108         legacyChanged = false;
1109     }
1110 
1111     /**
1112      * Remove all invalid services from the Map. Invalid services can only
1113      * occur if the legacy properties are inconsistent or incomplete.
1114      */
removeInvalidServices(Map<ServiceKey,Service> map)1115     private void removeInvalidServices(Map<ServiceKey,Service> map) {
1116         for (Iterator<Map.Entry<ServiceKey, Service>> t =
1117                 map.entrySet().iterator(); t.hasNext(); ) {
1118             Service s = t.next().getValue();
1119             if (s.isValid() == false) {
1120                 t.remove();
1121             }
1122         }
1123     }
1124 
getTypeAndAlgorithm(String key)1125     private static String[] getTypeAndAlgorithm(String key) {
1126         int i = key.indexOf('.');
1127         if (i < 1) {
1128             if (debug != null) {
1129                 debug.println("Ignoring invalid entry in provider: "
1130                         + key);
1131             }
1132             return null;
1133         }
1134         String type = key.substring(0, i);
1135         String alg = key.substring(i + 1);
1136         return new String[] {type, alg};
1137     }
1138 
1139     private static final String ALIAS_PREFIX = "Alg.Alias.";
1140     private static final String ALIAS_PREFIX_LOWER = "alg.alias.";
1141     private static final int ALIAS_LENGTH = ALIAS_PREFIX.length();
1142 
parseLegacyPut(String name, String value)1143     private void parseLegacyPut(String name, String value) {
1144         if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
1145             // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
1146             // aliasKey ~ MessageDigest.SHA
1147             String stdAlg = value;
1148             String aliasKey = name.substring(ALIAS_LENGTH);
1149             String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
1150             if (typeAndAlg == null) {
1151                 return;
1152             }
1153             String type = getEngineName(typeAndAlg[0]);
1154             String aliasAlg = typeAndAlg[1].intern();
1155             ServiceKey key = new ServiceKey(type, stdAlg, true);
1156             Service s = legacyMap.get(key);
1157             if (s == null) {
1158                 s = new Service(this, type, stdAlg);
1159                 legacyMap.put(key, s);
1160             }
1161             legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
1162             s.addAlias(aliasAlg);
1163         } else {
1164             String[] typeAndAlg = getTypeAndAlgorithm(name);
1165             if (typeAndAlg == null) {
1166                 return;
1167             }
1168             int i = typeAndAlg[1].indexOf(' ');
1169             if (i == -1) {
1170                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
1171                 String type = getEngineName(typeAndAlg[0]);
1172                 String stdAlg = typeAndAlg[1].intern();
1173                 String className = value;
1174                 ServiceKey key = new ServiceKey(type, stdAlg, true);
1175                 Service s = legacyMap.get(key);
1176                 if (s == null) {
1177                     s = new Service(this, type, stdAlg);
1178                     legacyMap.put(key, s);
1179                 }
1180                 s.className = className;
1181 
1182                 if (type.equals("SecureRandom")) {
1183                     updateSecureRandomEntries(true, s.algorithm);
1184                 }
1185             } else { // attribute
1186                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
1187                 String attributeValue = value;
1188                 String type = getEngineName(typeAndAlg[0]);
1189                 String attributeString = typeAndAlg[1];
1190                 String stdAlg = attributeString.substring(0, i).intern();
1191                 String attributeName = attributeString.substring(i + 1);
1192                 // kill additional spaces
1193                 while (attributeName.startsWith(" ")) {
1194                     attributeName = attributeName.substring(1);
1195                 }
1196                 attributeName = attributeName.intern();
1197                 ServiceKey key = new ServiceKey(type, stdAlg, true);
1198                 Service s = legacyMap.get(key);
1199                 if (s == null) {
1200                     s = new Service(this, type, stdAlg);
1201                     legacyMap.put(key, s);
1202                 }
1203                 s.addAttribute(attributeName, attributeValue);
1204             }
1205         }
1206     }
1207 
1208     /**
1209      * Get the service describing this Provider's implementation of the
1210      * specified type of this algorithm or alias. If no such
1211      * implementation exists, this method returns null. If there are two
1212      * matching services, one added to this provider using
1213      * {@link #putService putService()} and one added via {@link #put put()},
1214      * the service added via {@link #putService putService()} is returned.
1215      *
1216      * @param type the type of {@link Service service} requested
1217      * (for example, {@code MessageDigest})
1218      * @param algorithm the case insensitive algorithm name (or alternate
1219      * alias) of the service requested (for example, {@code SHA-1})
1220      *
1221      * @return the service describing this Provider's matching service
1222      * or null if no such service exists
1223      *
1224      * @throws NullPointerException if type or algorithm is null
1225      *
1226      * @since 1.5
1227      */
getService(String type, String algorithm)1228     public Service getService(String type, String algorithm) {
1229         checkInitialized();
1230 
1231         // avoid allocating a new ServiceKey object if possible
1232         ServiceKey key = previousKey;
1233         if (key.matches(type, algorithm) == false) {
1234             key = new ServiceKey(type, algorithm, false);
1235             previousKey = key;
1236         }
1237         if (!serviceMap.isEmpty()) {
1238             Service s = serviceMap.get(key);
1239             if (s != null) {
1240                 return s;
1241             }
1242         }
1243         synchronized (this) {
1244             ensureLegacyParsed();
1245             if (legacyMap != null && !legacyMap.isEmpty()) {
1246                 return legacyMap.get(key);
1247             }
1248         }
1249         return null;
1250     }
1251 
1252     // ServiceKey from previous getService() call
1253     // by re-using it if possible we avoid allocating a new object
1254     // and the toUpperCase() call.
1255     // re-use will occur e.g. as the framework traverses the provider
1256     // list and queries each provider with the same values until it finds
1257     // a matching service
1258     private static volatile ServiceKey previousKey =
1259                                             new ServiceKey("", "", false);
1260 
1261     /**
1262      * Get an unmodifiable Set of all services supported by
1263      * this Provider.
1264      *
1265      * @return an unmodifiable Set of all services supported by
1266      * this Provider
1267      *
1268      * @since 1.5
1269      */
getServices()1270     public synchronized Set<Service> getServices() {
1271         checkInitialized();
1272         if (legacyChanged || servicesChanged) {
1273             serviceSet = null;
1274         }
1275         if (serviceSet == null) {
1276             ensureLegacyParsed();
1277             Set<Service> set = new LinkedHashSet<>();
1278             if (!serviceMap.isEmpty()) {
1279                 set.addAll(serviceMap.values());
1280             }
1281             if (legacyMap != null && !legacyMap.isEmpty()) {
1282                 set.addAll(legacyMap.values());
1283             }
1284             serviceSet = Collections.unmodifiableSet(set);
1285             servicesChanged = false;
1286         }
1287         return serviceSet;
1288     }
1289 
1290     /**
1291      * Add a service. If a service of the same type with the same algorithm
1292      * name exists and it was added using {@link #putService putService()},
1293      * it is replaced by the new service.
1294      * This method also places information about this service
1295      * in the provider's Hashtable values in the format described in the
1296      * {@extLink security_guide_jca
1297      * Java Cryptography Architecture (JCA) Reference Guide}.
1298      *
1299      * <p>Also, if there is a security manager, its
1300      * {@code checkSecurityAccess} method is called with the string
1301      * {@code "putProviderProperty."+name}, where {@code name} is
1302      * the provider name, to see if it's ok to set this provider's property
1303      * values. If the default implementation of {@code checkSecurityAccess}
1304      * is used (that is, that method is not overridden), then this results in
1305      * a call to the security manager's {@code checkPermission} method with
1306      * a {@code SecurityPermission("putProviderProperty."+name)}
1307      * permission.
1308      *
1309      * @param s the Service to add
1310      *
1311      * @throws SecurityException
1312      *      if a security manager exists and its {@link
1313      *      java.lang.SecurityManager#checkSecurityAccess} method denies
1314      *      access to set property values.
1315      * @throws NullPointerException if s is null
1316      *
1317      * @since 1.5
1318      */
putService(Service s)1319     protected void putService(Service s) {
1320         check("putProviderProperty." + name);
1321         if (debug != null) {
1322             debug.println(name + ".putService(): " + s);
1323         }
1324         if (s == null) {
1325             throw new NullPointerException();
1326         }
1327         if (s.getProvider() != this) {
1328             throw new IllegalArgumentException
1329                     ("service.getProvider() must match this Provider object");
1330         }
1331         String type = s.getType();
1332         String algorithm = s.getAlgorithm();
1333         ServiceKey key = new ServiceKey(type, algorithm, true);
1334         implRemoveService(serviceMap.get(key));
1335         serviceMap.put(key, s);
1336         for (String alias : s.getAliases()) {
1337             serviceMap.put(new ServiceKey(type, alias, true), s);
1338         }
1339         servicesChanged = true;
1340         synchronized (this) {
1341             putPropertyStrings(s);
1342             if (type.equals("SecureRandom")) {
1343                 updateSecureRandomEntries(true, s.algorithm);
1344             }
1345         }
1346     }
1347 
1348     // keep tracks of the registered secure random algos and store them in order
updateSecureRandomEntries(boolean doAdd, String s)1349     private void updateSecureRandomEntries(boolean doAdd, String s) {
1350         Objects.requireNonNull(s);
1351         if (doAdd) {
1352             if (prngAlgos == null) {
1353                 prngAlgos = new LinkedHashSet<String>();
1354             }
1355             prngAlgos.add(s);
1356         } else {
1357             prngAlgos.remove(s);
1358         }
1359 
1360         if (debug != null) {
1361             debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " + s);
1362         }
1363     }
1364 
1365     // used by new SecureRandom() to find out the default SecureRandom
1366     // service for this provider
getDefaultSecureRandomService()1367     synchronized Service getDefaultSecureRandomService() {
1368         checkInitialized();
1369 
1370         if (legacyChanged) {
1371             prngAlgos = null;
1372             ensureLegacyParsed();
1373         }
1374 
1375         if (prngAlgos != null && !prngAlgos.isEmpty()) {
1376             // IMPORTANT: use the Service obj returned by getService(...) call
1377             // as providers may override putService(...)/getService(...) and
1378             // return their own Service objects
1379             return getService("SecureRandom", prngAlgos.iterator().next());
1380         }
1381 
1382         return null;
1383     }
1384 
1385     /**
1386      * Put the string properties for this Service in this Provider's
1387      * Hashtable.
1388      */
putPropertyStrings(Service s)1389     private void putPropertyStrings(Service s) {
1390         String type = s.getType();
1391         String algorithm = s.getAlgorithm();
1392         // use super() to avoid permission check and other processing
1393         super.put(type + "." + algorithm, s.getClassName());
1394         for (String alias : s.getAliases()) {
1395             super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
1396         }
1397         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
1398             String key = type + "." + algorithm + " " + entry.getKey();
1399             super.put(key, entry.getValue());
1400         }
1401     }
1402 
1403     /**
1404      * Remove the string properties for this Service from this Provider's
1405      * Hashtable.
1406      */
removePropertyStrings(Service s)1407     private void removePropertyStrings(Service s) {
1408         String type = s.getType();
1409         String algorithm = s.getAlgorithm();
1410         // use super() to avoid permission check and other processing
1411         super.remove(type + "." + algorithm);
1412         for (String alias : s.getAliases()) {
1413             super.remove(ALIAS_PREFIX + type + "." + alias);
1414         }
1415         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
1416             String key = type + "." + algorithm + " " + entry.getKey();
1417             super.remove(key);
1418         }
1419     }
1420 
1421     /**
1422      * Remove a service previously added using
1423      * {@link #putService putService()}. The specified service is removed from
1424      * this provider. It will no longer be returned by
1425      * {@link #getService getService()} and its information will be removed
1426      * from this provider's Hashtable.
1427      *
1428      * <p>Also, if there is a security manager, its
1429      * {@code checkSecurityAccess} method is called with the string
1430      * {@code "removeProviderProperty."+name}, where {@code name} is
1431      * the provider name, to see if it's ok to remove this provider's
1432      * properties. If the default implementation of
1433      * {@code checkSecurityAccess} is used (that is, that method is not
1434      * overridden), then this results in a call to the security manager's
1435      * {@code checkPermission} method with a
1436      * {@code SecurityPermission("removeProviderProperty."+name)}
1437      * permission.
1438      *
1439      * @param s the Service to be removed
1440      *
1441      * @throws  SecurityException
1442      *          if a security manager exists and its {@link
1443      *          java.lang.SecurityManager#checkSecurityAccess} method denies
1444      *          access to remove this provider's properties.
1445      * @throws NullPointerException if s is null
1446      *
1447      * @since 1.5
1448      */
removeService(Service s)1449     protected void removeService(Service s) {
1450         check("removeProviderProperty." + name);
1451         if (debug != null) {
1452             debug.println(name + ".removeService(): " + s);
1453         }
1454         if (s == null) {
1455             throw new NullPointerException();
1456         }
1457         implRemoveService(s);
1458     }
1459 
implRemoveService(Service s)1460     private void implRemoveService(Service s) {
1461         if ((s == null) || serviceMap.isEmpty()) {
1462             return;
1463         }
1464         String type = s.getType();
1465         String algorithm = s.getAlgorithm();
1466         ServiceKey key = new ServiceKey(type, algorithm, false);
1467         Service oldService = serviceMap.get(key);
1468         if (s != oldService) {
1469             return;
1470         }
1471         servicesChanged = true;
1472         serviceMap.remove(key);
1473         for (String alias : s.getAliases()) {
1474             serviceMap.remove(new ServiceKey(type, alias, false));
1475         }
1476         synchronized (this) {
1477             removePropertyStrings(s);
1478             if (type.equals("SecureRandom")) {
1479                 updateSecureRandomEntries(false, s.algorithm);
1480             }
1481         }
1482     }
1483 
1484     // Wrapped String that behaves in a case insensitive way for equals/hashCode
1485     private static class UString {
1486         final String string;
1487         final String lowerString;
1488 
UString(String s)1489         UString(String s) {
1490             this.string = s;
1491             this.lowerString = s.toLowerCase(ENGLISH);
1492         }
1493 
hashCode()1494         public int hashCode() {
1495             return lowerString.hashCode();
1496         }
1497 
equals(Object obj)1498         public boolean equals(Object obj) {
1499             if (this == obj) {
1500                 return true;
1501             }
1502             return obj instanceof UString other
1503                     && lowerString.equals(other.lowerString);
1504         }
1505 
toString()1506         public String toString() {
1507             return string;
1508         }
1509     }
1510 
1511     // describe relevant properties of a type of engine
1512     private static class EngineDescription {
1513         final String name;
1514         final boolean supportsParameter;
1515         final String constructorParameterClassName;
1516         private volatile Class<?> constructorParameterClass;
1517 
EngineDescription(String name, boolean sp, String paramName)1518         EngineDescription(String name, boolean sp, String paramName) {
1519             this.name = name;
1520             this.supportsParameter = sp;
1521             this.constructorParameterClassName = paramName;
1522         }
getConstructorParameterClass()1523         Class<?> getConstructorParameterClass() throws ClassNotFoundException {
1524             Class<?> clazz = constructorParameterClass;
1525             if (clazz == null) {
1526                 clazz = Class.forName(constructorParameterClassName);
1527                 constructorParameterClass = clazz;
1528             }
1529             return clazz;
1530         }
1531     }
1532 
1533     // built in knowledge of the engine types shipped as part of the JDK
1534     private static final Map<String,EngineDescription> knownEngines;
1535 
addEngine(String name, boolean sp, String paramName)1536     private static void addEngine(String name, boolean sp, String paramName) {
1537         EngineDescription ed = new EngineDescription(name, sp, paramName);
1538         // also index by canonical name to avoid toLowerCase() for some lookups
1539         knownEngines.put(name.toLowerCase(ENGLISH), ed);
1540         knownEngines.put(name, ed);
1541     }
1542 
1543     static {
1544         knownEngines = new HashMap<>();
1545         // JCA
1546         addEngine("AlgorithmParameterGenerator",        false, null);
1547         addEngine("AlgorithmParameters",                false, null);
1548         addEngine("KeyFactory",                         false, null);
1549         addEngine("KeyPairGenerator",                   false, null);
1550         addEngine("KeyStore",                           false, null);
1551         addEngine("MessageDigest",                      false, null);
1552         addEngine("SecureRandom",                       false,
1553                 "java.security.SecureRandomParameters");
1554         addEngine("Signature",                          true,  null);
1555         addEngine("CertificateFactory",                 false, null);
1556         addEngine("CertPathBuilder",                    false, null);
1557         addEngine("CertPathValidator",                  false, null);
1558         addEngine("CertStore",                          false,
1559                             "java.security.cert.CertStoreParameters");
1560         // JCE
1561         addEngine("Cipher",                             true,  null);
1562         addEngine("ExemptionMechanism",                 false, null);
1563         addEngine("Mac",                                true,  null);
1564         addEngine("KeyAgreement",                       true,  null);
1565         addEngine("KeyGenerator",                       false, null);
1566         addEngine("SecretKeyFactory",                   false, null);
1567         // JSSE
1568         addEngine("KeyManagerFactory",                  false, null);
1569         addEngine("SSLContext",                         false, null);
1570         addEngine("TrustManagerFactory",                false, null);
1571         // JGSS
1572         addEngine("GssApiMechanism",                    false, null);
1573         // SASL
1574         addEngine("SaslClientFactory",                  false, null);
1575         addEngine("SaslServerFactory",                  false, null);
1576         // POLICY
1577         addEngine("Policy",                             false,
1578                             "java.security.Policy$Parameters");
1579         // CONFIGURATION
1580         addEngine("Configuration",                      false,
1581                             "javax.security.auth.login.Configuration$Parameters");
1582         // XML DSig
1583         addEngine("XMLSignatureFactory",                false, null);
1584         addEngine("KeyInfoFactory",                     false, null);
1585         addEngine("TransformService",                   false, null);
1586         // Smart Card I/O
1587         addEngine("TerminalFactory",                    false,
1588                             "java.lang.Object");
1589     }
1590 
1591     // get the "standard" (mixed-case) engine name for arbitary case engine name
1592     // if there is no known engine by that name, return s
getEngineName(String s)1593     private static String getEngineName(String s) {
1594         // try original case first, usually correct
1595         EngineDescription e = knownEngines.get(s);
1596         if (e == null) {
1597             e = knownEngines.get(s.toLowerCase(ENGLISH));
1598         }
1599         return (e == null) ? s : e.name;
1600     }
1601 
1602     /**
1603      * The description of a security service. It encapsulates the properties
1604      * of a service and contains a factory method to obtain new implementation
1605      * instances of this service.
1606      *
1607      * <p>Each service has a provider that offers the service, a type,
1608      * an algorithm name, and the name of the class that implements the
1609      * service. Optionally, it also includes a list of alternate algorithm
1610      * names for this service (aliases) and attributes, which are a map of
1611      * (name, value) String pairs.
1612      *
1613      * <p>This class defines the methods {@link #supportsParameter
1614      * supportsParameter()} and {@link #newInstance newInstance()}
1615      * which are used by the Java security framework when it searches for
1616      * suitable services and instantiates them. The valid arguments to those
1617      * methods depend on the type of service. For the service types defined
1618      * within Java SE, see the
1619      * {@extLink security_guide_jca
1620      * Java Cryptography Architecture (JCA) Reference Guide}
1621      * for the valid values.
1622      * Note that components outside of Java SE can define additional types of
1623      * services and their behavior.
1624      *
1625      * <p>Instances of this class are immutable.
1626      *
1627      * @since 1.5
1628      */
1629     public static class Service {
1630         private final String type;
1631         private final String algorithm;
1632         private String className;
1633         private final Provider provider;
1634         private List<String> aliases;
1635         private Map<UString,String> attributes;
1636         private final EngineDescription engineDescription;
1637 
1638         // Reference to the cached implementation Class object.
1639         // Will be a Class if this service is loaded from the built-in
1640         // classloader (unloading not possible), otherwise a WeakReference to a
1641         // Class
1642         private Object classCache;
1643 
1644         // Will be a Constructor if this service is loaded from the built-in
1645         // classloader (unloading not possible), otherwise a WeakReference to
1646         // a Constructor
1647         private Object constructorCache;
1648 
1649         // flag indicating whether this service has its attributes for
1650         // supportedKeyFormats or supportedKeyClasses set
1651         // if null, the values have not been initialized
1652         // if TRUE, at least one of supportedFormats/Classes is non null
1653         private volatile Boolean hasKeyAttributes;
1654 
1655         // supported encoding formats
1656         private String[] supportedFormats;
1657 
1658         // names of the supported key (super) classes
1659         private Class<?>[] supportedClasses;
1660 
1661         // whether this service has been registered with the Provider
1662         private boolean registered;
1663 
1664         private static final Class<?>[] CLASS0 = new Class<?>[0];
1665 
1666         // this constructor and these methods are used for parsing
1667         // the legacy string properties.
1668 
Service(Provider provider, String type, String algorithm)1669         private Service(Provider provider, String type, String algorithm) {
1670             this.provider = provider;
1671             this.type = type;
1672             this.algorithm = algorithm;
1673             engineDescription = knownEngines.get(type);
1674             aliases = Collections.<String>emptyList();
1675             attributes = Collections.<UString,String>emptyMap();
1676         }
1677 
isValid()1678         private boolean isValid() {
1679             return (type != null) && (algorithm != null) && (className != null);
1680         }
1681 
addAlias(String alias)1682         private void addAlias(String alias) {
1683             if (aliases.isEmpty()) {
1684                 aliases = new ArrayList<>(2);
1685             }
1686             aliases.add(alias);
1687         }
1688 
addAttribute(String type, String value)1689         void addAttribute(String type, String value) {
1690             if (attributes.isEmpty()) {
1691                 attributes = new HashMap<>(8);
1692             }
1693             attributes.put(new UString(type), value);
1694         }
1695 
1696         /**
1697          * Construct a new service.
1698          *
1699          * @param provider the provider that offers this service
1700          * @param type the type of this service
1701          * @param algorithm the algorithm name
1702          * @param className the name of the class implementing this service
1703          * @param aliases List of aliases or null if algorithm has no aliases
1704          * @param attributes Map of attributes or null if this implementation
1705          *                   has no attributes
1706          *
1707          * @throws NullPointerException if provider, type, algorithm, or
1708          * className is null
1709          */
Service(Provider provider, String type, String algorithm, String className, List<String> aliases, Map<String,String> attributes)1710         public Service(Provider provider, String type, String algorithm,
1711                 String className, List<String> aliases,
1712                 Map<String,String> attributes) {
1713             if ((provider == null) || (type == null) ||
1714                     (algorithm == null) || (className == null)) {
1715                 throw new NullPointerException();
1716             }
1717             this.provider = provider;
1718             this.type = getEngineName(type);
1719             engineDescription = knownEngines.get(type);
1720             this.algorithm = algorithm;
1721             this.className = className;
1722             if (aliases == null) {
1723                 this.aliases = Collections.<String>emptyList();
1724             } else {
1725                 this.aliases = new ArrayList<>(aliases);
1726             }
1727             if (attributes == null) {
1728                 this.attributes = Collections.<UString,String>emptyMap();
1729             } else {
1730                 this.attributes = new HashMap<>();
1731                 for (Map.Entry<String,String> entry : attributes.entrySet()) {
1732                     this.attributes.put(new UString(entry.getKey()), entry.getValue());
1733                 }
1734             }
1735         }
1736 
1737         /**
1738          * Get the type of this service. For example, {@code MessageDigest}.
1739          *
1740          * @return the type of this service
1741          */
getType()1742         public final String getType() {
1743             return type;
1744         }
1745 
1746         /**
1747          * Return the name of the algorithm of this service. For example,
1748          * {@code SHA-1}.
1749          *
1750          * @return the algorithm of this service
1751          */
getAlgorithm()1752         public final String getAlgorithm() {
1753             return algorithm;
1754         }
1755 
1756         /**
1757          * Return the Provider of this service.
1758          *
1759          * @return the Provider of this service
1760          */
getProvider()1761         public final Provider getProvider() {
1762             return provider;
1763         }
1764 
1765         /**
1766          * Return the name of the class implementing this service.
1767          *
1768          * @return the name of the class implementing this service
1769          */
getClassName()1770         public final String getClassName() {
1771             return className;
1772         }
1773 
1774         // internal only
getAliases()1775         private final List<String> getAliases() {
1776             return aliases;
1777         }
1778 
1779         /**
1780          * Return the value of the specified attribute or null if this
1781          * attribute is not set for this Service.
1782          *
1783          * @param name the name of the requested attribute
1784          *
1785          * @return the value of the specified attribute or null if the
1786          *         attribute is not present
1787          *
1788          * @throws NullPointerException if name is null
1789          */
getAttribute(String name)1790         public final String getAttribute(String name) {
1791             if (name == null) {
1792                 throw new NullPointerException();
1793             }
1794             return attributes.get(new UString(name));
1795         }
1796 
1797         /**
1798          * Return a new instance of the implementation described by this
1799          * service. The security provider framework uses this method to
1800          * construct implementations. Applications will typically not need
1801          * to call it.
1802          *
1803          * <p>The default implementation uses reflection to invoke the
1804          * standard constructor for this type of service.
1805          * Security providers can override this method to implement
1806          * instantiation in a different way.
1807          * For details and the values of constructorParameter that are
1808          * valid for the various types of services see the
1809          * {@extLink security_guide_jca
1810          * Java Cryptography Architecture (JCA) Reference Guide}.
1811          *
1812          * @param constructorParameter the value to pass to the constructor,
1813          * or null if this type of service does not use a constructorParameter.
1814          *
1815          * @return a new implementation of this service
1816          *
1817          * @throws InvalidParameterException if the value of
1818          * constructorParameter is invalid for this type of service.
1819          * @throws NoSuchAlgorithmException if instantiation failed for
1820          * any other reason.
1821          */
newInstance(Object constructorParameter)1822         public Object newInstance(Object constructorParameter)
1823                 throws NoSuchAlgorithmException {
1824             if (registered == false) {
1825                 if (provider.getService(type, algorithm) != this) {
1826                     throw new NoSuchAlgorithmException
1827                         ("Service not registered with Provider "
1828                         + provider.getName() + ": " + this);
1829                 }
1830                 registered = true;
1831             }
1832             Class<?> ctrParamClz;
1833             try {
1834                 EngineDescription cap = engineDescription;
1835                 if (cap == null) {
1836                     // unknown engine type, use generic code
1837                     // this is the code path future for non-core
1838                     // optional packages
1839                     ctrParamClz = constructorParameter == null?
1840                         null : constructorParameter.getClass();
1841                 } else {
1842                     ctrParamClz = cap.constructorParameterClassName == null?
1843                         null : Class.forName(cap.constructorParameterClassName);
1844                     if (constructorParameter != null) {
1845                         if (ctrParamClz == null) {
1846                             throw new InvalidParameterException
1847                                 ("constructorParameter not used with " + type
1848                                 + " engines");
1849                         } else {
1850                             Class<?> argClass = constructorParameter.getClass();
1851                             if (ctrParamClz.isAssignableFrom(argClass) == false) {
1852                                 throw new InvalidParameterException
1853                                     ("constructorParameter must be instanceof "
1854                                     + cap.constructorParameterClassName.replace('$', '.')
1855                                     + " for engine type " + type);
1856                             }
1857                         }
1858                     }
1859                 }
1860                 // constructorParameter can be null if not provided
1861                 return newInstanceUtil(ctrParamClz, constructorParameter);
1862             } catch (NoSuchAlgorithmException e) {
1863                 throw e;
1864             } catch (InvocationTargetException e) {
1865                 throw new NoSuchAlgorithmException
1866                     ("Error constructing implementation (algorithm: "
1867                     + algorithm + ", provider: " + provider.getName()
1868                     + ", class: " + className + ")", e.getCause());
1869             } catch (Exception e) {
1870                 throw new NoSuchAlgorithmException
1871                     ("Error constructing implementation (algorithm: "
1872                     + algorithm + ", provider: " + provider.getName()
1873                     + ", class: " + className + ")", e);
1874             }
1875         }
1876 
newInstanceOf()1877         private Object newInstanceOf() throws Exception {
1878             Constructor<?> con = getDefaultConstructor();
1879             return con.newInstance(EMPTY);
1880         }
1881 
newInstanceUtil(Class<?> ctrParamClz, Object ctorParamObj)1882         private Object newInstanceUtil(Class<?> ctrParamClz, Object ctorParamObj)
1883                 throws Exception
1884         {
1885             if (ctrParamClz == null) {
1886                 return newInstanceOf();
1887             } else {
1888                 // Looking for the constructor with a params first and fallback
1889                 // to one without if not found. This is to support the enhanced
1890                 // SecureRandom where both styles of constructors are supported.
1891                 // Before jdk9, there was no params support (only getInstance(alg))
1892                 // and an impl only had the params-less constructor. Since jdk9,
1893                 // there is getInstance(alg,params) and an impl can contain
1894                 // an Impl(params) constructor.
1895                 try {
1896                     Constructor<?> con = getImplClass().getConstructor(ctrParamClz);
1897                     return con.newInstance(ctorParamObj);
1898                 } catch (NoSuchMethodException nsme) {
1899                     // For pre-jdk9 SecureRandom implementations, they only
1900                     // have params-less constructors which still works when
1901                     // the input ctorParamObj is null.
1902                     //
1903                     // For other primitives using params, ctorParamObj should not
1904                     // be null and nsme is thrown, just like before.
1905                     if (ctorParamObj == null) {
1906                         try {
1907                             return newInstanceOf();
1908                         } catch (NoSuchMethodException nsme2) {
1909                             nsme.addSuppressed(nsme2);
1910                             throw nsme;
1911                         }
1912                     } else {
1913                         throw nsme;
1914                     }
1915                 }
1916             }
1917         }
1918 
1919         // return the implementation Class object for this service
getImplClass()1920         private Class<?> getImplClass() throws NoSuchAlgorithmException {
1921             try {
1922                 Object cache = classCache;
1923                 if (cache instanceof Class<?> clazz) {
1924                     return clazz;
1925                 }
1926                 Class<?> clazz = null;
1927                 if (cache instanceof WeakReference<?> ref){
1928                     clazz = (Class<?>)ref.get();
1929                 }
1930                 if (clazz == null) {
1931                     ClassLoader cl = provider.getClass().getClassLoader();
1932                     if (cl == null) {
1933                         clazz = Class.forName(className);
1934                     } else {
1935                         clazz = cl.loadClass(className);
1936                     }
1937                     if (!Modifier.isPublic(clazz.getModifiers())) {
1938                         throw new NoSuchAlgorithmException
1939                             ("class configured for " + type + " (provider: " +
1940                             provider.getName() + ") is not public.");
1941                     }
1942                     classCache = (cl == null) ? clazz : new WeakReference<Class<?>>(clazz);
1943                 }
1944                 return clazz;
1945             } catch (ClassNotFoundException e) {
1946                 throw new NoSuchAlgorithmException
1947                     ("class configured for " + type + " (provider: " +
1948                     provider.getName() + ") cannot be found.", e);
1949             }
1950         }
1951 
getDefaultConstructor()1952         private Constructor<?> getDefaultConstructor()
1953             throws NoSuchAlgorithmException, NoSuchMethodException
1954         {
1955             Object cache = constructorCache;
1956             if (cache instanceof Constructor<?> con) {
1957                 return con;
1958             }
1959             Constructor<?> con = null;
1960             if (cache instanceof WeakReference<?> ref){
1961                 con = (Constructor<?>)ref.get();
1962             }
1963             if (con == null) {
1964                 Class<?> clazz = getImplClass();
1965                 con = clazz.getConstructor();
1966                 constructorCache = (clazz.getClassLoader() == null)
1967                         ? con : new WeakReference<Constructor<?>>(con);
1968             }
1969             return con;
1970         }
1971 
1972         /**
1973          * Test whether this Service can use the specified parameter.
1974          * Returns false if this service cannot use the parameter. Returns
1975          * true if this service can use the parameter, if a fast test is
1976          * infeasible, or if the status is unknown.
1977          *
1978          * <p>The security provider framework uses this method with
1979          * some types of services to quickly exclude non-matching
1980          * implementations for consideration.
1981          * Applications will typically not need to call it.
1982          *
1983          * <p>For details and the values of parameter that are valid for the
1984          * various types of services see the top of this class and the
1985          * {@extLink security_guide_jca
1986          * Java Cryptography Architecture (JCA) Reference Guide}.
1987          * Security providers can override it to implement their own test.
1988          *
1989          * @param parameter the parameter to test
1990          *
1991          * @return false if this service cannot use the specified
1992          * parameter; true if it can possibly use the parameter
1993          *
1994          * @throws InvalidParameterException if the value of parameter is
1995          * invalid for this type of service or if this method cannot be
1996          * used with this type of service
1997          */
supportsParameter(Object parameter)1998         public boolean supportsParameter(Object parameter) {
1999             EngineDescription cap = engineDescription;
2000             if (cap == null) {
2001                 // unknown engine type, return true by default
2002                 return true;
2003             }
2004             if (!cap.supportsParameter) {
2005                 throw new InvalidParameterException("supportsParameter() not "
2006                     + "used with " + type + " engines");
2007             }
2008             // allow null for keys without attributes for compatibility
2009             if ((parameter != null) && (!(parameter instanceof Key))) {
2010                 throw new InvalidParameterException
2011                     ("Parameter must be instanceof Key for engine " + type);
2012             }
2013             if (!hasKeyAttributes()) {
2014                 return true;
2015             }
2016             if (parameter == null) {
2017                 return false;
2018             }
2019             Key key = (Key)parameter;
2020             if (supportsKeyFormat(key)) {
2021                 return true;
2022             }
2023             if (supportsKeyClass(key)) {
2024                 return true;
2025             }
2026             return false;
2027         }
2028 
2029         /**
2030          * Return whether this service has its supported properties for
2031          * keys defined. Parses the attributes if not yet initialized.
2032          */
hasKeyAttributes()2033         private boolean hasKeyAttributes() {
2034             Boolean b = hasKeyAttributes;
2035             if (b == null) {
2036                 synchronized (this) {
2037                     b = hasKeyAttributes;
2038                     if (b == null) {
2039                         String s;
2040                         s = getAttribute("SupportedKeyFormats");
2041                         if (s != null) {
2042                             supportedFormats = s.split("\\|");
2043                         }
2044                         s = getAttribute("SupportedKeyClasses");
2045                         if (s != null) {
2046                             String[] classNames = s.split("\\|");
2047                             List<Class<?>> classList =
2048                                 new ArrayList<>(classNames.length);
2049                             for (String className : classNames) {
2050                                 Class<?> clazz = getKeyClass(className);
2051                                 if (clazz != null) {
2052                                     classList.add(clazz);
2053                                 }
2054                             }
2055                             supportedClasses = classList.toArray(CLASS0);
2056                         }
2057                         boolean bool = (supportedFormats != null)
2058                             || (supportedClasses != null);
2059                         b = Boolean.valueOf(bool);
2060                         hasKeyAttributes = b;
2061                     }
2062                 }
2063             }
2064             return b.booleanValue();
2065         }
2066 
2067         // get the key class object of the specified name
getKeyClass(String name)2068         private Class<?> getKeyClass(String name) {
2069             try {
2070                 return Class.forName(name);
2071             } catch (ClassNotFoundException e) {
2072                 // ignore
2073             }
2074             try {
2075                 ClassLoader cl = provider.getClass().getClassLoader();
2076                 if (cl != null) {
2077                     return cl.loadClass(name);
2078                 }
2079             } catch (ClassNotFoundException e) {
2080                 // ignore
2081             }
2082             return null;
2083         }
2084 
supportsKeyFormat(Key key)2085         private boolean supportsKeyFormat(Key key) {
2086             if (supportedFormats == null) {
2087                 return false;
2088             }
2089             String format = key.getFormat();
2090             if (format == null) {
2091                 return false;
2092             }
2093             for (String supportedFormat : supportedFormats) {
2094                 if (supportedFormat.equals(format)) {
2095                     return true;
2096                 }
2097             }
2098             return false;
2099         }
2100 
supportsKeyClass(Key key)2101         private boolean supportsKeyClass(Key key) {
2102             if (supportedClasses == null) {
2103                 return false;
2104             }
2105             Class<?> keyClass = key.getClass();
2106             for (Class<?> clazz : supportedClasses) {
2107                 if (clazz.isAssignableFrom(keyClass)) {
2108                     return true;
2109                 }
2110             }
2111             return false;
2112         }
2113 
2114         /**
2115          * Return a String representation of this service.
2116          *
2117          * @return a String representation of this service.
2118          */
toString()2119         public String toString() {
2120             String aString = aliases.isEmpty()
2121                 ? "" : "\r\n  aliases: " + aliases.toString();
2122             String attrs = attributes.isEmpty()
2123                 ? "" : "\r\n  attributes: " + attributes.toString();
2124             return provider.getName() + ": " + type + "." + algorithm
2125                 + " -> " + className + aString + attrs + "\r\n";
2126         }
2127     }
2128 }
2129