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