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
18package java.security;
19
20
21import java.io.BufferedInputStream;
22import java.io.InputStream;
23import java.util.Enumeration;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.List;
28import java.util.Map;
29import java.util.Map.Entry;
30import java.util.Properties;
31import java.util.Set;
32import org.apache.harmony.security.fortress.Engine;
33import org.apache.harmony.security.fortress.SecurityAccess;
34import 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 */
41public 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;
57            configStream.close();
58        } catch (Exception ex) {
59            System.logE("Could not load 'security.properties'", ex);
60        }
61        if (!loaded) {
62            registerDefaultProviders();
63        }
64        Engine.door = new SecurityDoor();
65    }
66
67    /**
68     * This class can't be instantiated.
69     */
70    private Security() {
71    }
72
73    // Register default providers
74    private static void registerDefaultProviders() {
75        secprops.put("security.provider.1", "org.apache.harmony.xnet.provider.jsse.OpenSSLProvider");
76        secprops.put("security.provider.2", "org.apache.harmony.security.provider.cert.DRLCertFactory");
77        secprops.put("security.provider.3", "org.bouncycastle.jce.provider.BouncyCastleProvider");
78        secprops.put("security.provider.4", "org.apache.harmony.security.provider.crypto.CryptoProvider");
79        secprops.put("security.provider.5", "org.apache.harmony.xnet.provider.jsse.JSSEProvider");
80    }
81
82    /**
83     * Returns value for the specified algorithm with the specified name.
84     *
85     * @param algName
86     *            the name of the algorithm.
87     * @param propName
88     *            the name of the property.
89     * @return value of the property.
90     * @deprecated Use {@link AlgorithmParameters} and {@link KeyFactory}
91     *             instead.
92     */
93    @Deprecated
94    public static String getAlgorithmProperty(String algName, String propName) {
95        if (algName == null || propName == null) {
96            return null;
97        }
98        String prop = "Alg." + propName + "." + algName;
99        Provider[] providers = getProviders();
100        for (Provider provider : providers) {
101            for (Enumeration e = provider.propertyNames(); e.hasMoreElements(); ) {
102                String propertyName = (String) e.nextElement();
103                if (propertyName.equalsIgnoreCase(prop)) {
104                    return provider.getProperty(propertyName);
105                }
106            }
107        }
108        return null;
109    }
110
111    /**
112     * Insert the given {@code Provider} at the specified {@code position}. The
113     * positions define the preference order in which providers are searched for
114     * requested algorithms.
115     *
116     * @param provider
117     *            the provider to insert.
118     * @param position
119     *            the position (starting from 1).
120     * @return the actual position or {@code -1} if the given {@code provider}
121     *         was already in the list. The actual position may be different
122     *         from the desired position.
123     */
124    public static synchronized int insertProviderAt(Provider provider, int position) {
125        // check that provider is not already
126        // installed, else return -1; if (position <1) or (position > max
127        // position) position = max position + 1; insert provider, shift up
128        // one position for next providers; Note: The position is 1-based
129        if (getProvider(provider.getName()) != null) {
130            return -1;
131        }
132        int result = Services.insertProviderAt(provider, position);
133        renumProviders();
134        return result;
135    }
136
137    /**
138     * Adds the given {@code provider} to the collection of providers at the
139     * next available position.
140     *
141     * @param provider
142     *            the provider to be added.
143     * @return the actual position or {@code -1} if the given {@code provider}
144     *         was already in the list.
145     */
146    public static int addProvider(Provider provider) {
147        return insertProviderAt(provider, 0);
148    }
149
150    /**
151     * Removes the {@code Provider} with the specified name form the collection
152     * of providers. If the the {@code Provider} with the specified name is
153     * removed, all provider at a greater position are shifted down one
154     * position.
155     *
156     * <p>Returns silently if {@code name} is {@code null} or no provider with the
157     * specified name is installed.
158     *
159     * @param name
160     *            the name of the provider to remove.
161     */
162    public static synchronized void removeProvider(String name) {
163        // It is not clear from spec.:
164        // 1. if name is null, should we checkSecurityAccess or not?
165        //    throw SecurityException or not?
166        // 2. as 1 but provider is not installed
167        // 3. behavior if name is empty string?
168
169        Provider p;
170        if ((name == null) || (name.length() == 0)) {
171            return;
172        }
173        p = getProvider(name);
174        if (p == null) {
175            return;
176        }
177        Services.removeProvider(p.getProviderNumber());
178        renumProviders();
179        p.setProviderNumber(-1);
180    }
181
182    /**
183     * Returns an array containing all installed providers. The providers are
184     * ordered according their preference order.
185     *
186     * @return an array containing all installed providers.
187     */
188    public static synchronized Provider[] getProviders() {
189        return Services.getProviders();
190    }
191
192    /**
193     * Returns the {@code Provider} with the specified name. Returns {@code
194     * null} if name is {@code null} or no provider with the specified name is
195     * installed.
196     *
197     * @param name
198     *            the name of the requested provider.
199     * @return the provider with the specified name, maybe {@code null}.
200     */
201    public static synchronized Provider getProvider(String name) {
202        return Services.getProvider(name);
203    }
204
205    /**
206     * Returns the array of providers which meet the user supplied string
207     * filter. The specified filter must be supplied in one of two formats:
208     * <nl>
209     * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
210     * <p>
211     * (for example: "MessageDigest.SHA")
212     * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
213     * ATTR_NAME:ATTR_VALUE
214     * <p>
215     * (for example: "Signature.MD2withRSA KeySize:512")
216     * </nl>
217     *
218     * @param filter
219     *            case-insensitive filter.
220     * @return the providers which meet the user supplied string filter {@code
221     *         filter}. A {@code null} value signifies that none of the
222     *         installed providers meets the filter specification.
223     * @throws InvalidParameterException
224     *             if an unusable filter is supplied.
225     * @throws NullPointerException
226     *             if {@code filter} is {@code null}.
227     */
228    public static Provider[] getProviders(String filter) {
229        if (filter == null) {
230            throw new NullPointerException("filter == null");
231        }
232        if (filter.length() == 0) {
233            throw new InvalidParameterException();
234        }
235        HashMap<String, String> hm = new HashMap<String, String>();
236        int i = filter.indexOf(':');
237        if ((i == filter.length() - 1) || (i == 0)) {
238            throw new InvalidParameterException();
239        }
240        if (i < 1) {
241            hm.put(filter, "");
242        } else {
243            hm.put(filter.substring(0, i), filter.substring(i + 1));
244        }
245        return getProviders(hm);
246    }
247
248    /**
249     * Returns the array of providers which meet the user supplied set of
250     * filters. The filter must be supplied in one of two formats:
251     * <nl>
252     * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
253     * <p>
254     * for example: "MessageDigest.SHA" The value associated with the key must
255     * be an empty string. <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
256     * ATTR_NAME:ATTR_VALUE
257     * <p>
258     * for example: "Signature.MD2withRSA KeySize:512" where "KeySize:512" is
259     * the value of the filter map entry.
260     * </nl>
261     *
262     * @param filter
263     *            case-insensitive filter.
264     * @return the providers which meet the user supplied string filter {@code
265     *         filter}. A {@code null} value signifies that none of the
266     *         installed providers meets the filter specification.
267     * @throws InvalidParameterException
268     *             if an unusable filter is supplied.
269     * @throws NullPointerException
270     *             if {@code filter} is {@code null}.
271     */
272    public static synchronized Provider[] getProviders(Map<String,String> filter) {
273        if (filter == null) {
274            throw new NullPointerException("filter == null");
275        }
276        if (filter.isEmpty()) {
277            return null;
278        }
279        java.util.List<Provider> result = Services.getProvidersList();
280        Set<Entry<String, String>> keys = filter.entrySet();
281        Map.Entry<String, String> entry;
282        for (Iterator<Entry<String, String>> it = keys.iterator(); it.hasNext();) {
283            entry = it.next();
284            String key = entry.getKey();
285            String val = entry.getValue();
286            String attribute = null;
287            int i = key.indexOf(' ');
288            int j = key.indexOf('.');
289            if (j == -1) {
290                throw new InvalidParameterException();
291            }
292            if (i == -1) { // <crypto_service>.<algorithm_or_type>
293                if (val.length() != 0) {
294                    throw new InvalidParameterException();
295                }
296            } else { // <crypto_service>.<algorithm_or_type> <attribute_name>
297                if (val.length() == 0) {
298                    throw new InvalidParameterException();
299                }
300                attribute = key.substring(i + 1);
301                if (attribute.trim().length() == 0) {
302                    throw new InvalidParameterException();
303                }
304                key = key.substring(0, i);
305            }
306            String serv = key.substring(0, j);
307            String alg = key.substring(j + 1);
308            if (serv.length() == 0 || alg.length() == 0) {
309                throw new InvalidParameterException();
310            }
311            Provider p;
312            for (int k = 0; k < result.size(); k++) {
313                try {
314                    p = result.get(k);
315                } catch (IndexOutOfBoundsException e) {
316                    break;
317                }
318                if (!p.implementsAlg(serv, alg, attribute, val)) {
319                    result.remove(p);
320                    k--;
321                }
322            }
323        }
324        if (result.size() > 0) {
325            return result.toArray(new Provider[result.size()]);
326        }
327        return null;
328    }
329
330    /**
331     * Returns the value of the security property named by the argument.
332     *
333     * @param key
334     *            the name of the requested security property.
335     * @return the value of the security property.
336     */
337    public static String getProperty(String key) {
338        if (key == null) {
339            throw new NullPointerException("key == null");
340        }
341        String property = secprops.getProperty(key);
342        if (property != null) {
343            property = property.trim();
344        }
345        return property;
346    }
347
348    /**
349     * Sets the value of the specified security property.
350     */
351    public static void setProperty(String key, String value) {
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     */
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     */
388    private static void renumProviders() {
389        Provider[] p = Services.getProviders();
390        for (int i = 0; i < p.length; i++) {
391            p[i].setProviderNumber(i + 1);
392        }
393    }
394
395    private static class SecurityDoor implements SecurityAccess {
396        // Access to Security.renumProviders()
397        public void renumProviders() {
398            Security.renumProviders();
399        }
400
401        //  Access to Security.getAliases()
402        public List<String> getAliases(Provider.Service s) {
403            return s.getAliases();
404        }
405
406        // Access to Provider.getService()
407        public Provider.Service getService(Provider p, String type) {
408            return p.getService(type);
409        }
410    }
411}
412