1 /*
   2  * Copyright (c) 1996, 2015, 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.io.*;
  29 import java.util.*;
  30 import static java.util.Locale.ENGLISH;
  31 import java.lang.ref.*;
  32 import java.lang.reflect.*;
  33 import java.util.function.BiConsumer;
  34 import java.util.function.BiFunction;
  35 import java.util.function.Function;
  36 
  37 /**
  38  * This class represents a "provider" for the
  39  * Java Security API, where a provider implements some or all parts of
  40  * Java Security. Services that a provider may implement include:
  41  *
  42  * <ul>
  43  *
  44  * <li>Algorithms (such as DSA, RSA, or SHA-256).
  45  *
  46  * <li>Key generation, conversion, and management facilities (such as for
  47  * algorithm-specific keys).
  48  *
  49  * </ul>
  50  *
  51  * <p>Some provider implementations may encounter unrecoverable internal
  52  * errors during their operation, for example a failure to communicate with a
  53  * security token. A {@link ProviderException} should be used to indicate
  54  * such errors.
  55  *
  56  * <p>Please note that a provider can be used to implement any security
  57  * service in Java that uses a pluggable architecture with a choice
  58  * of implementations that fit underneath.
  59  *
  60  * <p>The service type {@code Provider} is reserved for use by the
  61  * security framework. Services of this type cannot be added, removed,
  62  * or modified by applications.
  63  * The following attributes are automatically placed in each Provider object:
  64  * <table cellspacing=4>
  65  * <caption><b>Attributes Automatically Placed in a Provider Object</b></caption>
  66  * <tr><th>Name</th><th>Value</th>
  67  * <tr><td>{@code Provider.id name}</td>
  68   *    <td>{@code String.valueOf(provider.getName())}</td>
  69  * <tr><td>{@code Provider.id version}</td>
  70  *     <td>{@code String.valueOf(provider.getVersion())}</td>
  71  * <tr><td>{@code Provider.id info}</td>
  72        <td>{@code String.valueOf(provider.getInfo())}</td>
  73  * <tr><td>{@code Provider.id className}</td>
  74  *     <td>{@code provider.getClass().getName()}</td>
  75  * </table>
  76  *
  77  * <p>Each provider has a name and a version number. A provider normally
  78  * identifies itself with a file named {@code java.security.Provider}
  79  * in the resource directory {@code META-INF/services}.
  80  * Security providers are looked up via the {@link ServiceLoader} mechanism
  81  * using the {@link ClassLoader#getSystemClassLoader application class loader}.
  82  *
  83  * <p>Providers may be configured such that they are automatically
  84  * installed and made available at runtime via the
  85  * {@link Security#getProviders() Security.getProviders()} method.
  86  * The mechanism for configuring and installing security providers is
  87  * implementation-specific.
  88  *
  89  * @implNote
  90  * The JDK implementation supports static registration of the security
  91  * providers via the {@code conf/security/java.security} file in the Java
  92  * installation directory. These providers are automatically installed by
  93  * the JDK runtime, see <a href =
  94  * "../../../technotes/guides/security/crypto/CryptoSpec.html#Provider">The Provider Class</a>
  95  * in the "Java Cryptography Architecture API Specification &amp; Reference"
  96  * for information about how a particular type of provider, the cryptographic
  97  * service provider, works and is installed.
  98  *
  99  * @author Benjamin Renaud
 100  * @author Andreas Sterbenz
 101  */
 102 public abstract class Provider extends Properties {
 103 
 104     // Declare serialVersionUID to be compatible with JDK1.1
 105     static final long serialVersionUID = -4298000515446427739L;
 106 
 107     private static final sun.security.util.Debug debug =
 108         sun.security.util.Debug.getInstance
 109         ("provider", "Provider");
 110 
 111     /**
 112      * The provider name.
 113      *
 114      * @serial
 115      */
 116     private String name;
 117 
 118     /**
 119      * A description of the provider and its services.
 120      *
 121      * @serial
 122      */
 123     private String info;
 124 
 125     /**
 126      * The provider version number.
 127      *
 128      * @serial
 129      */
 130     private double version;
 131 
 132 
 133     private transient Set<Map.Entry<Object,Object>> entrySet = null;
 134     private transient int entrySetCallCount = 0;
 135 
 136     private transient boolean initialized;
 137 
 138     private static Object newInstanceUtil(final Class<?> clazz,
 139         final Class<?> ctrParamClz, final Object ctorParamObj)
 140         throws Exception {
 141         if (ctrParamClz == null) {
 142             Constructor<?> con = clazz.getConstructor();
 143             return con.newInstance();
 144         } else {
 145             Constructor<?> con = clazz.getConstructor(ctrParamClz);
 146             return con.newInstance(ctorParamObj);
 147         }
 148     }
 149 
 150     /**
 151      * Constructs a provider with the specified name, version number,
 152      * and information.
 153      *
 154      * @param name the provider name.
 155      *
 156      * @param version the provider version number.
 157      *
 158      * @param info a description of the provider and its services.
 159      */
 160     protected Provider(String name, double version, String info) {
 161         this.name = name;
 162         this.version = version;
 163         this.info = info;
 164         putId();
 165         initialized = true;
 166     }
 167 
 168     /**
 169      * Apply the supplied configuration argument to this provider instance
 170      * and return the configured provider. Note that if this provider cannot
 171      * be configured in-place, a new provider will be created and returned.
 172      * Therefore, callers should always use the returned provider.
 173      *
 174      * @implSpec
 175      * The default implementation throws {@code UnsupportedOperationException}.
 176      * Subclasses should override this method only if a configuration argument
 177      * is supported.
 178      *
 179      * @param configArg the configuration information for configuring this
 180      *         provider.
 181      *
 182      * @throws UnsupportedOperationException if a configuration argument is
 183      *         not supported.
 184      * @throws NullPointerException if the supplied configuration argument is
 185                null.
 186      * @throws InvalidParameterException if the supplied configuration argument
 187      *         is invalid.
 188      * @return a provider configured with the supplied configuration argument.
 189      *
 190      * @since 9
 191      */
 192     public Provider configure(String configArg) {
 193         throw new UnsupportedOperationException("configure is not supported");
 194     }
 195 
 196     /**
 197      * Check if this provider instance has been configured.
 198      *
 199      * @implSpec
 200      * The default implementation returns true.
 201      * Subclasses should override this method if the provider instance requires
 202      * an explicit {@code configure} call after being constructed.
 203      *
 204      * @return true if no further configuration is needed, false otherwise.
 205      *
 206      * @since 9
 207      */
 208     public boolean isConfigured() {
 209         return true;
 210     }
 211 
 212 
 213     /**
 214      * Returns the name of this provider.
 215      *
 216      * @return the name of this provider.
 217      */
 218     public String getName() {
 219         return name;
 220     }
 221 
 222     /**
 223      * Returns the version number for this provider.
 224      *
 225      * @return the version number for this provider.
 226      */
 227     public double getVersion() {
 228         return version;
 229     }
 230 
 231     /**
 232      * Returns a human-readable description of the provider and its
 233      * services.  This may return an HTML page, with relevant links.
 234      *
 235      * @return a description of the provider and its services.
 236      */
 237     public String getInfo() {
 238         return info;
 239     }
 240 
 241     /**
 242      * Returns a string with the name and the version number
 243      * of this provider.
 244      *
 245      * @return the string with the name and the version number
 246      * for this provider.
 247      */
 248     public String toString() {
 249         return name + " version " + version;
 250     }
 251 
 252     /*
 253      * override the following methods to ensure that provider
 254      * information can only be changed if the caller has the appropriate
 255      * permissions.
 256      */
 257 
 258     /**
 259      * Clears this provider so that it no longer contains the properties
 260      * used to look up facilities implemented by the provider.
 261      *
 262      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 263      * method is called with the string {@code "clearProviderProperties."+name}
 264      * (where {@code name} is the provider name) to see if it's ok to clear
 265      * this provider.
 266      *
 267      * @throws  SecurityException
 268      *          if a security manager exists and its {@link
 269      *          java.lang.SecurityManager#checkSecurityAccess} method
 270      *          denies access to clear this provider
 271      *
 272      * @since 1.2
 273      */
 274     @Override
 275     public synchronized void clear() {
 276         check("clearProviderProperties."+name);
 277         if (debug != null) {
 278             debug.println("Remove " + name + " provider properties");
 279         }
 280         implClear();
 281     }
 282 
 283     /**
 284      * Reads a property list (key and element pairs) from the input stream.
 285      *
 286      * @param inStream the input stream.
 287      * @exception IOException if an error occurred when reading from the
 288      *               input stream.
 289      * @see java.util.Properties#load
 290      */
 291     @Override
 292     public synchronized void load(InputStream inStream) throws IOException {
 293         check("putProviderProperty."+name);
 294         if (debug != null) {
 295             debug.println("Load " + name + " provider properties");
 296         }
 297         Properties tempProperties = new Properties();
 298         tempProperties.load(inStream);
 299         implPutAll(tempProperties);
 300     }
 301 
 302     /**
 303      * Copies all of the mappings from the specified Map to this provider.
 304      * These mappings will replace any properties that this provider had
 305      * for any of the keys currently in the specified Map.
 306      *
 307      * @since 1.2
 308      */
 309     @Override
 310     public synchronized void putAll(Map<?,?> t) {
 311         check("putProviderProperty."+name);
 312         if (debug != null) {
 313             debug.println("Put all " + name + " provider properties");
 314         }
 315         implPutAll(t);
 316     }
 317 
 318     /**
 319      * Returns an unmodifiable Set view of the property entries contained
 320      * in this Provider.
 321      *
 322      * @see   java.util.Map.Entry
 323      * @since 1.2
 324      */
 325     @Override
 326     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
 327         checkInitialized();
 328         if (entrySet == null) {
 329             if (entrySetCallCount++ == 0)  // Initial call
 330                 entrySet = Collections.unmodifiableMap(this).entrySet();
 331             else
 332                 return super.entrySet();   // Recursive call
 333         }
 334 
 335         // This exception will be thrown if the implementation of
 336         // Collections.unmodifiableMap.entrySet() is changed such that it
 337         // no longer calls entrySet() on the backing Map.  (Provider's
 338         // entrySet implementation depends on this "implementation detail",
 339         // which is unlikely to change.
 340         if (entrySetCallCount != 2)
 341             throw new RuntimeException("Internal error.");
 342 
 343         return entrySet;
 344     }
 345 
 346     /**
 347      * Returns an unmodifiable Set view of the property keys contained in
 348      * this provider.
 349      *
 350      * @since 1.2
 351      */
 352     @Override
 353     public Set<Object> keySet() {
 354         checkInitialized();
 355         return Collections.unmodifiableSet(super.keySet());
 356     }
 357 
 358     /**
 359      * Returns an unmodifiable Collection view of the property values
 360      * contained in this provider.
 361      *
 362      * @since 1.2
 363      */
 364     @Override
 365     public Collection<Object> values() {
 366         checkInitialized();
 367         return Collections.unmodifiableCollection(super.values());
 368     }
 369 
 370     /**
 371      * Sets the {@code key} property to have the specified
 372      * {@code value}.
 373      *
 374      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 375      * method is called with the string {@code "putProviderProperty."+name},
 376      * where {@code name} is the provider name, to see if it's ok to set this
 377      * provider's property values.
 378      *
 379      * @throws  SecurityException
 380      *          if a security manager exists and its {@link
 381      *          java.lang.SecurityManager#checkSecurityAccess} method
 382      *          denies access to set property values.
 383      *
 384      * @since 1.2
 385      */
 386     @Override
 387     public synchronized Object put(Object key, Object value) {
 388         check("putProviderProperty."+name);
 389         if (debug != null) {
 390             debug.println("Set " + name + " provider property [" +
 391                           key + "/" + value +"]");
 392         }
 393         return implPut(key, value);
 394     }
 395 
 396     /**
 397      * If the specified key is not already associated with a value (or is mapped
 398      * to {@code null}) associates it with the given value and returns
 399      * {@code null}, else returns the current value.
 400      *
 401      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 402      * method is called with the string {@code "putProviderProperty."+name},
 403      * where {@code name} is the provider name, to see if it's ok to set this
 404      * provider's property values.
 405      *
 406      * @throws  SecurityException
 407      *          if a security manager exists and its {@link
 408      *          java.lang.SecurityManager#checkSecurityAccess} method
 409      *          denies access to set property values.
 410      *
 411      * @since 1.8
 412      */
 413     @Override
 414     public synchronized Object putIfAbsent(Object key, Object value) {
 415         check("putProviderProperty."+name);
 416         if (debug != null) {
 417             debug.println("Set " + name + " provider property [" +
 418                           key + "/" + value +"]");
 419         }
 420         return implPutIfAbsent(key, value);
 421     }
 422 
 423     /**
 424      * Removes the {@code key} property (and its corresponding
 425      * {@code value}).
 426      *
 427      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 428      * method is called with the string {@code "removeProviderProperty."+name},
 429      * where {@code name} is the provider name, to see if it's ok to remove this
 430      * provider's properties.
 431      *
 432      * @throws  SecurityException
 433      *          if a security manager exists and its {@link
 434      *          java.lang.SecurityManager#checkSecurityAccess} method
 435      *          denies access to remove this provider's properties.
 436      *
 437      * @since 1.2
 438      */
 439     @Override
 440     public synchronized Object remove(Object key) {
 441         check("removeProviderProperty."+name);
 442         if (debug != null) {
 443             debug.println("Remove " + name + " provider property " + key);
 444         }
 445         return implRemove(key);
 446     }
 447 
 448     /**
 449      * Removes the entry for the specified key only if it is currently
 450      * mapped to the specified value.
 451      *
 452      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 453      * method is called with the string {@code "removeProviderProperty."+name},
 454      * where {@code name} is the provider name, to see if it's ok to remove this
 455      * provider's properties.
 456      *
 457      * @throws  SecurityException
 458      *          if a security manager exists and its {@link
 459      *          java.lang.SecurityManager#checkSecurityAccess} method
 460      *          denies access to remove this provider's properties.
 461      *
 462      * @since 1.8
 463      */
 464     @Override
 465     public synchronized boolean remove(Object key, Object value) {
 466         check("removeProviderProperty."+name);
 467         if (debug != null) {
 468             debug.println("Remove " + name + " provider property " + key);
 469         }
 470         return implRemove(key, value);
 471     }
 472 
 473     /**
 474      * Replaces the entry for the specified key only if currently
 475      * mapped to the specified value.
 476      *
 477      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 478      * method is called with the string {@code "putProviderProperty."+name},
 479      * where {@code name} is the provider name, to see if it's ok to set this
 480      * provider's property values.
 481      *
 482      * @throws  SecurityException
 483      *          if a security manager exists and its {@link
 484      *          java.lang.SecurityManager#checkSecurityAccess} method
 485      *          denies access to set property values.
 486      *
 487      * @since 1.8
 488      */
 489     @Override
 490     public synchronized boolean replace(Object key, Object oldValue,
 491             Object newValue) {
 492         check("putProviderProperty." + name);
 493 
 494         if (debug != null) {
 495             debug.println("Replace " + name + " provider property " + key);
 496         }
 497         return implReplace(key, oldValue, newValue);
 498     }
 499 
 500     /**
 501      * Replaces the entry for the specified key only if it is
 502      * currently mapped to some value.
 503      *
 504      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 505      * method is called with the string {@code "putProviderProperty."+name},
 506      * where {@code name} is the provider name, to see if it's ok to set this
 507      * provider's property values.
 508      *
 509      * @throws  SecurityException
 510      *          if a security manager exists and its {@link
 511      *          java.lang.SecurityManager#checkSecurityAccess} method
 512      *          denies access to set property values.
 513      *
 514      * @since 1.8
 515      */
 516     @Override
 517     public synchronized Object replace(Object key, Object value) {
 518         check("putProviderProperty." + name);
 519 
 520         if (debug != null) {
 521             debug.println("Replace " + name + " provider property " + key);
 522         }
 523         return implReplace(key, value);
 524     }
 525 
 526     /**
 527      * Replaces each entry's value with the result of invoking the given
 528      * function on that entry, in the order entries are returned by an entry
 529      * set iterator, until all entries have been processed or the function
 530      * throws an exception.
 531      *
 532      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 533      * method is called with the string {@code "putProviderProperty."+name},
 534      * where {@code name} is the provider name, to see if it's ok to set this
 535      * provider's property values.
 536      *
 537      * @throws  SecurityException
 538      *          if a security manager exists and its {@link
 539      *          java.lang.SecurityManager#checkSecurityAccess} method
 540      *          denies access to set property values.
 541      *
 542      * @since 1.8
 543      */
 544     @Override
 545     public synchronized void replaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function) {
 546         check("putProviderProperty." + name);
 547 
 548         if (debug != null) {
 549             debug.println("ReplaceAll " + name + " provider property ");
 550         }
 551         implReplaceAll(function);
 552     }
 553 
 554     /**
 555      * Attempts to compute a mapping for the specified key and its
 556      * current mapped value (or {@code null} if there is no current
 557      * mapping).
 558      *
 559      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 560      * method is called with the strings {@code "putProviderProperty."+name}
 561      * and {@code "removeProviderProperty."+name}, where {@code name} is the
 562      * provider name, to see if it's ok to set this provider's property values
 563      * and remove this provider's properties.
 564      *
 565      * @throws  SecurityException
 566      *          if a security manager exists and its {@link
 567      *          java.lang.SecurityManager#checkSecurityAccess} method
 568      *          denies access to set property values or remove properties.
 569      *
 570      * @since 1.8
 571      */
 572     @Override
 573     public synchronized Object compute(Object key,
 574         BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
 575         check("putProviderProperty." + name);
 576         check("removeProviderProperty" + name);
 577 
 578         if (debug != null) {
 579             debug.println("Compute " + name + " provider property " + key);
 580         }
 581         return implCompute(key, remappingFunction);
 582     }
 583 
 584     /**
 585      * If the specified key is not already associated with a value (or
 586      * is mapped to {@code null}), attempts to compute its value using
 587      * the given mapping function and enters it into this map unless
 588      * {@code null}.
 589      *
 590      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 591      * method is called with the strings {@code "putProviderProperty."+name}
 592      * and {@code "removeProviderProperty."+name}, where {@code name} is the
 593      * provider name, to see if it's ok to set this provider's property values
 594      * and remove this provider's properties.
 595      *
 596      * @throws  SecurityException
 597      *          if a security manager exists and its {@link
 598      *          java.lang.SecurityManager#checkSecurityAccess} method
 599      *          denies access to set property values and remove properties.
 600      *
 601      * @since 1.8
 602      */
 603     @Override
 604     public synchronized Object computeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
 605         check("putProviderProperty." + name);
 606         check("removeProviderProperty" + name);
 607 
 608         if (debug != null) {
 609             debug.println("ComputeIfAbsent " + name + " provider property " +
 610                     key);
 611         }
 612         return implComputeIfAbsent(key, mappingFunction);
 613     }
 614 
 615     /**
 616      * If the value for the specified key is present and non-null, attempts to
 617      * compute a new mapping given the key and its current mapped value.
 618      *
 619      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 620      * method is called with the strings {@code "putProviderProperty."+name}
 621      * and {@code "removeProviderProperty."+name}, where {@code name} is the
 622      * provider name, to see if it's ok to set this provider's property values
 623      * and remove this provider's properties.
 624      *
 625      * @throws  SecurityException
 626      *          if a security manager exists and its {@link
 627      *          java.lang.SecurityManager#checkSecurityAccess} method
 628      *          denies access to set property values or remove properties.
 629      *
 630      * @since 1.8
 631      */
 632     @Override
 633     public synchronized Object computeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
 634         check("putProviderProperty." + name);
 635         check("removeProviderProperty" + name);
 636 
 637         if (debug != null) {
 638             debug.println("ComputeIfPresent " + name + " provider property " +
 639                     key);
 640         }
 641         return implComputeIfPresent(key, remappingFunction);
 642     }
 643 
 644     /**
 645      * If the specified key is not already associated with a value or is
 646      * associated with null, associates it with the given value. Otherwise,
 647      * replaces the value with the results of the given remapping function,
 648      * or removes if the result is null. This method may be of use when
 649      * combining multiple mapped values for a key.
 650      *
 651      * <p>If a security manager is enabled, its {@code checkSecurityAccess}
 652      * method is called with the strings {@code "putProviderProperty."+name}
 653      * and {@code "removeProviderProperty."+name}, where {@code name} is the
 654      * provider name, to see if it's ok to set this provider's property values
 655      * and remove this provider's properties.
 656      *
 657      * @throws  SecurityException
 658      *          if a security manager exists and its {@link
 659      *          java.lang.SecurityManager#checkSecurityAccess} method
 660      *          denies access to set property values or remove properties.
 661      *
 662      * @since 1.8
 663      */
 664     @Override
 665     public synchronized Object merge(Object key, Object value,  BiFunction<? super Object, ? super Object, ? extends Object>  remappingFunction) {
 666         check("putProviderProperty." + name);
 667         check("removeProviderProperty" + name);
 668 
 669         if (debug != null) {
 670             debug.println("Merge " + name + " provider property " + key);
 671         }
 672         return implMerge(key, value, remappingFunction);
 673     }
 674 
 675     // let javadoc show doc from superclass
 676     @Override
 677     public Object get(Object key) {
 678         checkInitialized();
 679         return super.get(key);
 680     }
 681     /**
 682      * @since 1.8
 683      */
 684     @Override
 685     public synchronized Object getOrDefault(Object key, Object defaultValue) {
 686         checkInitialized();
 687         return super.getOrDefault(key, defaultValue);
 688     }
 689 
 690     /**
 691      * @since 1.8
 692      */
 693     @Override
 694     public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
 695         checkInitialized();
 696         super.forEach(action);
 697     }
 698 
 699     // let javadoc show doc from superclass
 700     @Override
 701     public Enumeration<Object> keys() {
 702         checkInitialized();
 703         return super.keys();
 704     }
 705 
 706     // let javadoc show doc from superclass
 707     @Override
 708     public Enumeration<Object> elements() {
 709         checkInitialized();
 710         return super.elements();
 711     }
 712 
 713     // let javadoc show doc from superclass
 714     public String getProperty(String key) {
 715         checkInitialized();
 716         return super.getProperty(key);
 717     }
 718 
 719     private void checkInitialized() {
 720         if (!initialized) {
 721             throw new IllegalStateException();
 722         }
 723     }
 724 
 725     private void check(String directive) {
 726         checkInitialized();
 727         SecurityManager security = System.getSecurityManager();
 728         if (security != null) {
 729             security.checkSecurityAccess(directive);
 730         }
 731     }
 732 
 733     // legacy properties changed since last call to any services method?
 734     private transient boolean legacyChanged;
 735     // serviceMap changed since last call to getServices()
 736     private transient boolean servicesChanged;
 737 
 738     // Map<String,String>
 739     private transient Map<String,String> legacyStrings;
 740 
 741     // Map<ServiceKey,Service>
 742     // used for services added via putService(), initialized on demand
 743     private transient Map<ServiceKey,Service> serviceMap;
 744 
 745     // Map<ServiceKey,Service>
 746     // used for services added via legacy methods, init on demand
 747     private transient Map<ServiceKey,Service> legacyMap;
 748 
 749     // Set<Service>
 750     // Unmodifiable set of all services. Initialized on demand.
 751     private transient Set<Service> serviceSet;
 752 
 753     // register the id attributes for this provider
 754     // this is to ensure that equals() and hashCode() do not incorrectly
 755     // report to different provider objects as the same
 756     private void putId() {
 757         // note: name and info may be null
 758         super.put("Provider.id name", String.valueOf(name));
 759         super.put("Provider.id version", String.valueOf(version));
 760         super.put("Provider.id info", String.valueOf(info));
 761         super.put("Provider.id className", this.getClass().getName());
 762     }
 763 
 764     private void readObject(ObjectInputStream in)
 765                 throws IOException, ClassNotFoundException {
 766         Map<Object,Object> copy = new HashMap<>();
 767         for (Map.Entry<Object,Object> entry : super.entrySet()) {
 768             copy.put(entry.getKey(), entry.getValue());
 769         }
 770         defaults = null;
 771         in.defaultReadObject();
 772         implClear();
 773         initialized = true;
 774         putAll(copy);
 775     }
 776 
 777     private boolean checkLegacy(Object key) {
 778         String keyString = (String)key;
 779         if (keyString.startsWith("Provider.")) {
 780             return false;
 781         }
 782 
 783         legacyChanged = true;
 784         if (legacyStrings == null) {
 785             legacyStrings = new LinkedHashMap<>();
 786         }
 787         return true;
 788     }
 789 
 790     /**
 791      * Copies all of the mappings from the specified Map to this provider.
 792      * Internal method to be called AFTER the security check has been
 793      * performed.
 794      */
 795     private void implPutAll(Map<?,?> t) {
 796         for (Map.Entry<?,?> e : t.entrySet()) {
 797             implPut(e.getKey(), e.getValue());
 798         }
 799     }
 800 
 801     private Object implRemove(Object key) {
 802         if (key instanceof String) {
 803             if (!checkLegacy(key)) {
 804                 return null;
 805             }
 806             legacyStrings.remove((String)key);
 807         }
 808         return super.remove(key);
 809     }
 810 
 811     private boolean implRemove(Object key, Object value) {
 812         if (key instanceof String && value instanceof String) {
 813             if (!checkLegacy(key)) {
 814                 return false;
 815             }
 816             legacyStrings.remove((String)key, value);
 817         }
 818         return super.remove(key, value);
 819     }
 820 
 821     private boolean implReplace(Object key, Object oldValue, Object newValue) {
 822         if ((key instanceof String) && (oldValue instanceof String) &&
 823                 (newValue instanceof String)) {
 824             if (!checkLegacy(key)) {
 825                 return false;
 826             }
 827             legacyStrings.replace((String)key, (String)oldValue,
 828                     (String)newValue);
 829         }
 830         return super.replace(key, oldValue, newValue);
 831     }
 832 
 833     private Object implReplace(Object key, Object value) {
 834         if ((key instanceof String) && (value instanceof String)) {
 835             if (!checkLegacy(key)) {
 836                 return null;
 837             }
 838             legacyStrings.replace((String)key, (String)value);
 839         }
 840         return super.replace(key, value);
 841     }
 842 
 843     @SuppressWarnings("unchecked") // Function must actually operate over strings
 844     private void implReplaceAll(BiFunction<? super Object, ? super Object, ? extends Object> function) {
 845         legacyChanged = true;
 846         if (legacyStrings == null) {
 847             legacyStrings = new LinkedHashMap<>();
 848         } else {
 849             legacyStrings.replaceAll((BiFunction<? super String, ? super String, ? extends String>) function);
 850         }
 851         super.replaceAll(function);
 852     }
 853 
 854     @SuppressWarnings("unchecked") // Function must actually operate over strings
 855     private Object implMerge(Object key, Object value, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
 856         if ((key instanceof String) && (value instanceof String)) {
 857             if (!checkLegacy(key)) {
 858                 return null;
 859             }
 860             legacyStrings.merge((String)key, (String)value,
 861                     (BiFunction<? super String, ? super String, ? extends String>) remappingFunction);
 862         }
 863         return super.merge(key, value, remappingFunction);
 864     }
 865 
 866     @SuppressWarnings("unchecked") // Function must actually operate over strings
 867     private Object implCompute(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
 868         if (key instanceof String) {
 869             if (!checkLegacy(key)) {
 870                 return null;
 871             }
 872             legacyStrings.computeIfAbsent((String) key,
 873                     (Function<? super String, ? extends String>) remappingFunction);
 874         }
 875         return super.compute(key, remappingFunction);
 876     }
 877 
 878     @SuppressWarnings("unchecked") // Function must actually operate over strings
 879     private Object implComputeIfAbsent(Object key, Function<? super Object, ? extends Object> mappingFunction) {
 880         if (key instanceof String) {
 881             if (!checkLegacy(key)) {
 882                 return null;
 883             }
 884             legacyStrings.computeIfAbsent((String) key,
 885                     (Function<? super String, ? extends String>) mappingFunction);
 886         }
 887         return super.computeIfAbsent(key, mappingFunction);
 888     }
 889 
 890     @SuppressWarnings("unchecked") // Function must actually operate over strings
 891     private Object implComputeIfPresent(Object key, BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction) {
 892         if (key instanceof String) {
 893             if (!checkLegacy(key)) {
 894                 return null;
 895             }
 896             legacyStrings.computeIfPresent((String) key,
 897                     (BiFunction<? super String, ? super String, ? extends String>) remappingFunction);
 898         }
 899         return super.computeIfPresent(key, remappingFunction);
 900     }
 901 
 902     private Object implPut(Object key, Object value) {
 903         if ((key instanceof String) && (value instanceof String)) {
 904             if (!checkLegacy(key)) {
 905                 return null;
 906             }
 907             legacyStrings.put((String)key, (String)value);
 908         }
 909         return super.put(key, value);
 910     }
 911 
 912     private Object implPutIfAbsent(Object key, Object value) {
 913         if ((key instanceof String) && (value instanceof String)) {
 914             if (!checkLegacy(key)) {
 915                 return null;
 916             }
 917             legacyStrings.putIfAbsent((String)key, (String)value);
 918         }
 919         return super.putIfAbsent(key, value);
 920     }
 921 
 922     private void implClear() {
 923         if (legacyStrings != null) {
 924             legacyStrings.clear();
 925         }
 926         if (legacyMap != null) {
 927             legacyMap.clear();
 928         }
 929         if (serviceMap != null) {
 930             serviceMap.clear();
 931         }
 932         legacyChanged = false;
 933         servicesChanged = false;
 934         serviceSet = null;
 935         super.clear();
 936         putId();
 937     }
 938 
 939     // used as key in the serviceMap and legacyMap HashMaps
 940     private static class ServiceKey {
 941         private final String type;
 942         private final String algorithm;
 943         private final String originalAlgorithm;
 944         private ServiceKey(String type, String algorithm, boolean intern) {
 945             this.type = type;
 946             this.originalAlgorithm = algorithm;
 947             algorithm = algorithm.toUpperCase(ENGLISH);
 948             this.algorithm = intern ? algorithm.intern() : algorithm;
 949         }
 950         public int hashCode() {
 951             return type.hashCode() + algorithm.hashCode();
 952         }
 953         public boolean equals(Object obj) {
 954             if (this == obj) {
 955                 return true;
 956             }
 957             if (obj instanceof ServiceKey == false) {
 958                 return false;
 959             }
 960             ServiceKey other = (ServiceKey)obj;
 961             return this.type.equals(other.type)
 962                 && this.algorithm.equals(other.algorithm);
 963         }
 964         boolean matches(String type, String algorithm) {
 965             return (this.type == type) && (this.originalAlgorithm == algorithm);
 966         }
 967     }
 968 
 969     /**
 970      * Ensure all the legacy String properties are fully parsed into
 971      * service objects.
 972      */
 973     private void ensureLegacyParsed() {
 974         if ((legacyChanged == false) || (legacyStrings == null)) {
 975             return;
 976         }
 977         serviceSet = null;
 978         if (legacyMap == null) {
 979             legacyMap = new LinkedHashMap<>();
 980         } else {
 981             legacyMap.clear();
 982         }
 983         for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
 984             parseLegacyPut(entry.getKey(), entry.getValue());
 985         }
 986         removeInvalidServices(legacyMap);
 987         legacyChanged = false;
 988     }
 989 
 990     /**
 991      * Remove all invalid services from the Map. Invalid services can only
 992      * occur if the legacy properties are inconsistent or incomplete.
 993      */
 994     private void removeInvalidServices(Map<ServiceKey,Service> map) {
 995         for (Iterator<Map.Entry<ServiceKey, Service>> t =
 996                 map.entrySet().iterator(); t.hasNext(); ) {
 997             Service s = t.next().getValue();
 998             if (s.isValid() == false) {
 999                 t.remove();
1000             }
1001         }
1002     }
1003 
1004     private String[] getTypeAndAlgorithm(String key) {
1005         int i = key.indexOf('.');
1006         if (i < 1) {
1007             if (debug != null) {
1008                 debug.println("Ignoring invalid entry in provider "
1009                         + name + ":" + key);
1010             }
1011             return null;
1012         }
1013         String type = key.substring(0, i);
1014         String alg = key.substring(i + 1);
1015         return new String[] {type, alg};
1016     }
1017 
1018     private static final String ALIAS_PREFIX = "Alg.Alias.";
1019     private static final String ALIAS_PREFIX_LOWER = "alg.alias.";
1020     private static final int ALIAS_LENGTH = ALIAS_PREFIX.length();
1021 
1022     private void parseLegacyPut(String name, String value) {
1023         if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
1024             // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
1025             // aliasKey ~ MessageDigest.SHA
1026             String stdAlg = value;
1027             String aliasKey = name.substring(ALIAS_LENGTH);
1028             String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
1029             if (typeAndAlg == null) {
1030                 return;
1031             }
1032             String type = getEngineName(typeAndAlg[0]);
1033             String aliasAlg = typeAndAlg[1].intern();
1034             ServiceKey key = new ServiceKey(type, stdAlg, true);
1035             Service s = legacyMap.get(key);
1036             if (s == null) {
1037                 s = new Service(this);
1038                 s.type = type;
1039                 s.algorithm = stdAlg;
1040                 legacyMap.put(key, s);
1041             }
1042             legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
1043             s.addAlias(aliasAlg);
1044         } else {
1045             String[] typeAndAlg = getTypeAndAlgorithm(name);
1046             if (typeAndAlg == null) {
1047                 return;
1048             }
1049             int i = typeAndAlg[1].indexOf(' ');
1050             if (i == -1) {
1051                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
1052                 String type = getEngineName(typeAndAlg[0]);
1053                 String stdAlg = typeAndAlg[1].intern();
1054                 String className = value;
1055                 ServiceKey key = new ServiceKey(type, stdAlg, true);
1056                 Service s = legacyMap.get(key);
1057                 if (s == null) {
1058                     s = new Service(this);
1059                     s.type = type;
1060                     s.algorithm = stdAlg;
1061                     legacyMap.put(key, s);
1062                 }
1063                 s.className = className;
1064             } else { // attribute
1065                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
1066                 String attributeValue = value;
1067                 String type = getEngineName(typeAndAlg[0]);
1068                 String attributeString = typeAndAlg[1];
1069                 String stdAlg = attributeString.substring(0, i).intern();
1070                 String attributeName = attributeString.substring(i + 1);
1071                 // kill additional spaces
1072                 while (attributeName.startsWith(" ")) {
1073                     attributeName = attributeName.substring(1);
1074                 }
1075                 attributeName = attributeName.intern();
1076                 ServiceKey key = new ServiceKey(type, stdAlg, true);
1077                 Service s = legacyMap.get(key);
1078                 if (s == null) {
1079                     s = new Service(this);
1080                     s.type = type;
1081                     s.algorithm = stdAlg;
1082                     legacyMap.put(key, s);
1083                 }
1084                 s.addAttribute(attributeName, attributeValue);
1085             }
1086         }
1087     }
1088 
1089     /**
1090      * Get the service describing this Provider's implementation of the
1091      * specified type of this algorithm or alias. If no such
1092      * implementation exists, this method returns null. If there are two
1093      * matching services, one added to this provider using
1094      * {@link #putService putService()} and one added via {@link #put put()},
1095      * the service added via {@link #putService putService()} is returned.
1096      *
1097      * @param type the type of {@link Service service} requested
1098      * (for example, {@code MessageDigest})
1099      * @param algorithm the case insensitive algorithm name (or alternate
1100      * alias) of the service requested (for example, {@code SHA-1})
1101      *
1102      * @return the service describing this Provider's matching service
1103      * or null if no such service exists
1104      *
1105      * @throws NullPointerException if type or algorithm is null
1106      *
1107      * @since 1.5
1108      */
1109     public synchronized Service getService(String type, String algorithm) {
1110         checkInitialized();
1111         // avoid allocating a new key object if possible
1112         ServiceKey key = previousKey;
1113         if (key.matches(type, algorithm) == false) {
1114             key = new ServiceKey(type, algorithm, false);
1115             previousKey = key;
1116         }
1117         if (serviceMap != null) {
1118             Service service = serviceMap.get(key);
1119             if (service != null) {
1120                 return service;
1121             }
1122         }
1123         ensureLegacyParsed();
1124         return (legacyMap != null) ? legacyMap.get(key) : null;
1125     }
1126 
1127     // ServiceKey from previous getService() call
1128     // by re-using it if possible we avoid allocating a new object
1129     // and the toUpperCase() call.
1130     // re-use will occur e.g. as the framework traverses the provider
1131     // list and queries each provider with the same values until it finds
1132     // a matching service
1133     private static volatile ServiceKey previousKey =
1134                                             new ServiceKey("", "", false);
1135 
1136     /**
1137      * Get an unmodifiable Set of all services supported by
1138      * this Provider.
1139      *
1140      * @return an unmodifiable Set of all services supported by
1141      * this Provider
1142      *
1143      * @since 1.5
1144      */
1145     public synchronized Set<Service> getServices() {
1146         checkInitialized();
1147         if (legacyChanged || servicesChanged) {
1148             serviceSet = null;
1149         }
1150         if (serviceSet == null) {
1151             ensureLegacyParsed();
1152             Set<Service> set = new LinkedHashSet<>();
1153             if (serviceMap != null) {
1154                 set.addAll(serviceMap.values());
1155             }
1156             if (legacyMap != null) {
1157                 set.addAll(legacyMap.values());
1158             }
1159             serviceSet = Collections.unmodifiableSet(set);
1160             servicesChanged = false;
1161         }
1162         return serviceSet;
1163     }
1164 
1165     /**
1166      * Add a service. If a service of the same type with the same algorithm
1167      * name exists and it was added using {@link #putService putService()},
1168      * it is replaced by the new service.
1169      * This method also places information about this service
1170      * in the provider's Hashtable values in the format described in the
1171      * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1172      * Java Cryptography Architecture API Specification &amp; Reference </a>.
1173      *
1174      * <p>Also, if there is a security manager, its
1175      * {@code checkSecurityAccess} method is called with the string
1176      * {@code "putProviderProperty."+name}, where {@code name} is
1177      * the provider name, to see if it's ok to set this provider's property
1178      * values. If the default implementation of {@code checkSecurityAccess}
1179      * is used (that is, that method is not overriden), then this results in
1180      * a call to the security manager's {@code checkPermission} method with
1181      * a {@code SecurityPermission("putProviderProperty."+name)}
1182      * permission.
1183      *
1184      * @param s the Service to add
1185      *
1186      * @throws SecurityException
1187      *      if a security manager exists and its {@link
1188      *      java.lang.SecurityManager#checkSecurityAccess} method denies
1189      *      access to set property values.
1190      * @throws NullPointerException if s is null
1191      *
1192      * @since 1.5
1193      */
1194     protected synchronized void putService(Service s) {
1195         check("putProviderProperty." + name);
1196         if (debug != null) {
1197             debug.println(name + ".putService(): " + s);
1198         }
1199         if (s == null) {
1200             throw new NullPointerException();
1201         }
1202         if (s.getProvider() != this) {
1203             throw new IllegalArgumentException
1204                     ("service.getProvider() must match this Provider object");
1205         }
1206         if (serviceMap == null) {
1207             serviceMap = new LinkedHashMap<>();
1208         }
1209         servicesChanged = true;
1210         String type = s.getType();
1211         String algorithm = s.getAlgorithm();
1212         ServiceKey key = new ServiceKey(type, algorithm, true);
1213         // remove existing service
1214         implRemoveService(serviceMap.get(key));
1215         serviceMap.put(key, s);
1216         for (String alias : s.getAliases()) {
1217             serviceMap.put(new ServiceKey(type, alias, true), s);
1218         }
1219         putPropertyStrings(s);
1220     }
1221 
1222     /**
1223      * Put the string properties for this Service in this Provider's
1224      * Hashtable.
1225      */
1226     private void putPropertyStrings(Service s) {
1227         String type = s.getType();
1228         String algorithm = s.getAlgorithm();
1229         // use super() to avoid permission check and other processing
1230         super.put(type + "." + algorithm, s.getClassName());
1231         for (String alias : s.getAliases()) {
1232             super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
1233         }
1234         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
1235             String key = type + "." + algorithm + " " + entry.getKey();
1236             super.put(key, entry.getValue());
1237         }
1238     }
1239 
1240     /**
1241      * Remove the string properties for this Service from this Provider's
1242      * Hashtable.
1243      */
1244     private void removePropertyStrings(Service s) {
1245         String type = s.getType();
1246         String algorithm = s.getAlgorithm();
1247         // use super() to avoid permission check and other processing
1248         super.remove(type + "." + algorithm);
1249         for (String alias : s.getAliases()) {
1250             super.remove(ALIAS_PREFIX + type + "." + alias);
1251         }
1252         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
1253             String key = type + "." + algorithm + " " + entry.getKey();
1254             super.remove(key);
1255         }
1256     }
1257 
1258     /**
1259      * Remove a service previously added using
1260      * {@link #putService putService()}. The specified service is removed from
1261      * this provider. It will no longer be returned by
1262      * {@link #getService getService()} and its information will be removed
1263      * from this provider's Hashtable.
1264      *
1265      * <p>Also, if there is a security manager, its
1266      * {@code checkSecurityAccess} method is called with the string
1267      * {@code "removeProviderProperty."+name}, where {@code name} is
1268      * the provider name, to see if it's ok to remove this provider's
1269      * properties. If the default implementation of
1270      * {@code checkSecurityAccess} is used (that is, that method is not
1271      * overriden), then this results in a call to the security manager's
1272      * {@code checkPermission} method with a
1273      * {@code SecurityPermission("removeProviderProperty."+name)}
1274      * permission.
1275      *
1276      * @param s the Service to be removed
1277      *
1278      * @throws  SecurityException
1279      *          if a security manager exists and its {@link
1280      *          java.lang.SecurityManager#checkSecurityAccess} method denies
1281      *          access to remove this provider's properties.
1282      * @throws NullPointerException if s is null
1283      *
1284      * @since 1.5
1285      */
1286     protected synchronized void removeService(Service s) {
1287         check("removeProviderProperty." + name);
1288         if (debug != null) {
1289             debug.println(name + ".removeService(): " + s);
1290         }
1291         if (s == null) {
1292             throw new NullPointerException();
1293         }
1294         implRemoveService(s);
1295     }
1296 
1297     private void implRemoveService(Service s) {
1298         if ((s == null) || (serviceMap == null)) {
1299             return;
1300         }
1301         String type = s.getType();
1302         String algorithm = s.getAlgorithm();
1303         ServiceKey key = new ServiceKey(type, algorithm, false);
1304         Service oldService = serviceMap.get(key);
1305         if (s != oldService) {
1306             return;
1307         }
1308         servicesChanged = true;
1309         serviceMap.remove(key);
1310         for (String alias : s.getAliases()) {
1311             serviceMap.remove(new ServiceKey(type, alias, false));
1312         }
1313         removePropertyStrings(s);
1314     }
1315 
1316     // Wrapped String that behaves in a case insensitive way for equals/hashCode
1317     private static class UString {
1318         final String string;
1319         final String lowerString;
1320 
1321         UString(String s) {
1322             this.string = s;
1323             this.lowerString = s.toLowerCase(ENGLISH);
1324         }
1325 
1326         public int hashCode() {
1327             return lowerString.hashCode();
1328         }
1329 
1330         public boolean equals(Object obj) {
1331             if (this == obj) {
1332                 return true;
1333             }
1334             if (obj instanceof UString == false) {
1335                 return false;
1336             }
1337             UString other = (UString)obj;
1338             return lowerString.equals(other.lowerString);
1339         }
1340 
1341         public String toString() {
1342             return string;
1343         }
1344     }
1345 
1346     // describe relevant properties of a type of engine
1347     private static class EngineDescription {
1348         final String name;
1349         final boolean supportsParameter;
1350         final String constructorParameterClassName;
1351         private volatile Class<?> constructorParameterClass;
1352 
1353         EngineDescription(String name, boolean sp, String paramName) {
1354             this.name = name;
1355             this.supportsParameter = sp;
1356             this.constructorParameterClassName = paramName;
1357         }
1358         Class<?> getConstructorParameterClass() throws ClassNotFoundException {
1359             Class<?> clazz = constructorParameterClass;
1360             if (clazz == null) {
1361                 clazz = Class.forName(constructorParameterClassName);
1362                 constructorParameterClass = clazz;
1363             }
1364             return clazz;
1365         }
1366     }
1367 
1368     // built in knowledge of the engine types shipped as part of the JDK
1369     private static final Map<String,EngineDescription> knownEngines;
1370 
1371     private static void addEngine(String name, boolean sp, String paramName) {
1372         EngineDescription ed = new EngineDescription(name, sp, paramName);
1373         // also index by canonical name to avoid toLowerCase() for some lookups
1374         knownEngines.put(name.toLowerCase(ENGLISH), ed);
1375         knownEngines.put(name, ed);
1376     }
1377 
1378     static {
1379         knownEngines = new HashMap<>();
1380         // JCA
1381         addEngine("AlgorithmParameterGenerator",        false, null);
1382         addEngine("AlgorithmParameters",                false, null);
1383         addEngine("KeyFactory",                         false, null);
1384         addEngine("KeyPairGenerator",                   false, null);
1385         addEngine("KeyStore",                           false, null);
1386         addEngine("MessageDigest",                      false, null);
1387         addEngine("SecureRandom",                       false, null);
1388         addEngine("Signature",                          true,  null);
1389         addEngine("CertificateFactory",                 false, null);
1390         addEngine("CertPathBuilder",                    false, null);
1391         addEngine("CertPathValidator",                  false, null);
1392         addEngine("CertStore",                          false,
1393                             "java.security.cert.CertStoreParameters");
1394         // JCE
1395         addEngine("Cipher",                             true,  null);
1396         addEngine("ExemptionMechanism",                 false, null);
1397         addEngine("Mac",                                true,  null);
1398         addEngine("KeyAgreement",                       true,  null);
1399         addEngine("KeyGenerator",                       false, null);
1400         addEngine("SecretKeyFactory",                   false, null);
1401         // JSSE
1402         addEngine("KeyManagerFactory",                  false, null);
1403         addEngine("SSLContext",                         false, null);
1404         addEngine("TrustManagerFactory",                false, null);
1405         // JGSS
1406         addEngine("GssApiMechanism",                    false, null);
1407         // SASL
1408         addEngine("SaslClientFactory",                  false, null);
1409         addEngine("SaslServerFactory",                  false, null);
1410         // POLICY
1411         addEngine("Policy",                             false,
1412                             "java.security.Policy$Parameters");
1413         // CONFIGURATION
1414         addEngine("Configuration",                      false,
1415                             "javax.security.auth.login.Configuration$Parameters");
1416         // XML DSig
1417         addEngine("XMLSignatureFactory",                false, null);
1418         addEngine("KeyInfoFactory",                     false, null);
1419         addEngine("TransformService",                   false, null);
1420         // Smart Card I/O
1421         addEngine("TerminalFactory",                    false,
1422                             "java.lang.Object");
1423     }
1424 
1425     // get the "standard" (mixed-case) engine name for arbitary case engine name
1426     // if there is no known engine by that name, return s
1427     private static String getEngineName(String s) {
1428         // try original case first, usually correct
1429         EngineDescription e = knownEngines.get(s);
1430         if (e == null) {
1431             e = knownEngines.get(s.toLowerCase(ENGLISH));
1432         }
1433         return (e == null) ? s : e.name;
1434     }
1435 
1436     /**
1437      * The description of a security service. It encapsulates the properties
1438      * of a service and contains a factory method to obtain new implementation
1439      * instances of this service.
1440      *
1441      * <p>Each service has a provider that offers the service, a type,
1442      * an algorithm name, and the name of the class that implements the
1443      * service. Optionally, it also includes a list of alternate algorithm
1444      * names for this service (aliases) and attributes, which are a map of
1445      * (name, value) String pairs.
1446      *
1447      * <p>This class defines the methods {@link #supportsParameter
1448      * supportsParameter()} and {@link #newInstance newInstance()}
1449      * which are used by the Java security framework when it searches for
1450      * suitable services and instantiates them. The valid arguments to those
1451      * methods depend on the type of service. For the service types defined
1452      * within Java SE, see the
1453      * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1454      * Java Cryptography Architecture API Specification &amp; Reference </a>
1455      * for the valid values.
1456      * Note that components outside of Java SE can define additional types of
1457      * services and their behavior.
1458      *
1459      * <p>Instances of this class are immutable.
1460      *
1461      * @since 1.5
1462      */
1463     public static class Service {
1464 
1465         private String type, algorithm, className;
1466         private final Provider provider;
1467         private List<String> aliases;
1468         private Map<UString,String> attributes;
1469 
1470         // Reference to the cached implementation Class object
1471         private volatile Reference<Class<?>> classRef;
1472 
1473         // flag indicating whether this service has its attributes for
1474         // supportedKeyFormats or supportedKeyClasses set
1475         // if null, the values have not been initialized
1476         // if TRUE, at least one of supportedFormats/Classes is non null
1477         private volatile Boolean hasKeyAttributes;
1478 
1479         // supported encoding formats
1480         private String[] supportedFormats;
1481 
1482         // names of the supported key (super) classes
1483         private Class<?>[] supportedClasses;
1484 
1485         // whether this service has been registered with the Provider
1486         private boolean registered;
1487 
1488         private static final Class<?>[] CLASS0 = new Class<?>[0];
1489 
1490         // this constructor and these methods are used for parsing
1491         // the legacy string properties.
1492 
1493         private Service(Provider provider) {
1494             this.provider = provider;
1495             aliases = Collections.<String>emptyList();
1496             attributes = Collections.<UString,String>emptyMap();
1497         }
1498 
1499         private boolean isValid() {
1500             return (type != null) && (algorithm != null) && (className != null);
1501         }
1502 
1503         private void addAlias(String alias) {
1504             if (aliases.isEmpty()) {
1505                 aliases = new ArrayList<>(2);
1506             }
1507             aliases.add(alias);
1508         }
1509 
1510         void addAttribute(String type, String value) {
1511             if (attributes.isEmpty()) {
1512                 attributes = new HashMap<>(8);
1513             }
1514             attributes.put(new UString(type), value);
1515         }
1516 
1517         /**
1518          * Construct a new service.
1519          *
1520          * @param provider the provider that offers this service
1521          * @param type the type of this service
1522          * @param algorithm the algorithm name
1523          * @param className the name of the class implementing this service
1524          * @param aliases List of aliases or null if algorithm has no aliases
1525          * @param attributes Map of attributes or null if this implementation
1526          *                   has no attributes
1527          *
1528          * @throws NullPointerException if provider, type, algorithm, or
1529          * className is null
1530          */
1531         public Service(Provider provider, String type, String algorithm,
1532                 String className, List<String> aliases,
1533                 Map<String,String> attributes) {
1534             if ((provider == null) || (type == null) ||
1535                     (algorithm == null) || (className == null)) {
1536                 throw new NullPointerException();
1537             }
1538             this.provider = provider;
1539             this.type = getEngineName(type);
1540             this.algorithm = algorithm;
1541             this.className = className;
1542             if (aliases == null) {
1543                 this.aliases = Collections.<String>emptyList();
1544             } else {
1545                 this.aliases = new ArrayList<>(aliases);
1546             }
1547             if (attributes == null) {
1548                 this.attributes = Collections.<UString,String>emptyMap();
1549             } else {
1550                 this.attributes = new HashMap<>();
1551                 for (Map.Entry<String,String> entry : attributes.entrySet()) {
1552                     this.attributes.put(new UString(entry.getKey()), entry.getValue());
1553                 }
1554             }
1555         }
1556 
1557         /**
1558          * Get the type of this service. For example, {@code MessageDigest}.
1559          *
1560          * @return the type of this service
1561          */
1562         public final String getType() {
1563             return type;
1564         }
1565 
1566         /**
1567          * Return the name of the algorithm of this service. For example,
1568          * {@code SHA-1}.
1569          *
1570          * @return the algorithm of this service
1571          */
1572         public final String getAlgorithm() {
1573             return algorithm;
1574         }
1575 
1576         /**
1577          * Return the Provider of this service.
1578          *
1579          * @return the Provider of this service
1580          */
1581         public final Provider getProvider() {
1582             return provider;
1583         }
1584 
1585         /**
1586          * Return the name of the class implementing this service.
1587          *
1588          * @return the name of the class implementing this service
1589          */
1590         public final String getClassName() {
1591             return className;
1592         }
1593 
1594         // internal only
1595         private final List<String> getAliases() {
1596             return aliases;
1597         }
1598 
1599         /**
1600          * Return the value of the specified attribute or null if this
1601          * attribute is not set for this Service.
1602          *
1603          * @param name the name of the requested attribute
1604          *
1605          * @return the value of the specified attribute or null if the
1606          *         attribute is not present
1607          *
1608          * @throws NullPointerException if name is null
1609          */
1610         public final String getAttribute(String name) {
1611             if (name == null) {
1612                 throw new NullPointerException();
1613             }
1614             return attributes.get(new UString(name));
1615         }
1616 
1617         /**
1618          * Return a new instance of the implementation described by this
1619          * service. The security provider framework uses this method to
1620          * construct implementations. Applications will typically not need
1621          * to call it.
1622          *
1623          * <p>The default implementation uses reflection to invoke the
1624          * standard constructor for this type of service.
1625          * Security providers can override this method to implement
1626          * instantiation in a different way.
1627          * For details and the values of constructorParameter that are
1628          * valid for the various types of services see the
1629          * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1630          * Java Cryptography Architecture API Specification &amp;
1631          * Reference</a>.
1632          *
1633          * @param constructorParameter the value to pass to the constructor,
1634          * or null if this type of service does not use a constructorParameter.
1635          *
1636          * @return a new implementation of this service
1637          *
1638          * @throws InvalidParameterException if the value of
1639          * constructorParameter is invalid for this type of service.
1640          * @throws NoSuchAlgorithmException if instantiation failed for
1641          * any other reason.
1642          */
1643         public Object newInstance(Object constructorParameter)
1644                 throws NoSuchAlgorithmException {
1645             if (registered == false) {
1646                 if (provider.getService(type, algorithm) != this) {
1647                     throw new NoSuchAlgorithmException
1648                         ("Service not registered with Provider "
1649                         + provider.getName() + ": " + this);
1650                 }
1651                 registered = true;
1652             }
1653             Class<?> ctrParamClz;
1654             try {
1655                 EngineDescription cap = knownEngines.get(type);
1656                 if (cap == null) {
1657                     // unknown engine type, use generic code
1658                     // this is the code path future for non-core
1659                     // optional packages
1660                     ctrParamClz = constructorParameter == null?
1661                         null : constructorParameter.getClass();
1662                 } else {
1663                     ctrParamClz = cap.constructorParameterClassName == null?
1664                         null : Class.forName(cap.constructorParameterClassName);
1665                     if (constructorParameter != null) {
1666                         if (ctrParamClz == null) {
1667                             throw new InvalidParameterException
1668                                 ("constructorParameter not used with " + type
1669                                 + " engines");
1670                         } else {
1671                             Class<?> argClass = constructorParameter.getClass();
1672                             if (ctrParamClz.isAssignableFrom(argClass) == false) {
1673                                 throw new InvalidParameterException
1674                                     ("constructorParameter must be instanceof "
1675                                     + cap.constructorParameterClassName.replace('$', '.')
1676                                     + " for engine type " + type);
1677                             }
1678                         }
1679                     }
1680                 }
1681                 return newInstanceUtil(getImplClass(), ctrParamClz, constructorParameter);
1682             } catch (NoSuchAlgorithmException e) {
1683                 throw e;
1684             } catch (InvocationTargetException e) {
1685                 throw new NoSuchAlgorithmException
1686                     ("Error constructing implementation (algorithm: "
1687                     + algorithm + ", provider: " + provider.getName()
1688                     + ", class: " + className + ")", e.getCause());
1689             } catch (Exception e) {
1690                 throw new NoSuchAlgorithmException
1691                     ("Error constructing implementation (algorithm: "
1692                     + algorithm + ", provider: " + provider.getName()
1693                     + ", class: " + className + ")", e);
1694             }
1695         }
1696 
1697         // return the implementation Class object for this service
1698         private Class<?> getImplClass() throws NoSuchAlgorithmException {
1699             try {
1700                 Reference<Class<?>> ref = classRef;
1701                 Class<?> clazz = (ref == null) ? null : ref.get();
1702                 if (clazz == null) {
1703                     ClassLoader cl = provider.getClass().getClassLoader();
1704                     if (cl == null) {
1705                         clazz = Class.forName(className);
1706                     } else {
1707                         clazz = cl.loadClass(className);
1708                     }
1709                     if (!Modifier.isPublic(clazz.getModifiers())) {
1710                         throw new NoSuchAlgorithmException
1711                             ("class configured for " + type + " (provider: " +
1712                             provider.getName() + ") is not public.");
1713                     }
1714                     classRef = new WeakReference<>(clazz);
1715                 }
1716                 return clazz;
1717             } catch (ClassNotFoundException e) {
1718                 throw new NoSuchAlgorithmException
1719                     ("class configured for " + type + " (provider: " +
1720                     provider.getName() + ") cannot be found.", e);
1721             }
1722         }
1723 
1724         /**
1725          * Test whether this Service can use the specified parameter.
1726          * Returns false if this service cannot use the parameter. Returns
1727          * true if this service can use the parameter, if a fast test is
1728          * infeasible, or if the status is unknown.
1729          *
1730          * <p>The security provider framework uses this method with
1731          * some types of services to quickly exclude non-matching
1732          * implementations for consideration.
1733          * Applications will typically not need to call it.
1734          *
1735          * <p>For details and the values of parameter that are valid for the
1736          * various types of services see the top of this class and the
1737          * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">
1738          * Java Cryptography Architecture API Specification &amp;
1739          * Reference</a>.
1740          * Security providers can override it to implement their own test.
1741          *
1742          * @param parameter the parameter to test
1743          *
1744          * @return false if this service cannot use the specified
1745          * parameter; true if it can possibly use the parameter
1746          *
1747          * @throws InvalidParameterException if the value of parameter is
1748          * invalid for this type of service or if this method cannot be
1749          * used with this type of service
1750          */
1751         public boolean supportsParameter(Object parameter) {
1752             EngineDescription cap = knownEngines.get(type);
1753             if (cap == null) {
1754                 // unknown engine type, return true by default
1755                 return true;
1756             }
1757             if (cap.supportsParameter == false) {
1758                 throw new InvalidParameterException("supportsParameter() not "
1759                     + "used with " + type + " engines");
1760             }
1761             // allow null for keys without attributes for compatibility
1762             if ((parameter != null) && (parameter instanceof Key == false)) {
1763                 throw new InvalidParameterException
1764                     ("Parameter must be instanceof Key for engine " + type);
1765             }
1766             if (hasKeyAttributes() == false) {
1767                 return true;
1768             }
1769             if (parameter == null) {
1770                 return false;
1771             }
1772             Key key = (Key)parameter;
1773             if (supportsKeyFormat(key)) {
1774                 return true;
1775             }
1776             if (supportsKeyClass(key)) {
1777                 return true;
1778             }
1779             return false;
1780         }
1781 
1782         /**
1783          * Return whether this service has its Supported* properties for
1784          * keys defined. Parses the attributes if not yet initialized.
1785          */
1786         private boolean hasKeyAttributes() {
1787             Boolean b = hasKeyAttributes;
1788             if (b == null) {
1789                 synchronized (this) {
1790                     String s;
1791                     s = getAttribute("SupportedKeyFormats");
1792                     if (s != null) {
1793                         supportedFormats = s.split("\\|");
1794                     }
1795                     s = getAttribute("SupportedKeyClasses");
1796                     if (s != null) {
1797                         String[] classNames = s.split("\\|");
1798                         List<Class<?>> classList =
1799                             new ArrayList<>(classNames.length);
1800                         for (String className : classNames) {
1801                             Class<?> clazz = getKeyClass(className);
1802                             if (clazz != null) {
1803                                 classList.add(clazz);
1804                             }
1805                         }
1806                         supportedClasses = classList.toArray(CLASS0);
1807                     }
1808                     boolean bool = (supportedFormats != null)
1809                         || (supportedClasses != null);
1810                     b = Boolean.valueOf(bool);
1811                     hasKeyAttributes = b;
1812                 }
1813             }
1814             return b.booleanValue();
1815         }
1816 
1817         // get the key class object of the specified name
1818         private Class<?> getKeyClass(String name) {
1819             try {
1820                 return Class.forName(name);
1821             } catch (ClassNotFoundException e) {
1822                 // ignore
1823             }
1824             try {
1825                 ClassLoader cl = provider.getClass().getClassLoader();
1826                 if (cl != null) {
1827                     return cl.loadClass(name);
1828                 }
1829             } catch (ClassNotFoundException e) {
1830                 // ignore
1831             }
1832             return null;
1833         }
1834 
1835         private boolean supportsKeyFormat(Key key) {
1836             if (supportedFormats == null) {
1837                 return false;
1838             }
1839             String format = key.getFormat();
1840             if (format == null) {
1841                 return false;
1842             }
1843             for (String supportedFormat : supportedFormats) {
1844                 if (supportedFormat.equals(format)) {
1845                     return true;
1846                 }
1847             }
1848             return false;
1849         }
1850 
1851         private boolean supportsKeyClass(Key key) {
1852             if (supportedClasses == null) {
1853                 return false;
1854             }
1855             Class<?> keyClass = key.getClass();
1856             for (Class<?> clazz : supportedClasses) {
1857                 if (clazz.isAssignableFrom(keyClass)) {
1858                     return true;
1859                 }
1860             }
1861             return false;
1862         }
1863 
1864         /**
1865          * Return a String representation of this service.
1866          *
1867          * @return a String representation of this service.
1868          */
1869         public String toString() {
1870             String aString = aliases.isEmpty()
1871                 ? "" : "\r\n  aliases: " + aliases.toString();
1872             String attrs = attributes.isEmpty()
1873                 ? "" : "\r\n  attributes: " + attributes.toString();
1874             return provider.getName() + ": " + type + "." + algorithm
1875                 + " -> " + className + aString + attrs + "\r\n";
1876         }
1877 
1878     }
1879 
1880 }