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
20import java.io.BufferedInputStream;
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.IOException;
24import java.io.InputStream;
25// BEGIN android-added
26import java.util.logging.Level;
27import java.util.logging.Logger;
28// END android-added
29import java.util.Enumeration;
30import java.net.URL;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.Iterator;
34import java.util.Map;
35import java.util.Properties;
36import java.util.Set;
37import java.util.Map.Entry;
38
39import org.apache.harmony.security.Util;
40import org.apache.harmony.security.fortress.Engine;
41import org.apache.harmony.security.fortress.PolicyUtils;
42import org.apache.harmony.security.fortress.SecurityAccess;
43import org.apache.harmony.security.fortress.Services;
44import org.apache.harmony.security.internal.nls.Messages;
45
46/**
47 * {@code Security} is the central class in the Java Security API. It manages
48 * the list of security {@code Provider} that have been installed into this
49 * runtime environment.
50 */
51public final class Security {
52
53    // Security properties
54    private static Properties secprops = new Properties();
55
56    // static initialization
57    // - load security properties files
58    // - load statically registered providers
59    // - if no provider description file found then load default providers
60    static {
61        AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {
62            public Void run() {
63                boolean loaded = false;
64
65                // BEGIN android-added
66                /*
67                 * Android only uses a local "security.properties" resource
68                 * for configuration. TODO: Reevaluate this decision.
69                 */
70                try {
71                    InputStream configStream =
72                        getClass().getResourceAsStream("security.properties"); //$NON-NLS-1$
73                    InputStream input =
74                        new BufferedInputStream(configStream, 8192);
75                    secprops.load(input);
76                    loaded = true;
77                    configStream.close();
78                } catch (Exception ex) {
79                    Logger.global.log(Level.SEVERE,
80                            "Could not load Security properties.", ex);
81                }
82                // END android-added
83
84                // BEGIN android-removed
85//                if (Util.equalsIgnoreCase("true", secprops.getProperty("security.allowCustomPropertiesFile", "true"))) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
86//                    String securityFile = System.getProperty("java.security.properties"); //$NON-NLS-1$
87//                    if (securityFile != null) {
88//                        if (securityFile.startsWith("=")) { // overwrite //$NON-NLS-1$
89//                            secprops = new Properties();
90//                            loaded = false;
91//                            securityFile = securityFile.substring(1);
92//                        }
93//                        try {
94//                            securityFile = PolicyUtils.expand(securityFile, System.getProperties());
95//                        } catch (PolicyUtils.ExpansionFailedException e) {
96////                            System.err.println("Could not load custom Security properties file "
97////                                    + securityFile +": " + e);
98//                        }
99//                        f = new File(securityFile);
100//                        InputStream is;
101//                        try {
102//                            if (f.exists()) {
103//                                FileInputStream fis = new FileInputStream(f);
104//                                is = new BufferedInputStream(fis);
105//                            } else {
106//                                URL url = new URL(securityFile);
107//                                is = new BufferedInputStream(url.openStream());
108//                            }
109//                            secprops.load(is);
110//                            loaded = true;
111//                            is.close();
112//                        } catch (IOException e) {
113// //                           System.err.println("Could not load custom Security properties file "
114// //                                   + securityFile +": " + e);
115//                        }
116//                    }
117//                }
118                // END android-removed
119                if (!loaded) {
120                    registerDefaultProviders();
121                }
122                Engine.door = new SecurityDoor();
123                return null;
124            }
125        });
126    }
127
128    /**
129     * This class can't be instantiated.
130     */
131    private Security() {
132    }
133
134    // Register default providers
135    private static void registerDefaultProviders() {
136        secprops.put("security.provider.1", "org.apache.harmony.security.provider.cert.DRLCertFactory");  //$NON-NLS-1$ //$NON-NLS-2$
137        secprops.put("security.provider.2", "org.apache.harmony.security.provider.crypto.CryptoProvider");  //$NON-NLS-1$ //$NON-NLS-2$
138        secprops.put("security.provider.3", "org.apache.harmony.xnet.provider.jsse.JSSEProvider");  //$NON-NLS-1$ //$NON-NLS-2$
139        secprops.put("security.provider.4", "org.bouncycastle.jce.provider.BouncyCastleProvider");  //$NON-NLS-1$ //$NON-NLS-2$
140    }
141
142    /**
143     * Returns value for the specified algorithm with the specified name.
144     *
145     * @param algName
146     *            the name of the algorithm.
147     * @param propName
148     *            the name of the property.
149     * @return value of the property.
150     * @deprecated Use {@link AlgorithmParameters} and {@link KeyFactory}
151     *             instead.
152     */
153    @Deprecated
154    public static String getAlgorithmProperty(String algName, String propName) {
155        if (algName == null || propName == null) {
156            return null;
157        }
158        // BEGIN android-changed
159        String prop = "Alg." + propName + "." + algName; //$NON-NLS-1$
160        // END android-changed
161        Provider[] providers = getProviders();
162        for (int i = 0; i < providers.length; i++) {
163            for (Enumeration e = providers[i].propertyNames(); e
164                    .hasMoreElements();) {
165                String pname = (String) e.nextElement();
166                if (Util.equalsIgnoreCase(prop, pname)) {
167                    return providers[i].getProperty(pname);
168                }
169            }
170        }
171        return null;
172    }
173
174    /**
175     * Insert the given {@code Provider} at the specified {@code position}. The
176     * positions define the preference order in which providers are searched for
177     * requested algorithms.
178     * <p>
179     * If a {@code SecurityManager} is installed, code calling this method needs
180     * the {@code SecurityPermission} {@code insertProvider.NAME} (where NAME is
181     * the provider name) to be granted, otherwise a {@code SecurityException}
182     * will be thrown.
183     *
184     * @param provider
185     *            the provider to insert.
186     * @param position
187     *            the position (starting from 1).
188     * @return the actual position or {@code -1} if the given {@code provider}
189     *         was already in the list. The actual position may be different
190     *         from the desired position.
191     * @throws SecurityException
192     *             if a {@code SecurityManager} is installed and the caller does
193     *             not have permission to invoke this method.
194     */
195    public static synchronized int insertProviderAt(Provider provider,
196            int position) {
197        // check security access; check that provider is not already
198        // installed, else return -1; if (position <1) or (position > max
199        // position) position = max position + 1; insert provider, shift up
200        // one position for next providers; Note: The position is 1-based
201        SecurityManager sm = System.getSecurityManager();
202        if (sm != null) {
203            sm.checkSecurityAccess("insertProvider." + provider.getName()); //$NON-NLS-1$
204        }
205        if (getProvider(provider.getName()) != null) {
206            return -1;
207        }
208        int result = Services.insertProviderAt(provider, position);
209        renumProviders();
210        return result;
211    }
212
213    /**
214     * Adds the given {@code provider} to the collection of providers at the
215     * next available position.
216     * <p>
217     * If a {@code SecurityManager} is installed, code calling this method needs
218     * the {@code SecurityPermission} {@code insertProvider.NAME} (where NAME is
219     * the provider name) to be granted, otherwise a {@code SecurityException}
220     * will be thrown.
221     *
222     * @param provider
223     *            the provider to be added.
224     * @return the actual position or {@code -1} if the given {@code provider}
225     *         was already in the list.
226     * @throws SecurityException
227     *             if a {@code SecurityManager} is installed and the caller does
228     *             not have permission to invoke this method.
229     */
230    public static int addProvider(Provider provider) {
231        return insertProviderAt(provider, 0);
232    }
233
234    /**
235     * Removes the {@code Provider} with the specified name form the collection
236     * of providers. If the the {@code Provider} with the specified name is
237     * removed, all provider at a greater position are shifted down one
238     * position.
239     * <p>
240     * Returns silently if {@code name} is {@code null} or no provider with the
241     * specified name is installed.
242     * <p>
243     * If a {@code SecurityManager} is installed, code calling this method needs
244     * the {@code SecurityPermission} {@code removeProvider.NAME} (where NAME is
245     * the provider name) to be granted, otherwise a {@code SecurityException}
246     * will be thrown.
247     *
248     * @param name
249     *            the name of the provider to remove.
250     * @throws SecurityException
251     *             if a {@code SecurityManager} is installed and the caller does
252     *             not have permission to invoke this method.
253     */
254    public static synchronized void removeProvider(String name) {
255        // It is not clear from spec.:
256        // 1. if name is null, should we checkSecurityAccess or not?
257        //    throw SecurityException or not?
258        // 2. as 1 but provider is not installed
259        // 3. behavior if name is empty string?
260
261        Provider p;
262        if ((name == null) || (name.length() == 0)) {
263            return;
264        }
265        p = getProvider(name);
266        if (p == null) {
267            return;
268        }
269        SecurityManager sm = System.getSecurityManager();
270        if (sm != null) {
271            sm.checkSecurityAccess("removeProvider." + name); //$NON-NLS-1$
272        }
273        Services.removeProvider(p.getProviderNumber());
274        renumProviders();
275        p.setProviderNumber(-1);
276    }
277
278    /**
279     * Returns an array containing all installed providers. The providers are
280     * ordered according their preference order.
281     *
282     * @return an array containing all installed providers.
283     */
284    public static synchronized Provider[] getProviders() {
285        return Services.getProviders();
286    }
287
288    /**
289     * Returns the {@code Provider} with the specified name. Returns {@code
290     * null} if name is {@code null} or no provider with the specified name is
291     * installed.
292     *
293     * @param name
294     *            the name of the requested provider.
295     * @return the provider with the specified name, maybe {@code null}.
296     */
297    public static synchronized Provider getProvider(String name) {
298        return Services.getProvider(name);
299    }
300
301    /**
302     * Returns the array of providers which meet the user supplied string
303     * filter. The specified filter must be supplied in one of two formats:
304     * <nl>
305     * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
306     * <p>
307     * (for example: "MessageDigest.SHA")
308     * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
309     * ATTR_NAME:ATTR_VALUE
310     * <p>
311     * (for example: "Signature.MD2withRSA KeySize:512")
312     * </nl>
313     *
314     * @param filter
315     *            case-insensitive filter.
316     * @return the providers which meet the user supplied string filter {@code
317     *         filter}. A {@code null} value signifies that none of the
318     *         installed providers meets the filter specification.
319     * @throws InvalidParameterException
320     *             if an unusable filter is supplied.
321     * @throws NullPointerException
322     *             if {@code filter} is {@code null}.
323     */
324    public static Provider[] getProviders(String filter) {
325        if (filter == null) {
326            throw new NullPointerException(Messages.getString("security.2A")); //$NON-NLS-1$
327        }
328        if (filter.length() == 0) {
329            throw new InvalidParameterException(
330                    Messages.getString("security.2B")); //$NON-NLS-1$
331        }
332        HashMap<String, String> hm = new HashMap<String, String>();
333        int i = filter.indexOf(':');
334        if ((i == filter.length() - 1) || (i == 0)) {
335            throw new InvalidParameterException(
336                    Messages.getString("security.2B")); //$NON-NLS-1$
337        }
338        if (i < 1) {
339            hm.put(filter, ""); //$NON-NLS-1$
340        } else {
341            hm.put(filter.substring(0, i), filter.substring(i + 1));
342        }
343        return getProviders(hm);
344    }
345
346    /**
347     * Returns the array of providers which meet the user supplied set of
348     * filters. The filter must be supplied in one of two formats:
349     * <nl>
350     * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
351     * <p>
352     * for example: "MessageDigest.SHA" The value associated with the key must
353     * be an empty string. <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
354     * ATTR_NAME:ATTR_VALUE
355     * <p>
356     * for example: "Signature.MD2withRSA KeySize:512" where "KeySize:512" is
357     * the value of the filter map entry.
358     * </nl>
359     *
360     * @param filter
361     *            case-insensitive filter.
362     * @return the providers which meet the user supplied string filter {@code
363     *         filter}. A {@code null} value signifies that none of the
364     *         installed providers meets the filter specification.
365     * @throws InvalidParameterException
366     *             if an unusable filter is supplied.
367     * @throws NullPointerException
368     *             if {@code filter} is {@code null}.
369     */
370    public static synchronized Provider[] getProviders(Map<String,String> filter) {
371        if (filter == null) {
372            throw new NullPointerException(Messages.getString("security.2A")); //$NON-NLS-1$
373        }
374        if (filter.isEmpty()) {
375            return null;
376        }
377        java.util.List<Provider> result = Services.getProvidersList();
378        Set<Entry<String, String>> keys = filter.entrySet();
379        Map.Entry<String, String> entry;
380        for (Iterator<Entry<String, String>> it = keys.iterator(); it.hasNext();) {
381            entry = it.next();
382            String key = entry.getKey();
383            String val = entry.getValue();
384            String attribute = null;
385            int i = key.indexOf(' ');
386            int j = key.indexOf('.');
387            if (j == -1) {
388                throw new InvalidParameterException(
389                        Messages.getString("security.2B")); //$NON-NLS-1$
390            }
391            if (i == -1) { // <crypto_service>.<algorithm_or_type>
392                if (val.length() != 0) {
393                    throw new InvalidParameterException(
394                            Messages.getString("security.2B")); //$NON-NLS-1$
395                }
396            } else { // <crypto_service>.<algorithm_or_type> <attribute_name>
397                if (val.length() == 0) {
398                    throw new InvalidParameterException(
399                            Messages.getString("security.2B")); //$NON-NLS-1$
400                }
401                attribute = key.substring(i + 1);
402                if (attribute.trim().length() == 0) {
403                    throw new InvalidParameterException(
404                            Messages.getString("security.2B")); //$NON-NLS-1$
405                }
406                key = key.substring(0, i);
407            }
408            String serv = key.substring(0, j);
409            String alg = key.substring(j + 1);
410            if (serv.length() == 0 || alg.length() == 0) {
411                throw new InvalidParameterException(
412                        Messages.getString("security.2B")); //$NON-NLS-1$
413            }
414            Provider p;
415            for (int k = 0; k < result.size(); k++) {
416                try {
417                    p = result.get(k);
418                } catch (IndexOutOfBoundsException e) {
419                    break;
420                }
421                if (!p.implementsAlg(serv, alg, attribute, val)) {
422                    result.remove(p);
423                    k--;
424                }
425            }
426        }
427        if (result.size() > 0) {
428            return result.toArray(new Provider[result.size()]);
429        }
430        return null;
431    }
432
433    /**
434     * Returns the value of the security property named by the argument.
435     * <p>
436     * If a {@code SecurityManager} is installed, code calling this method needs
437     * the {@code SecurityPermission} {@code getProperty.KEY} (where KEY is the
438     * specified {@code key}) to be granted, otherwise a {@code
439     * SecurityException} will be thrown.
440     *
441     * @param key
442     *            the name of the requested security property.
443     * @return the value of the security property.
444     * @throws SecurityException
445     *             if a {@code SecurityManager} is installed and the caller does
446     *             not have permission to invoke this method.
447     */
448    public static String getProperty(String key) {
449        if (key == null) {
450            throw new NullPointerException(Messages.getString("security.2C")); //$NON-NLS-1$
451        }
452        SecurityManager sm = System.getSecurityManager();
453        if (sm != null) {
454            sm.checkSecurityAccess("getProperty." + key); //$NON-NLS-1$
455        }
456        String property = secprops.getProperty(key);
457        if (property != null) {
458            property = property.trim();
459        }
460        return property;
461    }
462
463    /**
464     * Sets the value of the specified security property.
465     * <p>
466     * If a {@code SecurityManager} is installed, code calling this method needs
467     * the {@code SecurityPermission} {@code setProperty.KEY} (where KEY is the
468     * specified {@code key}) to be granted, otherwise a {@code
469     * SecurityException} will be thrown.
470     *
471     * @param key
472     *            the name of the security property.
473     * @param datnum
474     *            the value of the security property.
475     * @throws SecurityException
476     *             if a {@code SecurityManager} is installed and the caller does
477     *             not have permission to invoke this method.
478     */
479    public static void setProperty(String key, String datnum) {
480        SecurityManager sm = System.getSecurityManager();
481        if (sm != null) {
482            sm.checkSecurityAccess("setProperty." + key); //$NON-NLS-1$
483        }
484        secprops.put(key, datnum);
485    }
486
487    /**
488     * Returns a {@code Set} of all registered algorithms for the specified
489     * cryptographic service. {@code "Signature"}, {@code "Cipher"} and {@code
490     * "KeyStore"} are examples for such kind of services.
491     *
492     * @param serviceName
493     *            the case-insensitive name of the service.
494     * @return a {@code Set} of all registered algorithms for the specified
495     *         cryptographic service, or an empty {@code Set} if {@code
496     *         serviceName} is {@code null} or if no registered provider
497     *         provides the requested service.
498     */
499    public static Set<String> getAlgorithms(String serviceName) {
500        Set<String> result = new HashSet<String>();
501        // BEGIN android-added
502        // compatibility with RI
503        if (serviceName == null) {
504            return result;
505        }
506        // END android-added
507        Provider[] p = getProviders();
508        for (int i = 0; i < p.length; i++) {
509            for (Iterator it = p[i].getServices().iterator(); it.hasNext();) {
510                Provider.Service s = (Provider.Service) it.next();
511                if (Util.equalsIgnoreCase(s.getType(),serviceName)) {
512                    result.add(s.getAlgorithm());
513                }
514            }
515        }
516        return result;
517    }
518
519    /**
520     *
521     * Update sequence numbers of all providers.
522     *
523     */
524    private static void renumProviders() {
525        Provider[] p = Services.getProviders();
526        for (int i = 0; i < p.length; i++) {
527            p[i].setProviderNumber(i + 1);
528        }
529    }
530
531    private static class SecurityDoor implements SecurityAccess {
532        // Access to Security.renumProviders()
533        public void renumProviders() {
534            Security.renumProviders();
535        }
536
537        //  Access to Security.getAliases()
538        public Iterator<String> getAliases(Provider.Service s) {
539            return s.getAliases();
540        }
541
542        // Access to Provider.getService()
543        public Provider.Service getService(Provider p, String type) {
544            return p.getService(type);
545        }
546    }
547}
548