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.util.*;
29import java.util.concurrent.ConcurrentHashMap;
30import java.io.*;
31import java.net.URL;
32
33import jdk.internal.misc.SharedSecrets;
34import sun.security.util.Debug;
35import sun.security.util.PropertyExpander;
36
37import sun.security.jca.*;
38
39/**
40 * <p>This class centralizes all security properties and common security
41 * methods. One of its primary uses is to manage providers.
42 *
43 * <p>The default values of security properties are read from an
44 * implementation-specific location, which is typically the properties file
45 * {@code conf/security/java.security} in the Java installation directory.
46 *
47 * @author Benjamin Renaud
48 * @since 1.1
49 */
50
51public final class Security {
52
53    /* Are we debugging? -- for developers */
54    private static final Debug sdebug =
55                        Debug.getInstance("properties");
56
57    /* The java.security properties */
58    private static Properties props;
59
60    // An element in the cache
61    private static class ProviderProperty {
62        String className;
63        Provider provider;
64    }
65
66    static {
67        // doPrivileged here because there are multiple
68        // things in initialize that might require privs.
69        // (the FileInputStream call and the File.exists call,
70        // the securityPropFile call, etc)
71        AccessController.doPrivileged(new PrivilegedAction<>() {
72            public Void run() {
73                initialize();
74                return null;
75            }
76        });
77    }
78
79    private static void initialize() {
80        props = new Properties();
81        boolean loadedProps = false;
82        boolean overrideAll = false;
83
84        // first load the system properties file
85        // to determine the value of security.overridePropertiesFile
86        File propFile = securityPropFile("java.security");
87        if (propFile.exists()) {
88            InputStream is = null;
89            try {
90                FileInputStream fis = new FileInputStream(propFile);
91                is = new BufferedInputStream(fis);
92                props.load(is);
93                loadedProps = true;
94
95                if (sdebug != null) {
96                    sdebug.println("reading security properties file: " +
97                                propFile);
98                }
99            } catch (IOException e) {
100                if (sdebug != null) {
101                    sdebug.println("unable to load security properties from " +
102                                propFile);
103                    e.printStackTrace();
104                }
105            } finally {
106                if (is != null) {
107                    try {
108                        is.close();
109                    } catch (IOException ioe) {
110                        if (sdebug != null) {
111                            sdebug.println("unable to close input stream");
112                        }
113                    }
114                }
115            }
116        }
117
118        if ("true".equalsIgnoreCase(props.getProperty
119                ("security.overridePropertiesFile"))) {
120
121            String extraPropFile = System.getProperty
122                                        ("java.security.properties");
123            if (extraPropFile != null && extraPropFile.startsWith("=")) {
124                overrideAll = true;
125                extraPropFile = extraPropFile.substring(1);
126            }
127
128            if (overrideAll) {
129                props = new Properties();
130                if (sdebug != null) {
131                    sdebug.println
132                        ("overriding other security properties files!");
133                }
134            }
135
136            // now load the user-specified file so its values
137            // will win if they conflict with the earlier values
138            if (extraPropFile != null) {
139                BufferedInputStream bis = null;
140                try {
141                    URL propURL;
142
143                    extraPropFile = PropertyExpander.expand(extraPropFile);
144                    propFile = new File(extraPropFile);
145                    if (propFile.exists()) {
146                        propURL = new URL
147                                ("file:" + propFile.getCanonicalPath());
148                    } else {
149                        propURL = new URL(extraPropFile);
150                    }
151                    bis = new BufferedInputStream(propURL.openStream());
152                    props.load(bis);
153                    loadedProps = true;
154
155                    if (sdebug != null) {
156                        sdebug.println("reading security properties file: " +
157                                        propURL);
158                        if (overrideAll) {
159                            sdebug.println
160                                ("overriding other security properties files!");
161                        }
162                    }
163                } catch (Exception e) {
164                    if (sdebug != null) {
165                        sdebug.println
166                                ("unable to load security properties from " +
167                                extraPropFile);
168                        e.printStackTrace();
169                    }
170                } finally {
171                    if (bis != null) {
172                        try {
173                            bis.close();
174                        } catch (IOException ioe) {
175                            if (sdebug != null) {
176                                sdebug.println("unable to close input stream");
177                            }
178                        }
179                    }
180                }
181            }
182        }
183
184        if (!loadedProps) {
185            initializeStatic();
186            if (sdebug != null) {
187                sdebug.println("unable to load security properties " +
188                        "-- using defaults");
189            }
190        }
191
192    }
193
194    /*
195     * Initialize to default values, if <java.home>/lib/java.security
196     * is not found.
197     */
198    private static void initializeStatic() {
199        props.put("security.provider.1", "sun.security.provider.Sun");
200        props.put("security.provider.2", "sun.security.rsa.SunRsaSign");
201        props.put("security.provider.3", "com.sun.net.ssl.internal.ssl.Provider");
202        props.put("security.provider.4", "com.sun.crypto.provider.SunJCE");
203        props.put("security.provider.5", "sun.security.jgss.SunProvider");
204        props.put("security.provider.6", "com.sun.security.sasl.Provider");
205    }
206
207    /**
208     * Don't let anyone instantiate this.
209     */
210    private Security() {
211    }
212
213    private static File securityPropFile(String filename) {
214        // maybe check for a system property which will specify where to
215        // look. Someday.
216        String sep = File.separator;
217        return new File(System.getProperty("java.home") + sep + "conf" + sep +
218                        "security" + sep + filename);
219    }
220
221    /**
222     * Looks up providers, and returns the property (and its associated
223     * provider) mapping the key, if any.
224     * The order in which the providers are looked up is the
225     * provider-preference order, as specificed in the security
226     * properties file.
227     */
228    private static ProviderProperty getProviderProperty(String key) {
229        ProviderProperty entry = null;
230
231        List<Provider> providers = Providers.getProviderList().providers();
232        for (int i = 0; i < providers.size(); i++) {
233
234            String matchKey = null;
235            Provider prov = providers.get(i);
236            String prop = prov.getProperty(key);
237
238            if (prop == null) {
239                // Is there a match if we do a case-insensitive property name
240                // comparison? Let's try ...
241                for (Enumeration<Object> e = prov.keys();
242                                e.hasMoreElements() && prop == null; ) {
243                    matchKey = (String)e.nextElement();
244                    if (key.equalsIgnoreCase(matchKey)) {
245                        prop = prov.getProperty(matchKey);
246                        break;
247                    }
248                }
249            }
250
251            if (prop != null) {
252                ProviderProperty newEntry = new ProviderProperty();
253                newEntry.className = prop;
254                newEntry.provider = prov;
255                return newEntry;
256            }
257        }
258
259        return entry;
260    }
261
262    /**
263     * Returns the property (if any) mapping the key for the given provider.
264     */
265    private static String getProviderProperty(String key, Provider provider) {
266        String prop = provider.getProperty(key);
267        if (prop == null) {
268            // Is there a match if we do a case-insensitive property name
269            // comparison? Let's try ...
270            for (Enumeration<Object> e = provider.keys();
271                                e.hasMoreElements() && prop == null; ) {
272                String matchKey = (String)e.nextElement();
273                if (key.equalsIgnoreCase(matchKey)) {
274                    prop = provider.getProperty(matchKey);
275                    break;
276                }
277            }
278        }
279        return prop;
280    }
281
282    /**
283     * Gets a specified property for an algorithm. The algorithm name
284     * should be a standard name. See the <a href=
285     * "{@docRoot}/../specs/security/standard-names.html">
286     * Java Security Standard Algorithm Names Specification</a>
287     * for information about standard algorithm names.
288     *
289     * One possible use is by specialized algorithm parsers, which may map
290     * classes to algorithms which they understand (much like Key parsers
291     * do).
292     *
293     * @param algName the algorithm name.
294     *
295     * @param propName the name of the property to get.
296     *
297     * @return the value of the specified property.
298     *
299     * @deprecated This method used to return the value of a proprietary
300     * property in the master file of the "SUN" Cryptographic Service
301     * Provider in order to determine how to parse algorithm-specific
302     * parameters. Use the new provider-based and algorithm-independent
303     * {@code AlgorithmParameters} and {@code KeyFactory} engine
304     * classes (introduced in the J2SE version 1.2 platform) instead.
305     */
306    @Deprecated
307    public static String getAlgorithmProperty(String algName,
308                                              String propName) {
309        ProviderProperty entry = getProviderProperty("Alg." + propName
310                                                     + "." + algName);
311        if (entry != null) {
312            return entry.className;
313        } else {
314            return null;
315        }
316    }
317
318    /**
319     * Adds a new provider, at a specified position. The position is
320     * the preference order in which providers are searched for
321     * requested algorithms.  The position is 1-based, that is,
322     * 1 is most preferred, followed by 2, and so on.
323     *
324     * <p>If the given provider is installed at the requested position,
325     * the provider that used to be at that position, and all providers
326     * with a position greater than {@code position}, are shifted up
327     * one position (towards the end of the list of installed providers).
328     *
329     * <p>A provider cannot be added if it is already installed.
330     *
331     * <p>If there is a security manager, the
332     * {@link java.lang.SecurityManager#checkSecurityAccess} method is called
333     * with the {@code "insertProvider"} permission target name to see if
334     * it's ok to add a new provider. If this permission check is denied,
335     * {@code checkSecurityAccess} is called again with the
336     * {@code "insertProvider."+provider.getName()} permission target name. If
337     * both checks are denied, a {@code SecurityException} is thrown.
338     *
339     * @param provider the provider to be added.
340     *
341     * @param position the preference position that the caller would
342     * like for this provider.
343     *
344     * @return the actual preference position in which the provider was
345     * added, or -1 if the provider was not added because it is
346     * already installed.
347     *
348     * @throws  NullPointerException if provider is null
349     * @throws  SecurityException
350     *          if a security manager exists and its {@link
351     *          java.lang.SecurityManager#checkSecurityAccess} method
352     *          denies access to add a new provider
353     *
354     * @see #getProvider
355     * @see #removeProvider
356     * @see java.security.SecurityPermission
357     */
358    public static synchronized int insertProviderAt(Provider provider,
359            int position) {
360        String providerName = provider.getName();
361        checkInsertProvider(providerName);
362        ProviderList list = Providers.getFullProviderList();
363        ProviderList newList = ProviderList.insertAt(list, provider, position - 1);
364        if (list == newList) {
365            return -1;
366        }
367        Providers.setProviderList(newList);
368        return newList.getIndex(providerName) + 1;
369    }
370
371    /**
372     * Adds a provider to the next position available.
373     *
374     * <p>If there is a security manager, the
375     * {@link java.lang.SecurityManager#checkSecurityAccess} method is called
376     * with the {@code "insertProvider"} permission target name to see if
377     * it's ok to add a new provider. If this permission check is denied,
378     * {@code checkSecurityAccess} is called again with the
379     * {@code "insertProvider."+provider.getName()} permission target name. If
380     * both checks are denied, a {@code SecurityException} is thrown.
381     *
382     * @param provider the provider to be added.
383     *
384     * @return the preference position in which the provider was
385     * added, or -1 if the provider was not added because it is
386     * already installed.
387     *
388     * @throws  NullPointerException if provider is null
389     * @throws  SecurityException
390     *          if a security manager exists and its {@link
391     *          java.lang.SecurityManager#checkSecurityAccess} method
392     *          denies access to add a new provider
393     *
394     * @see #getProvider
395     * @see #removeProvider
396     * @see java.security.SecurityPermission
397     */
398    public static int addProvider(Provider provider) {
399        /*
400         * We can't assign a position here because the statically
401         * registered providers may not have been installed yet.
402         * insertProviderAt() will fix that value after it has
403         * loaded the static providers.
404         */
405        return insertProviderAt(provider, 0);
406    }
407
408    /**
409     * Removes the provider with the specified name.
410     *
411     * <p>When the specified provider is removed, all providers located
412     * at a position greater than where the specified provider was are shifted
413     * down one position (towards the head of the list of installed
414     * providers).
415     *
416     * <p>This method returns silently if the provider is not installed or
417     * if name is null.
418     *
419     * <p>First, if there is a security manager, its
420     * {@code checkSecurityAccess}
421     * method is called with the string {@code "removeProvider."+name}
422     * to see if it's ok to remove the provider.
423     * If the default implementation of {@code checkSecurityAccess}
424     * is used (i.e., that method is not overriden), then this will result in
425     * a call to the security manager's {@code checkPermission} method
426     * with a {@code SecurityPermission("removeProvider."+name)}
427     * permission.
428     *
429     * @param name the name of the provider to remove.
430     *
431     * @throws  SecurityException
432     *          if a security manager exists and its {@link
433     *          java.lang.SecurityManager#checkSecurityAccess} method
434     *          denies
435     *          access to remove the provider
436     *
437     * @see #getProvider
438     * @see #addProvider
439     */
440    public static synchronized void removeProvider(String name) {
441        check("removeProvider." + name);
442        ProviderList list = Providers.getFullProviderList();
443        ProviderList newList = ProviderList.remove(list, name);
444        Providers.setProviderList(newList);
445    }
446
447    /**
448     * Returns an array containing all the installed providers. The order of
449     * the providers in the array is their preference order.
450     *
451     * @return an array of all the installed providers.
452     */
453    public static Provider[] getProviders() {
454        return Providers.getFullProviderList().toArray();
455    }
456
457    /**
458     * Returns the provider installed with the specified name, if
459     * any. Returns null if no provider with the specified name is
460     * installed or if name is null.
461     *
462     * @param name the name of the provider to get.
463     *
464     * @return the provider of the specified name.
465     *
466     * @see #removeProvider
467     * @see #addProvider
468     */
469    public static Provider getProvider(String name) {
470        return Providers.getProviderList().getProvider(name);
471    }
472
473    /**
474     * Returns an array containing all installed providers that satisfy the
475     * specified selection criterion, or null if no such providers have been
476     * installed. The returned providers are ordered
477     * according to their
478     * {@linkplain #insertProviderAt(java.security.Provider, int) preference order}.
479     *
480     * <p> A cryptographic service is always associated with a particular
481     * algorithm or type. For example, a digital signature service is
482     * always associated with a particular algorithm (e.g., DSA),
483     * and a CertificateFactory service is always associated with
484     * a particular certificate type (e.g., X.509).
485     *
486     * <p>The selection criterion must be specified in one of the following two
487     * formats:
488     * <ul>
489     * <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
490     * <p> The cryptographic service name must not contain any dots.
491     * <p> A
492     * provider satisfies the specified selection criterion iff the provider
493     * implements the
494     * specified algorithm or type for the specified cryptographic service.
495     * <p> For example, "CertificateFactory.X.509"
496     * would be satisfied by any provider that supplied
497     * a CertificateFactory implementation for X.509 certificates.
498     * <li> <i>{@literal <crypto_service>.<algorithm_or_type>
499     * <attribute_name>:<attribute_value>}</i>
500     * <p> The cryptographic service name must not contain any dots. There
501     * must be one or more space characters between the
502     * <i>{@literal <algorithm_or_type>}</i> and the
503     * <i>{@literal <attribute_name>}</i>.
504     *  <p> A provider satisfies this selection criterion iff the
505     * provider implements the specified algorithm or type for the specified
506     * cryptographic service and its implementation meets the
507     * constraint expressed by the specified attribute name/value pair.
508     * <p> For example, "Signature.SHA1withDSA KeySize:1024" would be
509     * satisfied by any provider that implemented
510     * the SHA1withDSA signature algorithm with a keysize of 1024 (or larger).
511     *
512     * </ul>
513     *
514     * <p> See the <a href=
515     * "{@docRoot}/../specs/security/standard-names.html">
516     * Java Security Standard Algorithm Names Specification</a>
517     * for information about standard cryptographic service names, standard
518     * algorithm names and standard attribute names.
519     *
520     * @param filter the criterion for selecting
521     * providers. The filter is case-insensitive.
522     *
523     * @return all the installed providers that satisfy the selection
524     * criterion, or null if no such providers have been installed.
525     *
526     * @throws InvalidParameterException
527     *         if the filter is not in the required format
528     * @throws NullPointerException if filter is null
529     *
530     * @see #getProviders(java.util.Map)
531     * @since 1.3
532     */
533    public static Provider[] getProviders(String filter) {
534        String key = null;
535        String value = null;
536        int index = filter.indexOf(':');
537
538        if (index == -1) {
539            key = filter;
540            value = "";
541        } else {
542            key = filter.substring(0, index);
543            value = filter.substring(index + 1);
544        }
545
546        Hashtable<String, String> hashtableFilter = new Hashtable<>(1);
547        hashtableFilter.put(key, value);
548
549        return (getProviders(hashtableFilter));
550    }
551
552    /**
553     * Returns an array containing all installed providers that satisfy the
554     * specified selection criteria, or null if no such providers have been
555     * installed. The returned providers are ordered
556     * according to their
557     * {@linkplain #insertProviderAt(java.security.Provider, int)
558     * preference order}.
559     *
560     * <p>The selection criteria are represented by a map.
561     * Each map entry represents a selection criterion.
562     * A provider is selected iff it satisfies all selection
563     * criteria. The key for any entry in such a map must be in one of the
564     * following two formats:
565     * <ul>
566     * <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
567     * <p> The cryptographic service name must not contain any dots.
568     * <p> The value associated with the key must be an empty string.
569     * <p> A provider
570     * satisfies this selection criterion iff the provider implements the
571     * specified algorithm or type for the specified cryptographic service.
572     * <li>  <i>{@literal <crypto_service>}.
573     * {@literal <algorithm_or_type> <attribute_name>}</i>
574     * <p> The cryptographic service name must not contain any dots. There
575     * must be one or more space characters between the
576     * <i>{@literal <algorithm_or_type>}</i>
577     * and the <i>{@literal <attribute_name>}</i>.
578     * <p> The value associated with the key must be a non-empty string.
579     * A provider satisfies this selection criterion iff the
580     * provider implements the specified algorithm or type for the specified
581     * cryptographic service and its implementation meets the
582     * constraint expressed by the specified attribute name/value pair.
583     * </ul>
584     *
585     * <p> See the <a href=
586     * "{@docRoot}/../specs/security/standard-names.html">
587     * Java Security Standard Algorithm Names Specification</a>
588     * for information about standard cryptographic service names, standard
589     * algorithm names and standard attribute names.
590     *
591     * @param filter the criteria for selecting
592     * providers. The filter is case-insensitive.
593     *
594     * @return all the installed providers that satisfy the selection
595     * criteria, or null if no such providers have been installed.
596     *
597     * @throws InvalidParameterException
598     *         if the filter is not in the required format
599     * @throws NullPointerException if filter is null
600     *
601     * @see #getProviders(java.lang.String)
602     * @since 1.3
603     */
604    public static Provider[] getProviders(Map<String,String> filter) {
605        // Get all installed providers first.
606        // Then only return those providers who satisfy the selection criteria.
607        Provider[] allProviders = Security.getProviders();
608        Set<String> keySet = filter.keySet();
609        LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5);
610
611        // Returns all installed providers
612        // if the selection criteria is null.
613        if ((keySet == null) || (allProviders == null)) {
614            return allProviders;
615        }
616
617        boolean firstSearch = true;
618
619        // For each selection criterion, remove providers
620        // which don't satisfy the criterion from the candidate set.
621        for (Iterator<String> ite = keySet.iterator(); ite.hasNext(); ) {
622            String key = ite.next();
623            String value = filter.get(key);
624
625            LinkedHashSet<Provider> newCandidates = getAllQualifyingCandidates(key, value,
626                                                               allProviders);
627            if (firstSearch) {
628                candidates = newCandidates;
629                firstSearch = false;
630            }
631
632            if ((newCandidates != null) && !newCandidates.isEmpty()) {
633                // For each provider in the candidates set, if it
634                // isn't in the newCandidate set, we should remove
635                // it from the candidate set.
636                for (Iterator<Provider> cansIte = candidates.iterator();
637                     cansIte.hasNext(); ) {
638                    Provider prov = cansIte.next();
639                    if (!newCandidates.contains(prov)) {
640                        cansIte.remove();
641                    }
642                }
643            } else {
644                candidates = null;
645                break;
646            }
647        }
648
649        if ((candidates == null) || (candidates.isEmpty()))
650            return null;
651
652        Object[] candidatesArray = candidates.toArray();
653        Provider[] result = new Provider[candidatesArray.length];
654
655        for (int i = 0; i < result.length; i++) {
656            result[i] = (Provider)candidatesArray[i];
657        }
658
659        return result;
660    }
661
662    // Map containing cached Spi Class objects of the specified type
663    private static final Map<String, Class<?>> spiMap =
664            new ConcurrentHashMap<>();
665
666    /**
667     * Return the Class object for the given engine type
668     * (e.g. "MessageDigest"). Works for Spis in the java.security package
669     * only.
670     */
671    private static Class<?> getSpiClass(String type) {
672        Class<?> clazz = spiMap.get(type);
673        if (clazz != null) {
674            return clazz;
675        }
676        try {
677            clazz = Class.forName("java.security." + type + "Spi");
678            spiMap.put(type, clazz);
679            return clazz;
680        } catch (ClassNotFoundException e) {
681            throw new AssertionError("Spi class not found", e);
682        }
683    }
684
685    /*
686     * Returns an array of objects: the first object in the array is
687     * an instance of an implementation of the requested algorithm
688     * and type, and the second object in the array identifies the provider
689     * of that implementation.
690     * The {@code provider} argument can be null, in which case all
691     * configured providers will be searched in order of preference.
692     */
693    static Object[] getImpl(String algorithm, String type, String provider)
694            throws NoSuchAlgorithmException, NoSuchProviderException {
695        if (provider == null) {
696            return GetInstance.getInstance
697                (type, getSpiClass(type), algorithm).toArray();
698        } else {
699            return GetInstance.getInstance
700                (type, getSpiClass(type), algorithm, provider).toArray();
701        }
702    }
703
704    static Object[] getImpl(String algorithm, String type, String provider,
705            Object params) throws NoSuchAlgorithmException,
706            NoSuchProviderException, InvalidAlgorithmParameterException {
707        if (provider == null) {
708            return GetInstance.getInstance
709                (type, getSpiClass(type), algorithm, params).toArray();
710        } else {
711            return GetInstance.getInstance
712                (type, getSpiClass(type), algorithm, params, provider).toArray();
713        }
714    }
715
716    /*
717     * Returns an array of objects: the first object in the array is
718     * an instance of an implementation of the requested algorithm
719     * and type, and the second object in the array identifies the provider
720     * of that implementation.
721     * The {@code provider} argument cannot be null.
722     */
723    static Object[] getImpl(String algorithm, String type, Provider provider)
724            throws NoSuchAlgorithmException {
725        return GetInstance.getInstance
726            (type, getSpiClass(type), algorithm, provider).toArray();
727    }
728
729    static Object[] getImpl(String algorithm, String type, Provider provider,
730            Object params) throws NoSuchAlgorithmException,
731            InvalidAlgorithmParameterException {
732        return GetInstance.getInstance
733            (type, getSpiClass(type), algorithm, params, provider).toArray();
734    }
735
736    /**
737     * Gets a security property value.
738     *
739     * <p>First, if there is a security manager, its
740     * {@code checkPermission}  method is called with a
741     * {@code java.security.SecurityPermission("getProperty."+key)}
742     * permission to see if it's ok to retrieve the specified
743     * security property value..
744     *
745     * @param key the key of the property being retrieved.
746     *
747     * @return the value of the security property corresponding to key.
748     *
749     * @throws  SecurityException
750     *          if a security manager exists and its {@link
751     *          java.lang.SecurityManager#checkPermission} method
752     *          denies
753     *          access to retrieve the specified security property value
754     * @throws  NullPointerException is key is null
755     *
756     * @see #setProperty
757     * @see java.security.SecurityPermission
758     */
759    public static String getProperty(String key) {
760        SecurityManager sm = System.getSecurityManager();
761        if (sm != null) {
762            sm.checkPermission(new SecurityPermission("getProperty."+
763                                                      key));
764        }
765        String name = props.getProperty(key);
766        if (name != null)
767            name = name.trim(); // could be a class name with trailing ws
768        return name;
769    }
770
771    /**
772     * Sets a security property value.
773     *
774     * <p>First, if there is a security manager, its
775     * {@code checkPermission} method is called with a
776     * {@code java.security.SecurityPermission("setProperty."+key)}
777     * permission to see if it's ok to set the specified
778     * security property value.
779     *
780     * @param key the name of the property to be set.
781     *
782     * @param datum the value of the property to be set.
783     *
784     * @throws  SecurityException
785     *          if a security manager exists and its {@link
786     *          java.lang.SecurityManager#checkPermission} method
787     *          denies access to set the specified security property value
788     * @throws  NullPointerException if key or datum is null
789     *
790     * @see #getProperty
791     * @see java.security.SecurityPermission
792     */
793    public static void setProperty(String key, String datum) {
794        check("setProperty."+key);
795        props.put(key, datum);
796        invalidateSMCache(key);  /* See below. */
797    }
798
799    /*
800     * Implementation detail:  If the property we just set in
801     * setProperty() was either "package.access" or
802     * "package.definition", we need to signal to the SecurityManager
803     * class that the value has just changed, and that it should
804     * invalidate it's local cache values.
805     */
806    private static void invalidateSMCache(String key) {
807
808        final boolean pa = key.equals("package.access");
809        final boolean pd = key.equals("package.definition");
810
811        if (pa || pd) {
812            SharedSecrets.getJavaLangAccess().invalidatePackageAccessCache();
813        }
814    }
815
816    private static void check(String directive) {
817        SecurityManager security = System.getSecurityManager();
818        if (security != null) {
819            security.checkSecurityAccess(directive);
820        }
821    }
822
823    private static void checkInsertProvider(String name) {
824        SecurityManager security = System.getSecurityManager();
825        if (security != null) {
826            try {
827                security.checkSecurityAccess("insertProvider");
828            } catch (SecurityException se1) {
829                try {
830                    security.checkSecurityAccess("insertProvider." + name);
831                } catch (SecurityException se2) {
832                    // throw first exception, but add second to suppressed
833                    se1.addSuppressed(se2);
834                    throw se1;
835                }
836            }
837        }
838    }
839
840    /*
841    * Returns all providers who satisfy the specified
842    * criterion.
843    */
844    private static LinkedHashSet<Provider> getAllQualifyingCandidates(
845                                                String filterKey,
846                                                String filterValue,
847                                                Provider[] allProviders) {
848        String[] filterComponents = getFilterComponents(filterKey,
849                                                        filterValue);
850
851        // The first component is the service name.
852        // The second is the algorithm name.
853        // If the third isn't null, that is the attrinute name.
854        String serviceName = filterComponents[0];
855        String algName = filterComponents[1];
856        String attrName = filterComponents[2];
857
858        return getProvidersNotUsingCache(serviceName, algName, attrName,
859                                         filterValue, allProviders);
860    }
861
862    private static LinkedHashSet<Provider> getProvidersNotUsingCache(
863                                                String serviceName,
864                                                String algName,
865                                                String attrName,
866                                                String filterValue,
867                                                Provider[] allProviders) {
868        LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5);
869        for (int i = 0; i < allProviders.length; i++) {
870            if (isCriterionSatisfied(allProviders[i], serviceName,
871                                     algName,
872                                     attrName, filterValue)) {
873                candidates.add(allProviders[i]);
874            }
875        }
876        return candidates;
877    }
878
879    /*
880     * Returns true if the given provider satisfies
881     * the selection criterion key:value.
882     */
883    private static boolean isCriterionSatisfied(Provider prov,
884                                                String serviceName,
885                                                String algName,
886                                                String attrName,
887                                                String filterValue) {
888        String key = serviceName + '.' + algName;
889
890        if (attrName != null) {
891            key += ' ' + attrName;
892        }
893        // Check whether the provider has a property
894        // whose key is the same as the given key.
895        String propValue = getProviderProperty(key, prov);
896
897        if (propValue == null) {
898            // Check whether we have an alias instead
899            // of a standard name in the key.
900            String standardName = getProviderProperty("Alg.Alias." +
901                                                      serviceName + "." +
902                                                      algName,
903                                                      prov);
904            if (standardName != null) {
905                key = serviceName + "." + standardName;
906
907                if (attrName != null) {
908                    key += ' ' + attrName;
909                }
910
911                propValue = getProviderProperty(key, prov);
912            }
913
914            if (propValue == null) {
915                // The provider doesn't have the given
916                // key in its property list.
917                return false;
918            }
919        }
920
921        // If the key is in the format of:
922        // <crypto_service>.<algorithm_or_type>,
923        // there is no need to check the value.
924
925        if (attrName == null) {
926            return true;
927        }
928
929        // If we get here, the key must be in the
930        // format of <crypto_service>.<algorithm_or_provider> <attribute_name>.
931        if (isStandardAttr(attrName)) {
932            return isConstraintSatisfied(attrName, filterValue, propValue);
933        } else {
934            return filterValue.equalsIgnoreCase(propValue);
935        }
936    }
937
938    /*
939     * Returns true if the attribute is a standard attribute;
940     * otherwise, returns false.
941     */
942    private static boolean isStandardAttr(String attribute) {
943        // For now, we just have two standard attributes:
944        // KeySize and ImplementedIn.
945        if (attribute.equalsIgnoreCase("KeySize"))
946            return true;
947
948        if (attribute.equalsIgnoreCase("ImplementedIn"))
949            return true;
950
951        return false;
952    }
953
954    /*
955     * Returns true if the requested attribute value is supported;
956     * otherwise, returns false.
957     */
958    private static boolean isConstraintSatisfied(String attribute,
959                                                 String value,
960                                                 String prop) {
961        // For KeySize, prop is the max key size the
962        // provider supports for a specific <crypto_service>.<algorithm>.
963        if (attribute.equalsIgnoreCase("KeySize")) {
964            int requestedSize = Integer.parseInt(value);
965            int maxSize = Integer.parseInt(prop);
966            if (requestedSize <= maxSize) {
967                return true;
968            } else {
969                return false;
970            }
971        }
972
973        // For Type, prop is the type of the implementation
974        // for a specific <crypto service>.<algorithm>.
975        if (attribute.equalsIgnoreCase("ImplementedIn")) {
976            return value.equalsIgnoreCase(prop);
977        }
978
979        return false;
980    }
981
982    static String[] getFilterComponents(String filterKey, String filterValue) {
983        int algIndex = filterKey.indexOf('.');
984
985        if (algIndex < 0) {
986            // There must be a dot in the filter, and the dot
987            // shouldn't be at the beginning of this string.
988            throw new InvalidParameterException("Invalid filter");
989        }
990
991        String serviceName = filterKey.substring(0, algIndex);
992        String algName = null;
993        String attrName = null;
994
995        if (filterValue.length() == 0) {
996            // The filterValue is an empty string. So the filterKey
997            // should be in the format of <crypto_service>.<algorithm_or_type>.
998            algName = filterKey.substring(algIndex + 1).trim();
999            if (algName.length() == 0) {
1000                // There must be a algorithm or type name.
1001                throw new InvalidParameterException("Invalid filter");
1002            }
1003        } else {
1004            // The filterValue is a non-empty string. So the filterKey must be
1005            // in the format of
1006            // <crypto_service>.<algorithm_or_type> <attribute_name>
1007            int attrIndex = filterKey.indexOf(' ');
1008
1009            if (attrIndex == -1) {
1010                // There is no attribute name in the filter.
1011                throw new InvalidParameterException("Invalid filter");
1012            } else {
1013                attrName = filterKey.substring(attrIndex + 1).trim();
1014                if (attrName.length() == 0) {
1015                    // There is no attribute name in the filter.
1016                    throw new InvalidParameterException("Invalid filter");
1017                }
1018            }
1019
1020            // There must be an algorithm name in the filter.
1021            if ((attrIndex < algIndex) ||
1022                (algIndex == attrIndex - 1)) {
1023                throw new InvalidParameterException("Invalid filter");
1024            } else {
1025                algName = filterKey.substring(algIndex + 1, attrIndex);
1026            }
1027        }
1028
1029        String[] result = new String[3];
1030        result[0] = serviceName;
1031        result[1] = algName;
1032        result[2] = attrName;
1033
1034        return result;
1035    }
1036
1037    /**
1038     * Returns a Set of Strings containing the names of all available
1039     * algorithms or types for the specified Java cryptographic service
1040     * (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore). Returns
1041     * an empty Set if there is no provider that supports the
1042     * specified service or if serviceName is null. For a complete list
1043     * of Java cryptographic services, please see the
1044     * {@extLink security_guide_jca
1045     * Java Cryptography Architecture (JCA) Reference Guide}.
1046     * Note: the returned set is immutable.
1047     *
1048     * @param serviceName the name of the Java cryptographic
1049     * service (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore).
1050     * Note: this parameter is case-insensitive.
1051     *
1052     * @return a Set of Strings containing the names of all available
1053     * algorithms or types for the specified Java cryptographic service
1054     * or an empty set if no provider supports the specified service.
1055     *
1056     * @since 1.4
1057     **/
1058    public static Set<String> getAlgorithms(String serviceName) {
1059
1060        if ((serviceName == null) || (serviceName.length() == 0) ||
1061            (serviceName.endsWith("."))) {
1062            return Collections.emptySet();
1063        }
1064
1065        HashSet<String> result = new HashSet<>();
1066        Provider[] providers = Security.getProviders();
1067
1068        for (int i = 0; i < providers.length; i++) {
1069            // Check the keys for each provider.
1070            for (Enumeration<Object> e = providers[i].keys();
1071                                                e.hasMoreElements(); ) {
1072                String currentKey =
1073                        ((String)e.nextElement()).toUpperCase(Locale.ENGLISH);
1074                if (currentKey.startsWith(
1075                        serviceName.toUpperCase(Locale.ENGLISH))) {
1076                    // We should skip the currentKey if it contains a
1077                    // whitespace. The reason is: such an entry in the
1078                    // provider property contains attributes for the
1079                    // implementation of an algorithm. We are only interested
1080                    // in entries which lead to the implementation
1081                    // classes.
1082                    if (currentKey.indexOf(' ') < 0) {
1083                        result.add(currentKey.substring(
1084                                                serviceName.length() + 1));
1085                    }
1086                }
1087            }
1088        }
1089        return Collections.unmodifiableSet(result);
1090    }
1091}
1092