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