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