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