1 /* Security.java --- Java base security class implementation
2 Copyright (C) 1999, 2001, 2002, 2003, Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package java
.security
;
40 import java
.io
.FileNotFoundException
;
41 import java
.io
.InputStream
;
42 import java
.io
.IOException
;
44 import java
.security
.Provider
;
45 import java
.util
.Collections
;
46 import java
.util
.Enumeration
;
47 import java
.util
.HashMap
;
48 import java
.util
.HashSet
;
49 import java
.util
.Iterator
;
50 import java
.util
.LinkedHashSet
;
52 import java
.util
.Properties
;
54 import java
.util
.Vector
;
57 * This class centralizes all security properties and common security methods.
58 * One of its primary uses is to manage providers.
60 * @author Mark Benvenuto <ivymccough@worldnet.att.net>
62 public final class Security
extends Object
64 private static final String ALG_ALIAS
= "Alg.Alias.";
66 private static Vector providers
= new Vector();
67 private static Properties secprops
= new Properties();
71 String base
= System
.getProperty ("gnu.classpath.home.url");
72 String vendor
= System
.getProperty ("gnu.classpath.vm.shortname");
74 // Try VM specific security file
75 boolean loaded
= loadProviders (base
, vendor
);
77 // Append classpath standard provider if possible
78 if (!loadProviders (base
, "classpath")
80 && providers
.size() == 0)
82 // No providers found and both security files failed to load properly.
84 ("WARNING: could not properly read security provider files:");
86 (" " + base
+ "/security/" + vendor
+ ".security");
88 (" " + base
+ "/security/" + "classpath" + ".security");
90 (" Falling back to standard GNU security provider");
91 providers
.addElement (new gnu
.java
.security
.provider
.Gnu());
95 // This class can't be instantiated.
101 * Tries to load the vender specific security providers from the given
102 * base URL. Returns true if the resource could be read and completely
103 * parsed successfully, false otherwise.
105 private static boolean loadProviders(String baseUrl
, String vendor
)
107 if (baseUrl
== null || vendor
== null)
110 boolean result
= true;
111 String secfilestr
= baseUrl
+ "/security/" + vendor
+ ".security";
114 InputStream fin
= new URL(secfilestr
).openStream();
119 while ((name
= secprops
.getProperty("security.provider." + i
)) != null)
121 Exception exception
= null;
124 providers
.addElement(Class
.forName(name
).newInstance());
126 catch (ClassNotFoundException x
)
130 catch (InstantiationException x
)
134 catch (IllegalAccessException x
)
139 if (exception
!= null)
141 System
.err
.println ("WARNING: Error loading security provider "
142 + name
+ ": " + exception
);
148 catch (IOException ignored
)
157 * Gets a specified property for an algorithm. The algorithm name should be a
158 * standard name. See Appendix A in the Java Cryptography Architecture API
159 * Specification & Reference for information about standard algorithm
160 * names. One possible use is by specialized algorithm parsers, which may map
161 * classes to algorithms which they understand (much like {@link Key} parsers
164 * @param algName the algorithm name.
165 * @param propName the name of the property to get.
166 * @return the value of the specified property.
167 * @deprecated This method used to return the value of a proprietary property
168 * in the master file of the "SUN" Cryptographic Service Provider in order to
169 * determine how to parse algorithm-specific parameters. Use the new
170 * provider-based and algorithm-independent {@link AlgorithmParameters} and
171 * {@link KeyFactory} engine classes (introduced in the Java 2 platform)
174 public static String
getAlgorithmProperty(String algName
, String propName
)
176 if (algName
== null || propName
== null)
179 String property
= String
.valueOf(propName
) + "." + String
.valueOf(algName
);
181 for (Iterator i
= providers
.iterator(); i
.hasNext(); )
183 p
= (Provider
) i
.next();
184 for (Iterator j
= p
.keySet().iterator(); j
.hasNext(); )
186 String key
= (String
) j
.next();
187 if (key
.equalsIgnoreCase(property
))
188 return p
.getProperty(key
);
195 * <p>Adds a new provider, at a specified position. The position is the
196 * preference order in which providers are searched for requested algorithms.
197 * Note that it is not guaranteed that this preference will be respected. The
198 * position is 1-based, that is, <code>1</code> is most preferred, followed by
199 * <code>2</code>, and so on.</p>
201 * <p>If the given provider is installed at the requested position, the
202 * provider that used to be at that position, and all providers with a
203 * position greater than position, are shifted up one position (towards the
204 * end of the list of installed providers).</p>
206 * <p>A provider cannot be added if it is already installed.</p>
208 * <p>First, if there is a security manager, its <code>checkSecurityAccess()
209 * </code> method is called with the string <code>"insertProvider."+provider.
210 * getName()</code> to see if it's ok to add a new provider. If the default
211 * implementation of <code>checkSecurityAccess()</code> is used (i.e., that
212 * method is not overriden), then this will result in a call to the security
213 * manager's <code>checkPermission()</code> method with a
214 * <code>SecurityPermission("insertProvider."+provider.getName())</code>
217 * @param provider the provider to be added.
218 * @param position the preference position that the caller would like for
220 * @return the actual preference position in which the provider was added, or
221 * <code>-1</code> if the provider was not added because it is already
223 * @throws SecurityException if a security manager exists and its
224 * {@link SecurityManager#checkSecurityAccess(String)} method denies access
225 * to add a new provider.
226 * @see #getProvider(String)
227 * @see #removeProvider(String)
228 * @see SecurityPermission
230 public static int insertProviderAt(Provider provider
, int position
)
232 SecurityManager sm
= System
.getSecurityManager();
234 sm
.checkSecurityAccess("insertProvider." + provider
.getName());
237 int max
= providers
.size ();
238 for (int i
= 0; i
< max
; i
++)
240 if (((Provider
) providers
.elementAt(i
)).getName() == provider
.getName())
249 providers
.insertElementAt(provider
, position
);
255 * <p>Adds a provider to the next position available.</p>
257 * <p>First, if there is a security manager, its <code>checkSecurityAccess()
258 * </code> method is called with the string <code>"insertProvider."+provider.
259 * getName()</code> to see if it's ok to add a new provider. If the default
260 * implementation of <code>checkSecurityAccess()</code> is used (i.e., that
261 * method is not overriden), then this will result in a call to the security
262 * manager's <code>checkPermission()</code> method with a
263 * <code>SecurityPermission("insertProvider."+provider.getName())</code>
266 * @param provider the provider to be added.
267 * @return the preference position in which the provider was added, or
268 * <code>-1</code> if the provider was not added because it is already
270 * @throws SecurityException if a security manager exists and its
271 * {@link SecurityManager#checkSecurityAccess(String)} method denies access
272 * to add a new provider.
273 * @see #getProvider(String)
274 * @see #removeProvider(String)
275 * @see SecurityPermission
277 public static int addProvider(Provider provider
)
279 return insertProviderAt (provider
, providers
.size () + 1);
283 * <p>Removes the provider with the specified name.</p>
285 * <p>When the specified provider is removed, all providers located at a
286 * position greater than where the specified provider was are shifted down
287 * one position (towards the head of the list of installed providers).</p>
289 * <p>This method returns silently if the provider is not installed.</p>
291 * <p>First, if there is a security manager, its <code>checkSecurityAccess()
292 * </code> method is called with the string <code>"removeProvider."+name</code>
293 * to see if it's ok to remove the provider. If the default implementation of
294 * <code>checkSecurityAccess()</code> is used (i.e., that method is not
295 * overriden), then this will result in a call to the security manager's
296 * <code>checkPermission()</code> method with a <code>SecurityPermission(
297 * "removeProvider."+name)</code> permission.</p>
299 * @param name the name of the provider to remove.
300 * @throws SecurityException if a security manager exists and its
301 * {@link SecurityManager#checkSecurityAccess(String)} method denies access
302 * to remove the provider.
303 * @see #getProvider(String)
304 * @see #addProvider(Provider)
306 public static void removeProvider(String name
)
308 SecurityManager sm
= System
.getSecurityManager();
310 sm
.checkSecurityAccess("removeProvider." + name
);
312 int max
= providers
.size ();
313 for (int i
= 0; i
< max
; i
++)
315 if (((Provider
) providers
.elementAt(i
)).getName() == name
)
324 * Returns an array containing all the installed providers. The order of the
325 * providers in the array is their preference order.
327 * @return an array of all the installed providers.
329 public static Provider
[] getProviders()
331 Provider array
[] = new Provider
[providers
.size ()];
332 providers
.copyInto (array
);
337 * Returns the provider installed with the specified name, if any. Returns
338 * <code>null</code> if no provider with the specified name is installed.
340 * @param name the name of the provider to get.
341 * @return the provider of the specified name.
342 * @see #removeProvider(String)
343 * @see #addProvider(Provider)
345 public static Provider
getProvider(String name
)
348 int max
= providers
.size ();
349 for (int i
= 0; i
< max
; i
++)
351 p
= (Provider
) providers
.elementAt(i
);
352 if (p
.getName() == name
)
359 * <p>Gets a security property value.</p>
361 * <p>First, if there is a security manager, its <code>checkPermission()</code>
362 * method is called with a <code>SecurityPermission("getProperty."+key)</code>
363 * permission to see if it's ok to retrieve the specified security property
366 * @param key the key of the property being retrieved.
367 * @return the value of the security property corresponding to key.
368 * @throws SecurityException if a security manager exists and its
369 * {@link SecurityManager#checkPermission(Permission)} method denies access
370 * to retrieve the specified security property value.
371 * @see #setProperty(String, String)
372 * @see SecurityPermission
374 public static String
getProperty(String key
)
376 SecurityManager sm
= System
.getSecurityManager();
378 sm
.checkSecurityAccess("getProperty." + key
);
380 return secprops
.getProperty(key
);
384 * <p>Sets a security property value.</p>
386 * <p>First, if there is a security manager, its <code>checkPermission()</code>
387 * method is called with a <code>SecurityPermission("setProperty."+key)</code>
388 * permission to see if it's ok to set the specified security property value.
391 * @param key the name of the property to be set.
392 * @param datnum the value of the property to be set.
393 * @throws SecurityException if a security manager exists and its
394 * {@link SecurityManager#checkPermission(Permission)} method denies access
395 * to set the specified security property value.
396 * @see #getProperty(String)
397 * @see SecurityPermission
399 public static void setProperty(String key
, String datnum
)
401 SecurityManager sm
= System
.getSecurityManager();
403 sm
.checkSecurityAccess("setProperty." + key
);
405 secprops
.put(key
, datnum
);
409 * Returns a Set of Strings containing the names of all available algorithms
410 * or types for the specified Java cryptographic service (e.g., Signature,
411 * MessageDigest, Cipher, Mac, KeyStore). Returns an empty Set if there is no
412 * provider that supports the specified service. For a complete list of Java
413 * cryptographic services, please see the Java Cryptography Architecture API
414 * Specification & Reference. Note: the returned set is immutable.
416 * @param serviceName the name of the Java cryptographic service (e.g.,
417 * Signature, MessageDigest, Cipher, Mac, KeyStore). Note: this parameter is
419 * @return a Set of Strings containing the names of all available algorithms
420 * or types for the specified Java cryptographic service or an empty set if
421 * no provider supports the specified service.
424 public static Set
getAlgorithms(String serviceName
)
426 HashSet result
= new HashSet();
427 if (serviceName
== null || serviceName
.length() == 0)
430 serviceName
= serviceName
.trim();
431 if (serviceName
.length() == 0)
434 serviceName
= serviceName
.toUpperCase()+".";
435 Provider
[] providers
= getProviders();
437 for (int i
= 0; i
< providers
.length
; i
++)
438 for (Enumeration e
= providers
[i
].propertyNames(); e
.hasMoreElements(); )
440 String service
= ((String
) e
.nextElement()).trim();
441 if (service
.toUpperCase().startsWith(serviceName
))
443 service
= service
.substring(serviceName
.length()).trim();
444 ndx
= service
.indexOf(' '); // get rid of attributes
446 service
= service
.substring(0, ndx
);
450 return Collections
.unmodifiableSet(result
);
454 * <p>Returns an array containing all installed providers that satisfy the
455 * specified selection criterion, or <code>null</code> if no such providers
456 * have been installed. The returned providers are ordered according to their
457 * preference order.</p>
459 * <p>A cryptographic service is always associated with a particular
460 * algorithm or type. For example, a digital signature service is always
461 * associated with a particular algorithm (e.g., <i>DSA</i>), and a
462 * CertificateFactory service is always associated with a particular
463 * certificate type (e.g., <i>X.509</i>).</p>
465 * <p>The selection criterion must be specified in one of the following two
469 * <li><p><crypto_service>.<algorithm_or_type></p>
470 * <p>The cryptographic service name must not contain any dots.</p>
471 * <p>A provider satisfies the specified selection criterion iff the
472 * provider implements the specified algorithm or type for the specified
473 * cryptographic service.</p>
474 * <p>For example, "CertificateFactory.X.509" would be satisfied by any
475 * provider that supplied a CertificateFactory implementation for X.509
476 * certificates.</p></li>
478 * <li><p><crypto_service>.<algorithm_or_type> <attribute_name>:<attribute_value></p>
479 * <p>The cryptographic service name must not contain any dots. There must
480 * be one or more space charaters between the the <algorithm_or_type>
481 * and the <attribute_name>.</p>
482 * <p>A provider satisfies this selection criterion iff the provider
483 * implements the specified algorithm or type for the specified
484 * cryptographic service and its implementation meets the constraint
485 * expressed by the specified attribute name/value pair.</p>
486 * <p>For example, "Signature.SHA1withDSA KeySize:1024" would be satisfied
487 * by any provider that implemented the SHA1withDSA signature algorithm
488 * with a keysize of 1024 (or larger).</p></li>
491 * <p>See Appendix A in the Java Cryptogaphy Architecture API Specification
492 * & Reference for information about standard cryptographic service names,
493 * standard algorithm names and standard attribute names.</p>
495 * @param filter the criterion for selecting providers. The filter is case-
497 * @return all the installed providers that satisfy the selection criterion,
498 * or null if no such providers have been installed.
499 * @throws InvalidParameterException if the filter is not in the required
501 * @see #getProviders(Map)
503 public static Provider
[] getProviders(String filter
)
505 if (providers
== null || providers
.isEmpty())
508 if (filter
== null || filter
.length() == 0)
509 return getProviders();
511 HashMap map
= new HashMap(1);
512 int i
= filter
.indexOf(':');
513 if (i
== -1) // <service>.<algorithm>
515 else // <service>.<algorithm> <attribute>:<value>
516 map
.put(filter
.substring(0, i
), filter
.substring(i
+1));
518 return getProviders(map
);
522 * <p>Returns an array containing all installed providers that satisfy the
523 * specified selection criteria, or <code>null</code> if no such providers
524 * have been installed. The returned providers are ordered according to their
525 * preference order.</p>
527 * <p>The selection criteria are represented by a map. Each map entry
528 * represents a selection criterion. A provider is selected iff it satisfies
529 * all selection criteria. The key for any entry in such a map must be in one
530 * of the following two formats:</p>
533 * <li><p><crypto_service>.<algorithm_or_type></p>
534 * <p>The cryptographic service name must not contain any dots.</p>
535 * <p>The value associated with the key must be an empty string.</p>
536 * <p>A provider satisfies this selection criterion iff the provider
537 * implements the specified algorithm or type for the specified
538 * cryptographic service.</p></li>
540 * <li><p><crypto_service>.<algorithm_or_type> <attribute_name></p>
541 * <p>The cryptographic service name must not contain any dots. There must
542 * be one or more space charaters between the <algorithm_or_type> and
543 * the <attribute_name>.</p>
544 * <p>The value associated with the key must be a non-empty string. A
545 * provider satisfies this selection criterion iff the provider implements
546 * the specified algorithm or type for the specified cryptographic service
547 * and its implementation meets the constraint expressed by the specified
548 * attribute name/value pair.</p></li>
551 * <p>See Appendix A in the Java Cryptogaphy Architecture API Specification
552 * & Reference for information about standard cryptographic service names,
553 * standard algorithm names and standard attribute names.</p>
555 * @param filter the criteria for selecting providers. The filter is case-
557 * @return all the installed providers that satisfy the selection criteria,
558 * or <code>null</code> if no such providers have been installed.
559 * @throws InvalidParameterException if the filter is not in the required
561 * @see #getProviders(String)
563 public static Provider
[] getProviders(Map filter
)
565 if (providers
== null || providers
.isEmpty())
569 return getProviders();
571 Set querries
= filter
.keySet();
572 if (querries
== null || querries
.isEmpty())
573 return getProviders();
575 LinkedHashSet result
= new LinkedHashSet(providers
); // assume all
577 String querry
, service
, algorithm
, attribute
, value
;
578 LinkedHashSet serviceProviders
= new LinkedHashSet(); // preserve insertion order
579 for (Iterator i
= querries
.iterator(); i
.hasNext(); )
581 querry
= (String
) i
.next();
582 if (querry
== null) // all providers
585 querry
= querry
.trim();
586 if (querry
.length() == 0) // all providers
589 dot
= querry
.indexOf('.');
590 if (dot
== -1) // syntax error
591 throw new InvalidParameterException(
592 "missing dot in '" + String
.valueOf(querry
)+"'");
594 value
= (String
) filter
.get(querry
);
595 // deconstruct querry into [service, algorithm, attribute]
596 if (value
== null || value
.trim().length() == 0) // <service>.<algorithm>
600 service
= querry
.substring(0, dot
).trim();
601 algorithm
= querry
.substring(dot
+1).trim();
603 else // <service>.<algorithm> <attribute>
605 ws
= querry
.indexOf(' ');
607 throw new InvalidParameterException(
608 "value (" + String
.valueOf(value
) +
609 ") is not empty, but querry (" + String
.valueOf(querry
) +
610 ") is missing at least one space character");
611 value
= value
.trim();
612 attribute
= querry
.substring(ws
+1).trim();
613 // was the dot in the attribute?
614 if (attribute
.indexOf('.') != -1)
615 throw new InvalidParameterException(
616 "attribute_name (" + String
.valueOf(attribute
) +
617 ") in querry (" + String
.valueOf(querry
) + ") contains a dot");
619 querry
= querry
.substring(0, ws
).trim();
620 service
= querry
.substring(0, dot
).trim();
621 algorithm
= querry
.substring(dot
+1).trim();
624 // service and algorithm must not be empty
625 if (service
.length() == 0)
626 throw new InvalidParameterException(
627 "<crypto_service> in querry (" + String
.valueOf(querry
) +
630 if (algorithm
.length() == 0)
631 throw new InvalidParameterException(
632 "<algorithm_or_type> in querry (" + String
.valueOf(querry
) +
635 selectProviders(service
, algorithm
, attribute
, value
, result
, serviceProviders
);
636 result
.retainAll(serviceProviders
); // eval next retaining found providers
637 if (result
.isEmpty()) // no point continuing
641 if (result
.isEmpty())
644 return (Provider
[]) result
.toArray(new Provider
[0]);
647 private static void selectProviders(String svc
, String algo
, String attr
,
648 String val
, LinkedHashSet providerSet
,
649 LinkedHashSet result
)
651 result
.clear(); // ensure we start with an empty result set
652 for (Iterator i
= providerSet
.iterator(); i
.hasNext(); )
654 Provider p
= (Provider
) i
.next();
655 if (provides(p
, svc
, algo
, attr
, val
))
660 private static boolean provides(Provider p
, String svc
, String algo
,
661 String attr
, String val
)
664 String serviceDotAlgorithm
= null;
667 boolean found
= false;
668 // if <svc>.<algo> <attr> is in the set then so is <svc>.<algo>
669 // but it may be stored under an alias <algo>. resolve
670 outer
: for (int r
= 0; r
< 3; r
++) // guard against circularity
672 serviceDotAlgorithm
= (svc
+"."+String
.valueOf(algo
)).trim();
673 inner
: for (it
= p
.keySet().iterator(); it
.hasNext(); )
675 key
= (String
) it
.next();
676 if (key
.equalsIgnoreCase(serviceDotAlgorithm
)) // eureka
681 // it may be there but as an alias
682 if (key
.equalsIgnoreCase(ALG_ALIAS
+ serviceDotAlgorithm
))
684 algo
= p
.getProperty(key
);
687 // else continue inner
694 // found a candidate for the querry. do we have an attr to match?
695 if (val
== null) // <service>.<algorithm> querry
698 // <service>.<algorithm> <attribute>; find the key entry that match
700 int limit
= serviceDotAlgorithm
.length() + 1;
701 for (it
= p
.keySet().iterator(); it
.hasNext(); )
703 key
= (String
) it
.next();
704 if (key
.length() <= limit
)
707 if (key
.substring(0, limit
).equalsIgnoreCase(serviceDotAlgorithm
+" "))
709 realAttr
= key
.substring(limit
).trim();
710 if (! realAttr
.equalsIgnoreCase(attr
))
713 // eveything matches so far. do the value
714 realVal
= p
.getProperty(key
);
718 realVal
= realVal
.trim();
719 // is it a string value?
720 if (val
.equalsIgnoreCase(realVal
))
723 // assume value is a number. cehck for greater-than-or-equal
724 return (new Integer(val
).intValue() >= new Integer(realVal
).intValue());