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