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