1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.security;
19 
20 import java.io.BufferedInputStream;
21 import java.io.InputStream;
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Properties;
31 import java.util.Set;
32 import org.apache.harmony.security.fortress.Engine;
33 import org.apache.harmony.security.fortress.SecurityAccess;
34 import org.apache.harmony.security.fortress.Services;
35 
36 /**
37  * {@code Security} is the central class in the Java Security API. It manages
38  * the list of security {@code Provider} that have been installed into this
39  * runtime environment.
40  */
41 public final class Security {
42 
43     // Security properties
44     private static final Properties secprops = new Properties();
45 
46     // static initialization
47     // - load security properties files
48     // - load statically registered providers
49     // - if no provider description file found then load default providers
50     static {
51         boolean loaded = false;
52         try {
53             InputStream configStream = Security.class.getResourceAsStream("security.properties");
54             InputStream input = new BufferedInputStream(configStream);
55             secprops.load(input);
56             loaded = true;
configStream.close()57             configStream.close();
58         } catch (Exception ex) {
59             System.logE("Could not load 'security.properties'", ex);
60         }
61         if (!loaded) {
registerDefaultProviders()62             registerDefaultProviders();
63         }
64         Engine.door = new SecurityDoor();
65     }
66 
67     /**
68      * This class can't be instantiated.
69      */
Security()70     private Security() {
71     }
72 
73     // Register default providers
registerDefaultProviders()74     private static void registerDefaultProviders() {
75         secprops.put("security.provider.1", "com.android.org.conscrypt.OpenSSLProvider");
76         secprops.put("security.provider.2", "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");
77         secprops.put("security.provider.3", "org.apache.harmony.security.provider.crypto.CryptoProvider");
78         secprops.put("security.provider.4", "com.android.org.conscrypt.JSSEProvider");
79     }
80 
81     /**
82      * Returns value for the specified algorithm with the specified name.
83      *
84      * @param algName
85      *            the name of the algorithm.
86      * @param propName
87      *            the name of the property.
88      * @return value of the property.
89      * @deprecated Use {@link AlgorithmParameters} and {@link KeyFactory} instead.
90      */
91     @Deprecated
getAlgorithmProperty(String algName, String propName)92     public static String getAlgorithmProperty(String algName, String propName) {
93         if (algName == null || propName == null) {
94             return null;
95         }
96         String prop = "Alg." + propName + "." + algName;
97         Provider[] providers = getProviders();
98         for (Provider provider : providers) {
99             for (Enumeration<?> e = provider.propertyNames(); e.hasMoreElements();) {
100                 String propertyName = (String) e.nextElement();
101                 if (propertyName.equalsIgnoreCase(prop)) {
102                     return provider.getProperty(propertyName);
103                 }
104             }
105         }
106         return null;
107     }
108 
109     /**
110      * Insert the given {@code Provider} at the specified {@code position}. The
111      * positions define the preference order in which providers are searched for
112      * requested algorithms.
113      *
114      * @param provider
115      *            the provider to insert.
116      * @param position
117      *            the position (starting from 1).
118      * @return the actual position or {@code -1} if the given {@code provider}
119      *         was already in the list. The actual position may be different
120      *         from the desired position.
121      */
insertProviderAt(Provider provider, int position)122     public static synchronized int insertProviderAt(Provider provider, int position) {
123         // check that provider is not already
124         // installed, else return -1; if (position <1) or (position > max
125         // position) position = max position + 1; insert provider, shift up
126         // one position for next providers; Note: The position is 1-based
127         if (getProvider(provider.getName()) != null) {
128             return -1;
129         }
130         int result = Services.insertProviderAt(provider, position);
131         renumProviders();
132         return result;
133     }
134 
135     /**
136      * Adds the given {@code provider} to the collection of providers at the
137      * next available position.
138      *
139      * @param provider
140      *            the provider to be added.
141      * @return the actual position or {@code -1} if the given {@code provider}
142      *         was already in the list.
143      */
addProvider(Provider provider)144     public static int addProvider(Provider provider) {
145         return insertProviderAt(provider, 0);
146     }
147 
148     /**
149      * Removes the {@code Provider} with the specified name form the collection
150      * of providers. If the the {@code Provider} with the specified name is
151      * removed, all provider at a greater position are shifted down one
152      * position.
153      *
154      * <p>Returns silently if {@code name} is {@code null} or no provider with the
155      * specified name is installed.
156      *
157      * @param name
158      *            the name of the provider to remove.
159      */
removeProvider(String name)160     public static synchronized void removeProvider(String name) {
161         // It is not clear from spec.:
162         // 1. if name is null, should we checkSecurityAccess or not?
163         //    throw SecurityException or not?
164         // 2. as 1 but provider is not installed
165         // 3. behavior if name is empty string?
166 
167         Provider p;
168         if ((name == null) || (name.length() == 0)) {
169             return;
170         }
171         p = getProvider(name);
172         if (p == null) {
173             return;
174         }
175         Services.removeProvider(p.getProviderNumber());
176         renumProviders();
177         p.setProviderNumber(-1);
178     }
179 
180     /**
181      * Returns an array containing all installed providers. The providers are
182      * ordered according their preference order.
183      *
184      * @return an array containing all installed providers.
185      */
getProviders()186     public static synchronized Provider[] getProviders() {
187         ArrayList<Provider> providers = Services.getProviders();
188         return providers.toArray(new Provider[providers.size()]);
189     }
190 
191     /**
192      * Returns the {@code Provider} with the specified name. Returns {@code
193      * null} if name is {@code null} or no provider with the specified name is
194      * installed.
195      *
196      * @param name
197      *            the name of the requested provider.
198      * @return the provider with the specified name, maybe {@code null}.
199      */
getProvider(String name)200     public static synchronized Provider getProvider(String name) {
201         return Services.getProvider(name);
202     }
203 
204     /**
205      * Returns the array of providers which meet the user supplied string
206      * filter. The specified filter must be supplied in one of two formats:
207      * <nl>
208      * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
209      * <p>
210      * (for example: "MessageDigest.SHA")
211      * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
212      * ATTR_NAME:ATTR_VALUE
213      * <p>
214      * (for example: "Signature.MD2withRSA KeySize:512")
215      * </nl>
216      *
217      * @param filter
218      *            case-insensitive filter.
219      * @return the providers which meet the user supplied string filter {@code
220      *         filter}. A {@code null} value signifies that none of the
221      *         installed providers meets the filter specification.
222      * @throws InvalidParameterException
223      *             if an unusable filter is supplied.
224      * @throws NullPointerException
225      *             if {@code filter} is {@code null}.
226      */
getProviders(String filter)227     public static Provider[] getProviders(String filter) {
228         if (filter == null) {
229             throw new NullPointerException("filter == null");
230         }
231         if (filter.length() == 0) {
232             throw new InvalidParameterException();
233         }
234         HashMap<String, String> hm = new HashMap<String, String>();
235         int i = filter.indexOf(':');
236         if ((i == filter.length() - 1) || (i == 0)) {
237             throw new InvalidParameterException();
238         }
239         if (i < 1) {
240             hm.put(filter, "");
241         } else {
242             hm.put(filter.substring(0, i), filter.substring(i + 1));
243         }
244         return getProviders(hm);
245     }
246 
247     /**
248      * Returns the array of providers which meet the user supplied set of
249      * filters. The filter must be supplied in one of two formats:
250      * <nl>
251      * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
252      * <p>
253      * for example: "MessageDigest.SHA" The value associated with the key must
254      * be an empty string. <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
255      * ATTR_NAME:ATTR_VALUE
256      * <p>
257      * for example: "Signature.MD2withRSA KeySize:512" where "KeySize:512" is
258      * the value of the filter map entry.
259      * </nl>
260      *
261      * @param filter
262      *            case-insensitive filter.
263      * @return the providers which meet the user supplied string filter {@code
264      *         filter}. A {@code null} value signifies that none of the
265      *         installed providers meets the filter specification.
266      * @throws InvalidParameterException
267      *             if an unusable filter is supplied.
268      * @throws NullPointerException
269      *             if {@code filter} is {@code null}.
270      */
getProviders(Map<String,String> filter)271     public static synchronized Provider[] getProviders(Map<String,String> filter) {
272         if (filter == null) {
273             throw new NullPointerException("filter == null");
274         }
275         if (filter.isEmpty()) {
276             return null;
277         }
278         ArrayList<Provider> result = new ArrayList<Provider>(Services.getProviders());
279         Set<Entry<String, String>> keys = filter.entrySet();
280         Map.Entry<String, String> entry;
281         for (Iterator<Entry<String, String>> it = keys.iterator(); it.hasNext();) {
282             entry = it.next();
283             String key = entry.getKey();
284             String val = entry.getValue();
285             String attribute = null;
286             int i = key.indexOf(' ');
287             int j = key.indexOf('.');
288             if (j == -1) {
289                 throw new InvalidParameterException();
290             }
291             if (i == -1) { // <crypto_service>.<algorithm_or_type>
292                 if (val.length() != 0) {
293                     throw new InvalidParameterException();
294                 }
295             } else { // <crypto_service>.<algorithm_or_type> <attribute_name>
296                 if (val.length() == 0) {
297                     throw new InvalidParameterException();
298                 }
299                 attribute = key.substring(i + 1);
300                 if (attribute.trim().length() == 0) {
301                     throw new InvalidParameterException();
302                 }
303                 key = key.substring(0, i);
304             }
305             String serv = key.substring(0, j);
306             String alg = key.substring(j + 1);
307             if (serv.length() == 0 || alg.length() == 0) {
308                 throw new InvalidParameterException();
309             }
310             filterProviders(result, serv, alg, attribute, val);
311         }
312         if (result.size() > 0) {
313             return result.toArray(new Provider[result.size()]);
314         }
315         return null;
316     }
317 
filterProviders(ArrayList<Provider> providers, String service, String algorithm, String attribute, String attrValue)318     private static void filterProviders(ArrayList<Provider> providers, String service,
319             String algorithm, String attribute, String attrValue) {
320         Iterator<Provider> it = providers.iterator();
321         while (it.hasNext()) {
322             Provider p = it.next();
323             if (!p.implementsAlg(service, algorithm, attribute, attrValue)) {
324                 it.remove();
325             }
326         }
327     }
328 
329     /**
330      * Returns the value of the security property named by the argument.
331      *
332      * @param key
333      *            the name of the requested security property.
334      * @return the value of the security property.
335      */
getProperty(String key)336     public static String getProperty(String key) {
337         if (key == null) {
338             throw new NullPointerException("key == null");
339         }
340         String property = secprops.getProperty(key);
341         if (property != null) {
342             property = property.trim();
343         }
344         return property;
345     }
346 
347     /**
348      * Sets the value of the specified security property.
349      */
setProperty(String key, String value)350     public static void setProperty(String key, String value) {
351         Services.setNeedRefresh();
352         secprops.put(key, value);
353     }
354 
355     /**
356      * Returns a {@code Set} of all registered algorithms for the specified
357      * cryptographic service. {@code "Signature"}, {@code "Cipher"} and {@code
358      * "KeyStore"} are examples for such kind of services.
359      *
360      * @param serviceName
361      *            the case-insensitive name of the service.
362      * @return a {@code Set} of all registered algorithms for the specified
363      *         cryptographic service, or an empty {@code Set} if {@code
364      *         serviceName} is {@code null} or if no registered provider
365      *         provides the requested service.
366      */
getAlgorithms(String serviceName)367     public static Set<String> getAlgorithms(String serviceName) {
368         Set<String> result = new HashSet<String>();
369         // compatibility with RI
370         if (serviceName == null) {
371             return result;
372         }
373         for (Provider provider : getProviders()) {
374             for (Provider.Service service: provider.getServices()) {
375                 if (service.getType().equalsIgnoreCase(serviceName)) {
376                     result.add(service.getAlgorithm());
377                 }
378             }
379         }
380         return result;
381     }
382 
383     /**
384      *
385      * Update sequence numbers of all providers.
386      *
387      */
renumProviders()388     private static void renumProviders() {
389         ArrayList<Provider> providers = Services.getProviders();
390         for (int i = 0; i < providers.size(); i++) {
391             providers.get(i).setProviderNumber(i + 1);
392         }
393     }
394 
395     private static class SecurityDoor implements SecurityAccess {
396         // Access to Security.renumProviders()
renumProviders()397         public void renumProviders() {
398             Security.renumProviders();
399         }
400 
401         //  Access to Security.getAliases()
getAliases(Provider.Service s)402         public List<String> getAliases(Provider.Service s) {
403             return s.getAliases();
404         }
405 
406         // Access to Provider.getService()
getService(Provider p, String type)407         public Provider.Service getService(Provider p, String type) {
408             return p.getService(type);
409         }
410     }
411 }
412