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