1 /*
   2  * Copyright (c) 1996, 2006, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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 &amp; 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>&lt;crypto_service>.&lt;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>&lt;crypto_service>.&lt;algorithm_or_type>
 502      * &lt;attribute_name>:&lt 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
 505      * <i>&lt;algorithm_or_type></i> and the <i>&lt;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 &amp; 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>&lt;crypto_service>.&lt;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>&lt;crypto_service>.&lt;algorithm_or_type> &lt;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>&lt;algorithm_or_type></i>
 577      * and the <i>&lt;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 &amp; 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 new AssertionError("Spi class not found", 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 &amp; 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 }