1 /* Security.java --- Java base security class implementation
2 Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
40 package java
.security
;
42 import gnu
.classpath
.SystemProperties
;
44 import gnu
.classpath
.Configuration
;
45 // GCJ LOCAL - We don't have VMStackWalker yet.
46 // import gnu.classpath.VMStackWalker;
48 import java
.io
.IOException
;
49 import java
.io
.InputStream
;
51 import java
.util
.Collections
;
52 import java
.util
.Enumeration
;
53 import java
.util
.HashMap
;
54 import java
.util
.HashSet
;
55 import java
.util
.Iterator
;
56 import java
.util
.LinkedHashSet
;
58 import java
.util
.Properties
;
60 import java
.util
.Vector
;
63 * This class centralizes all security properties and common security methods.
64 * One of its primary uses is to manage security providers.
66 * @author Mark Benvenuto (ivymccough@worldnet.att.net)
68 public final class Security
70 private static final String ALG_ALIAS
= "Alg.Alias.";
72 private static Vector providers
= new Vector();
73 private static Properties secprops
= new Properties();
77 String base
= SystemProperties
.getProperty("gnu.classpath.home.url");
78 String vendor
= SystemProperties
.getProperty("gnu.classpath.vm.shortname");
80 // Try VM specific security file
81 boolean loaded
= loadProviders (base
, vendor
);
83 // Append classpath standard provider if possible
84 if (!loadProviders (base
, "classpath")
86 && providers
.size() == 0)
88 if (Configuration
.DEBUG
)
90 /* No providers found and both security files failed to
91 * load properly. Give a warning in case of DEBUG is
92 * enabled. Could be done with java.util.logging later.
95 ("WARNING: could not properly read security provider files:");
97 (" " + base
+ "/security/" + vendor
100 (" " + base
+ "/security/" + "classpath"
103 (" Falling back to standard GNU security provider");
105 // Note that this matches our classpath.security file.
106 providers
.addElement (new gnu
.java
.security
.provider
.Gnu());
107 providers
.addElement(new gnu
.javax
.crypto
.jce
.GnuCrypto());
108 providers
.addElement(new gnu
.javax
.crypto
.jce
.GnuSasl());
109 providers
.addElement(new gnu
.javax
.net
.ssl
.provider
.Jessie());
110 providers
.addElement(new gnu
.javax
.security
.auth
.callback
.GnuCallbacks());
113 // This class can't be instantiated.
119 * Tries to load the vender specific security providers from the given base
120 * URL. Returns true if the resource could be read and completely parsed
121 * successfully, false otherwise.
123 private static boolean loadProviders(String baseUrl
, String vendor
)
125 if (baseUrl
== null || vendor
== null)
128 boolean result
= true;
129 String secfilestr
= baseUrl
+ "/security/" + vendor
+ ".security";
132 InputStream fin
= new URL(secfilestr
).openStream();
137 while ((name
= secprops
.getProperty("security.provider." + i
)) != null)
139 Exception exception
= null;
142 ClassLoader sys
= ClassLoader
.getSystemClassLoader();
143 providers
.addElement(Class
.forName(name
, true, sys
).newInstance());
145 catch (ClassNotFoundException x
)
149 catch (InstantiationException x
)
153 catch (IllegalAccessException x
)
158 if (exception
!= null)
160 System
.err
.println ("WARNING: Error loading security provider "
161 + name
+ ": " + exception
);
167 catch (IOException ignored
)
176 * Returns the value associated to a designated property name for a given
180 * the algorithm name.
182 * the name of the property to return.
183 * @return the value of the specified property or <code>null</code> if none
185 * @deprecated Use the provider-based and algorithm-independent
186 * {@link AlgorithmParameters} and {@link KeyFactory} engine
189 public static String
getAlgorithmProperty(String algName
, String propName
)
191 if (algName
== null || propName
== null)
194 String property
= String
.valueOf(propName
) + "." + String
.valueOf(algName
);
196 for (Iterator i
= providers
.iterator(); i
.hasNext(); )
198 p
= (Provider
) i
.next();
199 for (Iterator j
= p
.keySet().iterator(); j
.hasNext(); )
201 String key
= (String
) j
.next();
202 if (key
.equalsIgnoreCase(property
))
203 return p
.getProperty(key
);
210 * Inserts a new designated {@link Provider} at a designated (1-based)
211 * position in the current list of installed {@link Provider}s,
214 * the new {@link Provider} to add.
216 * the position (starting from 1) of where to install
217 * <code>provider</code>.
218 * @return the actual position, in the list of installed Providers. Returns
219 * <code>-1</code> if <code>provider</code> was laready in the
220 * list. The actual position may be different than the desired
221 * <code>position</code>.
222 * @throws SecurityException
223 * if a {@link SecurityManager} is installed and it disallows this
225 * @see #getProvider(String)
226 * @see #removeProvider(String)
227 * @see SecurityPermission
229 public static int insertProviderAt(Provider provider
, int position
)
231 SecurityManager sm
= System
.getSecurityManager();
233 sm
.checkSecurityAccess("insertProvider." + provider
.getName());
236 int max
= providers
.size ();
237 for (int i
= 0; i
< max
; i
++)
239 if (((Provider
) providers
.elementAt(i
)).getName().equals(provider
.getName()))
248 providers
.insertElementAt(provider
, position
);
254 * Appends the designated new {@link Provider} to the current list of
255 * installed {@link Provider}s.
258 * the new {@link Provider} to append.
259 * @return the position (starting from 1) of <code>provider</code> in the
260 * current list of {@link Provider}s, or <code>-1</code> if
261 * <code>provider</code> was already there.
262 * @throws SecurityException
263 * if a {@link SecurityManager} is installed and it disallows this
265 * @see #getProvider(String)
266 * @see #removeProvider(String)
267 * @see SecurityPermission
269 public static int addProvider(Provider provider
)
271 return insertProviderAt (provider
, providers
.size () + 1);
275 * Removes an already installed {@link Provider}, given its name, from the
276 * current list of installed {@link Provider}s.
279 * the name of an already installed {@link Provider} to remove.
280 * @throws SecurityException
281 * if a {@link SecurityManager} is installed and it disallows this
283 * @see #getProvider(String)
284 * @see #addProvider(Provider)
286 public static void removeProvider(String name
)
288 SecurityManager sm
= System
.getSecurityManager();
290 sm
.checkSecurityAccess("removeProvider." + name
);
292 int max
= providers
.size ();
293 for (int i
= 0; i
< max
; i
++)
295 if (((Provider
) providers
.elementAt(i
)).getName().equals(name
))
304 * Returns the current list of installed {@link Provider}s as an array
305 * ordered according to their installation preference order.
307 * @return an array of all the installed providers.
309 public static Provider
[] getProviders()
311 Provider
[] array
= new Provider
[providers
.size ()];
312 providers
.copyInto (array
);
317 * Returns an already installed {@link Provider} given its name.
320 * the name of an already installed {@link Provider}.
321 * @return the {@link Provider} known by <code>name</code>. Returns
322 * <code>null</code> if the current list of {@link Provider}s does
323 * not include one named <code>name</code>.
324 * @see #removeProvider(String)
325 * @see #addProvider(Provider)
327 public static Provider
getProvider(String name
)
334 if (name
.length() == 0)
338 int max
= providers
.size ();
339 for (int i
= 0; i
< max
; i
++)
341 p
= (Provider
) providers
.elementAt(i
);
342 if (p
.getName().equals(name
))
349 * Returns the value associated with a Security propery.
352 * the key of the property to fetch.
353 * @return the value of the Security property associated with
354 * <code>key</code>. Returns <code>null</code> if no such property
356 * @throws SecurityException
357 * if a {@link SecurityManager} is installed and it disallows this
359 * @see #setProperty(String, String)
360 * @see SecurityPermission
362 public static String
getProperty(String key
)
364 // GCJ LOCAL - We don't have VMStackWalker yet.
365 // XXX To prevent infinite recursion when the SecurityManager calls us,
366 // don't do a security check if the caller is trusted (by virtue of having
367 // been loaded by the bootstrap class loader).
368 SecurityManager sm
= System
.getSecurityManager();
369 // if (sm != null && VMStackWalker.getCallingClassLoader() != null)
371 sm
.checkSecurityAccess("getProperty." + key
);
373 return secprops
.getProperty(key
);
377 * Sets or changes a designated Security property to a designated value.
380 * the name of the property to set.
382 * the new value of the property.
383 * @throws SecurityException
384 * if a {@link SecurityManager} is installed and it disallows this
386 * @see #getProperty(String)
387 * @see SecurityPermission
389 public static void setProperty(String key
, String datum
)
391 SecurityManager sm
= System
.getSecurityManager();
393 sm
.checkSecurityAccess("setProperty." + key
);
396 secprops
.remove(key
);
398 secprops
.put(key
, datum
);
402 * For a given <i>service</i> (e.g. Signature, MessageDigest, etc...) this
403 * method returns the {@link Set} of all available algorithm names (instances
404 * of {@link String}, from all currently installed {@link Provider}s.
407 * the case-insensitive name of a service (e.g. Signature,
408 * MessageDigest, etc).
409 * @return a {@link Set} of {@link String}s containing the names of all
410 * algorithm names provided by all of the currently installed
414 public static Set
getAlgorithms(String serviceName
)
416 HashSet result
= new HashSet();
417 if (serviceName
== null || serviceName
.length() == 0)
420 serviceName
= serviceName
.trim();
421 if (serviceName
.length() == 0)
424 serviceName
= serviceName
.toUpperCase()+".";
425 Provider
[] providers
= getProviders();
427 for (int i
= 0; i
< providers
.length
; i
++)
428 for (Enumeration e
= providers
[i
].propertyNames(); e
.hasMoreElements(); )
430 String service
= ((String
) e
.nextElement()).trim();
431 if (service
.toUpperCase().startsWith(serviceName
))
433 service
= service
.substring(serviceName
.length()).trim();
434 ndx
= service
.indexOf(' '); // get rid of attributes
436 service
= service
.substring(0, ndx
);
440 return Collections
.unmodifiableSet(result
);
444 * Returns an array of currently installed {@link Provider}s, ordered
445 * according to their installation preference order, which satisfy a given
446 * <i>selection</i> criterion.
448 * <p>This implementation recognizes a <i>selection</i> criterion written in
449 * one of two following forms:</p>
452 * <li><crypto_service>.<algorithm_or_type>: Where
453 * <i>crypto_service</i> is a case-insensitive string, similar to what has
454 * been described in the {@link #getAlgorithms(String)} method, and
455 * <i>algorithm_or_type</i> is a known case-insensitive name of an
456 * Algorithm, or one of its aliases.
458 * <p>For example, "CertificateFactory.X.509" would return all the installed
459 * {@link Provider}s which provide a <i>CertificateFactory</i>
460 * implementation of <i>X.509</i>.</p></li>
462 * <li><crypto_service>.<algorithm_or_type> <attribute_name>:<value>:
463 * Where <i>crypto_service</i> is a case-insensitive string, similar to what
464 * has been described in the {@link #getAlgorithms(String)} method,
465 * <i>algorithm_or_type</i> is a case-insensitive known name of an Algorithm
466 * or one of its aliases, <i>attribute_name</i> is a case-insensitive
467 * property name with no whitespace characters, and no dots, in-between, and
468 * <i>value</i> is a {@link String} with no whitespace characters in-between.
470 * <p>For example, "Signature.Sha1WithDSS KeySize:1024" would return all the
471 * installed {@link Provider}s which declared their ability to provide
472 * <i>Signature</i> services, using the <i>Sha1WithDSS</i> algorithm with
473 * key sizes of <i>1024</i>.</p></li>
477 * the <i>selection</i> criterion for selecting among the installed
479 * @return all the installed {@link Provider}s which satisfy the <i>selection</i>
480 * criterion. Returns <code>null</code> if no installed
481 * {@link Provider}s were found which satisfy the <i>selection</i>
482 * criterion. Returns ALL installed {@link Provider}s if
483 * <code>filter</code> is <code>null</code> or is an empty string.
484 * @throws InvalidParameterException
485 * if an exception occurs while parsing the <code>filter</code>.
486 * @see #getProviders(Map)
488 public static Provider
[] getProviders(String filter
)
490 if (providers
== null || providers
.isEmpty())
493 if (filter
== null || filter
.length() == 0)
494 return getProviders();
496 HashMap map
= new HashMap(1);
497 int i
= filter
.indexOf(':');
498 if (i
== -1) // <service>.<algorithm>
500 else // <service>.<algorithm> <attribute>:<value>
501 map
.put(filter
.substring(0, i
), filter
.substring(i
+1));
503 return getProviders(map
);
507 * Returns an array of currently installed {@link Provider}s which satisfy a
508 * set of <i>selection</i> criteria.
510 * <p>The <i>selection</i> criteria are defined in a {@link Map} where each
511 * element specifies a <i>selection</i> querry. The <i>Keys</i> in this
512 * {@link Map} must be in one of the two following forms:</p>
515 * <li><crypto_service>.<algorithm_or_type>: Where
516 * <i>crypto_service</i> is a case-insensitive string, similar to what has
517 * been described in the {@link #getAlgorithms(String)} method, and
518 * <i>algorithm_or_type</i> is a case-insensitive known name of an
519 * Algorithm, or one of its aliases. The <i>value</i> of the entry in the
520 * {@link Map} for such a <i>Key</i> MUST be the empty string.
521 * {@link Provider}s which provide an implementation for the designated
522 * <i>service algorithm</i> are included in the result.</li>
524 * <li><crypto_service>.<algorithm_or_type> <attribute_name>:
525 * Where <i>crypto_service</i> is a case-insensitive string, similar to what
526 * has been described in the {@link #getAlgorithms(String)} method,
527 * <i>algorithm_or_type</i> is a case-insensitive known name of an Algorithm
528 * or one of its aliases, and <i>attribute_name</i> is a case-insensitive
529 * property name with no whitespace characters, and no dots, in-between. The
530 * <i>value</i> of the entry in this {@link Map} for such a <i>Key</i> MUST
531 * NOT be <code>null</code> or an empty string. {@link Provider}s which
532 * declare the designated <i>attribute_name</i> and <i>value</i> for the
533 * designated <i>service algorithm</i> are included in the result.</li>
537 * a {@link Map} of <i>selection querries</i>.
538 * @return all currently installed {@link Provider}s which satisfy ALL the
539 * <i>selection</i> criteria defined in <code>filter</code>.
540 * Returns ALL installed {@link Provider}s if <code>filter</code>
541 * is <code>null</code> or empty.
542 * @throws InvalidParameterException
543 * if an exception is encountered while parsing the syntax of the
544 * {@link Map}'s <i>keys</i>.
545 * @see #getProviders(String)
547 public static Provider
[] getProviders(Map filter
)
549 if (providers
== null || providers
.isEmpty())
553 return getProviders();
555 Set querries
= filter
.keySet();
556 if (querries
== null || querries
.isEmpty())
557 return getProviders();
559 LinkedHashSet result
= new LinkedHashSet(providers
); // assume all
561 String querry
, service
, algorithm
, attribute
, value
;
562 LinkedHashSet serviceProviders
= new LinkedHashSet(); // preserve insertion order
563 for (Iterator i
= querries
.iterator(); i
.hasNext(); )
565 querry
= (String
) i
.next();
566 if (querry
== null) // all providers
569 querry
= querry
.trim();
570 if (querry
.length() == 0) // all providers
573 dot
= querry
.indexOf('.');
574 if (dot
== -1) // syntax error
575 throw new InvalidParameterException(
576 "missing dot in '" + String
.valueOf(querry
)+"'");
578 value
= (String
) filter
.get(querry
);
579 // deconstruct querry into [service, algorithm, attribute]
580 if (value
== null || value
.trim().length() == 0) // <service>.<algorithm>
584 service
= querry
.substring(0, dot
).trim();
585 algorithm
= querry
.substring(dot
+1).trim();
587 else // <service>.<algorithm> <attribute>
589 ws
= querry
.indexOf(' ');
591 throw new InvalidParameterException(
592 "value (" + String
.valueOf(value
) +
593 ") is not empty, but querry (" + String
.valueOf(querry
) +
594 ") is missing at least one space character");
595 value
= value
.trim();
596 attribute
= querry
.substring(ws
+1).trim();
597 // was the dot in the attribute?
598 if (attribute
.indexOf('.') != -1)
599 throw new InvalidParameterException(
600 "attribute_name (" + String
.valueOf(attribute
) +
601 ") in querry (" + String
.valueOf(querry
) + ") contains a dot");
603 querry
= querry
.substring(0, ws
).trim();
604 service
= querry
.substring(0, dot
).trim();
605 algorithm
= querry
.substring(dot
+1).trim();
608 // service and algorithm must not be empty
609 if (service
.length() == 0)
610 throw new InvalidParameterException(
611 "<crypto_service> in querry (" + String
.valueOf(querry
) +
614 if (algorithm
.length() == 0)
615 throw new InvalidParameterException(
616 "<algorithm_or_type> in querry (" + String
.valueOf(querry
) +
619 selectProviders(service
, algorithm
, attribute
, value
, result
, serviceProviders
);
620 result
.retainAll(serviceProviders
); // eval next retaining found providers
621 if (result
.isEmpty()) // no point continuing
625 if (result
.isEmpty())
628 return (Provider
[]) result
.toArray(new Provider
[result
.size()]);
631 private static void selectProviders(String svc
, String algo
, String attr
,
632 String val
, LinkedHashSet providerSet
,
633 LinkedHashSet result
)
635 result
.clear(); // ensure we start with an empty result set
636 for (Iterator i
= providerSet
.iterator(); i
.hasNext(); )
638 Provider p
= (Provider
) i
.next();
639 if (provides(p
, svc
, algo
, attr
, val
))
644 private static boolean provides(Provider p
, String svc
, String algo
,
645 String attr
, String val
)
648 String serviceDotAlgorithm
= null;
651 boolean found
= false;
652 // if <svc>.<algo> <attr> is in the set then so is <svc>.<algo>
653 // but it may be stored under an alias <algo>. resolve
654 outer
: for (int r
= 0; r
< 3; r
++) // guard against circularity
656 serviceDotAlgorithm
= (svc
+"."+String
.valueOf(algo
)).trim();
657 for (it
= p
.keySet().iterator(); it
.hasNext(); )
659 key
= (String
) it
.next();
660 if (key
.equalsIgnoreCase(serviceDotAlgorithm
)) // eureka
665 // it may be there but as an alias
666 if (key
.equalsIgnoreCase(ALG_ALIAS
+ serviceDotAlgorithm
))
668 algo
= p
.getProperty(key
);
671 // else continue inner
678 // found a candidate for the querry. do we have an attr to match?
679 if (val
== null) // <service>.<algorithm> querry
682 // <service>.<algorithm> <attribute>; find the key entry that match
684 int limit
= serviceDotAlgorithm
.length() + 1;
685 for (it
= p
.keySet().iterator(); it
.hasNext(); )
687 key
= (String
) it
.next();
688 if (key
.length() <= limit
)
691 if (key
.substring(0, limit
).equalsIgnoreCase(serviceDotAlgorithm
+" "))
693 realAttr
= key
.substring(limit
).trim();
694 if (! realAttr
.equalsIgnoreCase(attr
))
697 // eveything matches so far. do the value
698 realVal
= p
.getProperty(key
);
702 realVal
= realVal
.trim();
703 // is it a string value?
704 if (val
.equalsIgnoreCase(realVal
))
707 // assume value is a number. cehck for greater-than-or-equal
708 return (new Integer(val
).intValue() >= new Integer(realVal
).intValue());