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