Home | History | Annotate | Download | only in security
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.security;
     28 
     29 import java.io.*;
     30 import java.util.*;
     31 import java.util.concurrent.atomic.AtomicBoolean;
     32 
     33 import static java.util.Locale.ENGLISH;
     34 
     35 import java.lang.ref.*;
     36 import java.lang.reflect.*;
     37 import java.security.Security;
     38 import java.security.cert.CertStoreParameters;
     39 import java.util.function.BiConsumer;
     40 
     41 /**
     42  * This class represents a "provider" for the
     43  * Java Security API, where a provider implements some or all parts of
     44  * Java Security. Services that a provider may implement include:
     45  *
     46  * <ul>
     47  *
     48  * <li>Algorithms (such as DSA, RSA, MD5 or SHA-1).
     49  *
     50  * <li>Key generation, conversion, and management facilities (such as for
     51  * algorithm-specific keys).
     52  *
     53  *</ul>
     54  *
     55  * <p>Each provider has a name and a version number, and is configured
     56  * in each runtime it is installed in.
     57  *
     58  * <p>See <a href =
     59  * "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html#Provider">The Provider Class</a>
     60  * in the "Java Cryptography Architecture API Specification &amp; Reference"
     61  * for information about how a particular type of provider, the
     62  * cryptographic service provider, works and is installed. However,
     63  * please note that a provider can be used to implement any security
     64  * service in Java that uses a pluggable architecture with a choice
     65  * of implementations that fit underneath.
     66  *
     67  * <p>Some provider implementations may encounter unrecoverable internal
     68  * errors during their operation, for example a failure to communicate with a
     69  * security token. A {@link ProviderException} should be used to indicate
     70  * such errors.
     71  *
     72  * <p>The service type <code>Provider</code> is reserved for use by the
     73  * security framework. Services of this type cannot be added, removed,
     74  * or modified by applications.
     75  * The following attributes are automatically placed in each Provider object:
     76  * <table cellspacing=4>
     77  * <tr><th>Name</th><th>Value</th>
     78  * <tr><td><code>Provider.id name</code></td>
     79   *    <td><code>String.valueOf(provider.getName())</code></td>
     80  * <tr><td><code>Provider.id version</code></td>
     81  *     <td><code>String.valueOf(provider.getVersion())</code></td>
     82  * <tr><td><code>Provider.id info</code></td>
     83        <td><code>String.valueOf(provider.getInfo())</code></td>
     84  * <tr><td><code>Provider.id className</code></td>
     85  *     <td><code>provider.getClass().getName()</code></td>
     86  * </table>
     87  *
     88  * @author Benjamin Renaud
     89  * @author Andreas Sterbenz
     90  */
     91 public abstract class Provider extends Properties {
     92 
     93     // Declare serialVersionUID to be compatible with JDK1.1
     94     static final long serialVersionUID = -4298000515446427739L;
     95 
     96     private volatile boolean registered = false;
     97 
     98     private static final sun.security.util.Debug debug =
     99         sun.security.util.Debug.getInstance
    100         ("provider", "Provider");
    101 
    102     /**
    103      * The provider name.
    104      *
    105      * @serial
    106      */
    107     private String name;
    108 
    109     /**
    110      * A description of the provider and its services.
    111      *
    112      * @serial
    113      */
    114     private String info;
    115 
    116     /**
    117      * The provider version number.
    118      *
    119      * @serial
    120      */
    121     private double version;
    122 
    123 
    124     private transient Set<Map.Entry<Object,Object>> entrySet = null;
    125     private transient int entrySetCallCount = 0;
    126 
    127     private transient boolean initialized;
    128 
    129     /**
    130      * Constructs a provider with the specified name, version number,
    131      * and information.
    132      *
    133      * @param name the provider name.
    134      *
    135      * @param version the provider version number.
    136      *
    137      * @param info a description of the provider and its services.
    138      */
    139     protected Provider(String name, double version, String info) {
    140         this.name = name;
    141         this.version = version;
    142         this.info = info;
    143         putId();
    144         initialized = true;
    145     }
    146 
    147     /**
    148      * Returns the name of this provider.
    149      *
    150      * @return the name of this provider.
    151      */
    152     public String getName() {
    153         return name;
    154     }
    155 
    156     /**
    157      * Returns the version number for this provider.
    158      *
    159      * @return the version number for this provider.
    160      */
    161     public double getVersion() {
    162         return version;
    163     }
    164 
    165     /**
    166      * Returns a human-readable description of the provider and its
    167      * services.  This may return an HTML page, with relevant links.
    168      *
    169      * @return a description of the provider and its services.
    170      */
    171     public String getInfo() {
    172         return info;
    173     }
    174 
    175     /**
    176      * Returns a string with the name and the version number
    177      * of this provider.
    178      *
    179      * @return the string with the name and the version number
    180      * for this provider.
    181      */
    182     public String toString() {
    183         return name + " version " + version;
    184     }
    185 
    186     /*
    187      * override the following methods to ensure that provider
    188      * information can only be changed if the caller has the appropriate
    189      * permissions.
    190      */
    191 
    192     /**
    193      * Clears this provider so that it no longer contains the properties
    194      * used to look up facilities implemented by the provider.
    195      *
    196      * <p>First, if there is a security manager, its
    197      * <code>checkSecurityAccess</code> method is called with the string
    198      * <code>"clearProviderProperties."+name</code> (where <code>name</code>
    199      * is the provider name) to see if it's ok to clear this provider.
    200      * If the default implementation of <code>checkSecurityAccess</code>
    201      * is used (that is, that method is not overriden), then this results in
    202      * a call to the security manager's <code>checkPermission</code> method
    203      * with a <code>SecurityPermission("clearProviderProperties."+name)</code>
    204      * permission.
    205      *
    206      * @throws  SecurityException
    207      *          if a security manager exists and its <code>{@link
    208      *          java.lang.SecurityManager#checkSecurityAccess}</code> method
    209      *          denies access to clear this provider
    210      *
    211      * @since 1.2
    212      */
    213     public synchronized void clear() {
    214         check("clearProviderProperties."+name);
    215         if (debug != null) {
    216             debug.println("Remove " + name + " provider properties");
    217         }
    218         implClear();
    219     }
    220 
    221     /**
    222      * Reads a property list (key and element pairs) from the input stream.
    223      *
    224      * @param inStream   the input stream.
    225      * @exception  IOException  if an error occurred when reading from the
    226      *               input stream.
    227      * @see java.util.Properties#load
    228      */
    229     public synchronized void load(InputStream inStream) throws IOException {
    230         check("putProviderProperty."+name);
    231         if (debug != null) {
    232             debug.println("Load " + name + " provider properties");
    233         }
    234         Properties tempProperties = new Properties();
    235         tempProperties.load(inStream);
    236         implPutAll(tempProperties);
    237     }
    238 
    239     /**
    240      * Copies all of the mappings from the specified Map to this provider.
    241      * These mappings will replace any properties that this provider had
    242      * for any of the keys currently in the specified Map.
    243      *
    244      * @since 1.2
    245      */
    246     public synchronized void putAll(Map<?,?> t) {
    247         check("putProviderProperty."+name);
    248         if (debug != null) {
    249             debug.println("Put all " + name + " provider properties");
    250         }
    251         implPutAll(t);
    252     }
    253 
    254     /**
    255      * Returns an unmodifiable Set view of the property entries contained
    256      * in this Provider.
    257      *
    258      * @see   java.util.Map.Entry
    259      * @since 1.2
    260      */
    261     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
    262         checkInitialized();
    263         if (entrySet == null) {
    264             if (entrySetCallCount++ == 0)  // Initial call
    265                 entrySet = Collections.unmodifiableMap(this).entrySet();
    266             else
    267                 return super.entrySet();   // Recursive call
    268         }
    269 
    270         // This exception will be thrown if the implementation of
    271         // Collections.unmodifiableMap.entrySet() is changed such that it
    272         // no longer calls entrySet() on the backing Map.  (Provider's
    273         // entrySet implementation depends on this "implementation detail",
    274         // which is unlikely to change.
    275         if (entrySetCallCount != 2)
    276             throw new RuntimeException("Internal error.");
    277 
    278         return entrySet;
    279     }
    280 
    281     /**
    282      * Returns an unmodifiable Set view of the property keys contained in
    283      * this provider.
    284      *
    285      * @since 1.2
    286      */
    287     public Set<Object> keySet() {
    288         checkInitialized();
    289         return Collections.unmodifiableSet(super.keySet());
    290     }
    291 
    292     /**
    293      * Returns an unmodifiable Collection view of the property values
    294      * contained in this provider.
    295      *
    296      * @since 1.2
    297      */
    298     public Collection<Object> values() {
    299         checkInitialized();
    300         return Collections.unmodifiableCollection(super.values());
    301     }
    302 
    303     /**
    304      * Sets the <code>key</code> property to have the specified
    305      * <code>value</code>.
    306      *
    307      * <p>First, if there is a security manager, its
    308      * <code>checkSecurityAccess</code> method is called with the string
    309      * <code>"putProviderProperty."+name</code>, where <code>name</code> is the
    310      * provider name, to see if it's ok to set this provider's property values.
    311      * If the default implementation of <code>checkSecurityAccess</code>
    312      * is used (that is, that method is not overriden), then this results in
    313      * a call to the security manager's <code>checkPermission</code> method
    314      * with a <code>SecurityPermission("putProviderProperty."+name)</code>
    315      * permission.
    316      *
    317      * @param key the property key.
    318      *
    319      * @param value the property value.
    320      *
    321      * @return the previous value of the specified property
    322      * (<code>key</code>), or null if it did not have one.
    323      *
    324      * @throws  SecurityException
    325      *          if a security manager exists and its <code>{@link
    326      *          java.lang.SecurityManager#checkSecurityAccess}</code> method
    327      *          denies access to set property values.
    328      *
    329      * @since 1.2
    330      */
    331     public synchronized Object put(Object key, Object value) {
    332         check("putProviderProperty."+name);
    333         if (debug != null) {
    334             debug.println("Set " + name + " provider property [" +
    335                           key + "/" + value +"]");
    336         }
    337         return implPut(key, value);
    338     }
    339 
    340     /**
    341      * Removes the <code>key</code> property (and its corresponding
    342      * <code>value</code>).
    343      *
    344      * <p>First, if there is a security manager, its
    345      * <code>checkSecurityAccess</code> method is called with the string
    346      * <code>"removeProviderProperty."+name</code>, where <code>name</code> is
    347      * the provider name, to see if it's ok to remove this provider's
    348      * properties. If the default implementation of
    349      * <code>checkSecurityAccess</code> is used (that is, that method is not
    350      * overriden), then this results in a call to the security manager's
    351      * <code>checkPermission</code> method with a
    352      * <code>SecurityPermission("removeProviderProperty."+name)</code>
    353      * permission.
    354      *
    355      * @param key the key for the property to be removed.
    356      *
    357      * @return the value to which the key had been mapped,
    358      * or null if the key did not have a mapping.
    359      *
    360      * @throws  SecurityException
    361      *          if a security manager exists and its <code>{@link
    362      *          java.lang.SecurityManager#checkSecurityAccess}</code> method
    363      *          denies access to remove this provider's properties.
    364      *
    365      * @since 1.2
    366      */
    367     public synchronized Object remove(Object key) {
    368         check("removeProviderProperty."+name);
    369         if (debug != null) {
    370             debug.println("Remove " + name + " provider property " + key);
    371         }
    372         return implRemove(key);
    373     }
    374 
    375     // let javadoc show doc from superclass
    376     public Object get(Object key) {
    377         checkInitialized();
    378         return super.get(key);
    379     }
    380 
    381     /**
    382      * @since 1.8
    383      */
    384     @Override
    385     public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
    386         checkInitialized();
    387         super.forEach(action);
    388     }
    389 
    390     // let javadoc show doc from superclass
    391     public Enumeration<Object> keys() {
    392         checkInitialized();
    393         return super.keys();
    394     }
    395 
    396     // let javadoc show doc from superclass
    397     public Enumeration<Object> elements() {
    398         checkInitialized();
    399         return super.elements();
    400     }
    401 
    402     // let javadoc show doc from superclass
    403     public String getProperty(String key) {
    404         checkInitialized();
    405         return super.getProperty(key);
    406     }
    407 
    408     private void checkInitialized() {
    409         if (!initialized) {
    410             throw new IllegalStateException();
    411         }
    412     }
    413 
    414     private void check(String directive) {
    415         checkInitialized();
    416         SecurityManager security = System.getSecurityManager();
    417         if (security != null) {
    418             security.checkSecurityAccess(directive);
    419         }
    420     }
    421 
    422     // legacy properties changed since last call to any services method?
    423     private transient boolean legacyChanged;
    424     // serviceMap changed since last call to getServices()
    425     private transient boolean servicesChanged;
    426 
    427     // Map<String,String>
    428     private transient Map<String,String> legacyStrings;
    429 
    430     // Map<ServiceKey,Service>
    431     // used for services added via putService(), initialized on demand
    432     private transient Map<ServiceKey,Service> serviceMap;
    433 
    434     // Map<ServiceKey,Service>
    435     // used for services added via legacy methods, init on demand
    436     private transient Map<ServiceKey,Service> legacyMap;
    437 
    438     // Set<Service>
    439     // Unmodifiable set of all services. Initialized on demand.
    440     private transient Set<Service> serviceSet;
    441 
    442     // register the id attributes for this provider
    443     // this is to ensure that equals() and hashCode() do not incorrectly
    444     // report to different provider objects as the same
    445     private void putId() {
    446         // note: name and info may be null
    447         super.put("Provider.id name", String.valueOf(name));
    448         super.put("Provider.id version", String.valueOf(version));
    449         super.put("Provider.id info", String.valueOf(info));
    450         super.put("Provider.id className", this.getClass().getName());
    451     }
    452 
    453     private void readObject(ObjectInputStream in)
    454                 throws IOException, ClassNotFoundException {
    455         registered = false;
    456         Map<Object,Object> copy = new HashMap<>();
    457         for (Map.Entry<Object,Object> entry : super.entrySet()) {
    458             copy.put(entry.getKey(), entry.getValue());
    459         }
    460         defaults = null;
    461         in.defaultReadObject();
    462         implClear();
    463         initialized = true;
    464         putAll(copy);
    465     }
    466 
    467     /**
    468      * Copies all of the mappings from the specified Map to this provider.
    469      * Internal method to be called AFTER the security check has been
    470      * performed.
    471      */
    472     private void implPutAll(Map t) {
    473         for (Map.Entry e : ((Map<?,?>)t).entrySet()) {
    474             implPut(e.getKey(), e.getValue());
    475         }
    476         if (registered) {
    477             Security.increaseVersion();
    478         }
    479     }
    480 
    481     private Object implRemove(Object key) {
    482         if (registered) {
    483             Security.increaseVersion();
    484         }
    485         if (key instanceof String) {
    486             String keyString = (String)key;
    487             if (keyString.startsWith("Provider.")) {
    488                 return null;
    489             }
    490             legacyChanged = true;
    491             if (legacyStrings == null) {
    492                 legacyStrings = new LinkedHashMap<String,String>();
    493             }
    494             legacyStrings.remove(keyString);
    495         }
    496         return super.remove(key);
    497     }
    498 
    499     private Object implPut(Object key, Object value) {
    500         if ((key instanceof String) && (value instanceof String)) {
    501             String keyString = (String)key;
    502             if (keyString.startsWith("Provider.")) {
    503                 return null;
    504             }
    505             if (registered) {
    506                 Security.increaseVersion();
    507             }
    508             legacyChanged = true;
    509             if (legacyStrings == null) {
    510                 legacyStrings = new LinkedHashMap<String,String>();
    511             }
    512             legacyStrings.put(keyString, (String)value);
    513         }
    514         return super.put(key, value);
    515     }
    516 
    517     private void implClear() {
    518         if (legacyStrings != null) {
    519             legacyStrings.clear();
    520         }
    521         if (legacyMap != null) {
    522             legacyMap.clear();
    523         }
    524         if (serviceMap != null) {
    525             serviceMap.clear();
    526         }
    527         legacyChanged = false;
    528         servicesChanged = false;
    529         serviceSet = null;
    530         super.clear();
    531         putId();
    532         if (registered) {
    533           Security.increaseVersion();
    534         }
    535     }
    536 
    537     // used as key in the serviceMap and legacyMap HashMaps
    538     private static class ServiceKey {
    539         private final String type;
    540         private final String algorithm;
    541         private final String originalAlgorithm;
    542         private ServiceKey(String type, String algorithm, boolean intern) {
    543             this.type = type;
    544             this.originalAlgorithm = algorithm;
    545             algorithm = algorithm.toUpperCase(ENGLISH);
    546             this.algorithm = intern ? algorithm.intern() : algorithm;
    547         }
    548         public int hashCode() {
    549             return type.hashCode() + algorithm.hashCode();
    550         }
    551         public boolean equals(Object obj) {
    552             if (this == obj) {
    553                 return true;
    554             }
    555             if (obj instanceof ServiceKey == false) {
    556                 return false;
    557             }
    558             ServiceKey other = (ServiceKey)obj;
    559             return this.type.equals(other.type)
    560                 && this.algorithm.equals(other.algorithm);
    561         }
    562         boolean matches(String type, String algorithm) {
    563             return (this.type == type) && (this.originalAlgorithm == algorithm);
    564         }
    565     }
    566 
    567     /**
    568      * Ensure all the legacy String properties are fully parsed into
    569      * service objects.
    570      */
    571     private void ensureLegacyParsed() {
    572         if ((legacyChanged == false) || (legacyStrings == null)) {
    573             return;
    574         }
    575         serviceSet = null;
    576         if (legacyMap == null) {
    577             legacyMap = new LinkedHashMap<ServiceKey,Service>();
    578         } else {
    579             legacyMap.clear();
    580         }
    581         for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
    582             parseLegacyPut(entry.getKey(), entry.getValue());
    583         }
    584         removeInvalidServices(legacyMap);
    585         legacyChanged = false;
    586     }
    587 
    588     /**
    589      * Remove all invalid services from the Map. Invalid services can only
    590      * occur if the legacy properties are inconsistent or incomplete.
    591      */
    592     private void removeInvalidServices(Map<ServiceKey,Service> map) {
    593         for (Iterator t = map.entrySet().iterator(); t.hasNext(); ) {
    594             Map.Entry entry = (Map.Entry)t.next();
    595             Service s = (Service)entry.getValue();
    596             if (s.isValid() == false) {
    597                 t.remove();
    598             }
    599         }
    600     }
    601 
    602     private String[] getTypeAndAlgorithm(String key) {
    603         int i = key.indexOf(".");
    604         if (i < 1) {
    605             if (debug != null) {
    606                 debug.println("Ignoring invalid entry in provider "
    607                         + name + ":" + key);
    608             }
    609             return null;
    610         }
    611         String type = key.substring(0, i);
    612         String alg = key.substring(i + 1);
    613         return new String[] {type, alg};
    614     }
    615 
    616     private final static String ALIAS_PREFIX = "Alg.Alias.";
    617     private final static String ALIAS_PREFIX_LOWER = "alg.alias.";
    618     private final static int ALIAS_LENGTH = ALIAS_PREFIX.length();
    619 
    620     private void parseLegacyPut(String name, String value) {
    621         if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
    622             // e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
    623             // aliasKey ~ MessageDigest.SHA
    624             String stdAlg = value;
    625             String aliasKey = name.substring(ALIAS_LENGTH);
    626             String[] typeAndAlg = getTypeAndAlgorithm(aliasKey);
    627             if (typeAndAlg == null) {
    628                 return;
    629             }
    630             String type = typeAndAlg[0];
    631             String aliasAlg = typeAndAlg[1].intern();
    632             ServiceKey key = new ServiceKey(type, stdAlg, true);
    633             Service s = legacyMap.get(key);
    634             if (s == null) {
    635                 s = new Service(this);
    636                 s.type = type;
    637                 s.algorithm = stdAlg;
    638                 legacyMap.put(key, s);
    639             }
    640             legacyMap.put(new ServiceKey(type, aliasAlg, true), s);
    641             s.addAlias(aliasAlg);
    642         } else {
    643             String[] typeAndAlg = getTypeAndAlgorithm(name);
    644             if (typeAndAlg == null) {
    645                 return;
    646             }
    647             int i = typeAndAlg[1].indexOf(' ');
    648             if (i == -1) {
    649                 // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
    650                 String type = typeAndAlg[0];
    651                 String stdAlg = typeAndAlg[1].intern();
    652                 String className = value;
    653                 ServiceKey key = new ServiceKey(type, stdAlg, true);
    654                 Service s = legacyMap.get(key);
    655                 if (s == null) {
    656                     s = new Service(this);
    657                     s.type = type;
    658                     s.algorithm = stdAlg;
    659                     legacyMap.put(key, s);
    660                 }
    661                 s.className = className;
    662             } else { // attribute
    663                 // e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
    664                 String attributeValue = value;
    665                 String type = typeAndAlg[0];
    666                 String attributeString = typeAndAlg[1];
    667                 String stdAlg = attributeString.substring(0, i).intern();
    668                 String attributeName = attributeString.substring(i + 1);
    669                 // kill additional spaces
    670                 while (attributeName.startsWith(" ")) {
    671                     attributeName = attributeName.substring(1);
    672                 }
    673                 attributeName = attributeName.intern();
    674                 ServiceKey key = new ServiceKey(type, stdAlg, true);
    675                 Service s = legacyMap.get(key);
    676                 if (s == null) {
    677                     s = new Service(this);
    678                     s.type = type;
    679                     s.algorithm = stdAlg;
    680                     legacyMap.put(key, s);
    681                 }
    682                 s.addAttribute(attributeName, attributeValue);
    683             }
    684         }
    685     }
    686 
    687     /**
    688      * Get the service describing this Provider's implementation of the
    689      * specified type of this algorithm or alias. If no such
    690      * implementation exists, this method returns null. If there are two
    691      * matching services, one added to this provider using
    692      * {@link #putService putService()} and one added via {@link #put put()},
    693      * the service added via {@link #putService putService()} is returned.
    694      *
    695      * @param type the type of {@link Service service} requested
    696      * (for example, <code>MessageDigest</code>)
    697      * @param algorithm the case insensitive algorithm name (or alternate
    698      * alias) of the service requested (for example, <code>SHA-1</code>)
    699      *
    700      * @return the service describing this Provider's matching service
    701      * or null if no such service exists
    702      *
    703      * @throws NullPointerException if type or algorithm is null
    704      *
    705      * @since 1.5
    706      */
    707     public synchronized Service getService(String type, String algorithm) {
    708         checkInitialized();
    709         // avoid allocating a new key object if possible
    710         ServiceKey key = previousKey;
    711         if (key.matches(type, algorithm) == false) {
    712             key = new ServiceKey(type, algorithm, false);
    713             previousKey = key;
    714         }
    715         if (serviceMap != null) {
    716             Service service = serviceMap.get(key);
    717             if (service != null) {
    718                 return service;
    719             }
    720         }
    721         ensureLegacyParsed();
    722         return (legacyMap != null) ? legacyMap.get(key) : null;
    723     }
    724 
    725     // ServiceKey from previous getService() call
    726     // by re-using it if possible we avoid allocating a new object
    727     // and the toUpperCase() call.
    728     // re-use will occur e.g. as the framework traverses the provider
    729     // list and queries each provider with the same values until it finds
    730     // a matching service
    731     private static volatile ServiceKey previousKey =
    732                                             new ServiceKey("", "", false);
    733 
    734     /**
    735      * Get an unmodifiable Set of all services supported by
    736      * this Provider.
    737      *
    738      * @return an unmodifiable Set of all services supported by
    739      * this Provider
    740      *
    741      * @since 1.5
    742      */
    743     public synchronized Set<Service> getServices() {
    744         checkInitialized();
    745         if (legacyChanged || servicesChanged) {
    746             serviceSet = null;
    747         }
    748         if (serviceSet == null) {
    749             ensureLegacyParsed();
    750             Set<Service> set = new LinkedHashSet<>();
    751             if (serviceMap != null) {
    752                 set.addAll(serviceMap.values());
    753             }
    754             if (legacyMap != null) {
    755                 set.addAll(legacyMap.values());
    756             }
    757             serviceSet = Collections.unmodifiableSet(set);
    758             servicesChanged = false;
    759         }
    760         return serviceSet;
    761     }
    762 
    763     /**
    764      * Add a service. If a service of the same type with the same algorithm
    765      * name exists and it was added using {@link #putService putService()},
    766      * it is replaced by the new service.
    767      * This method also places information about this service
    768      * in the provider's Hashtable values in the format described in the
    769      * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html">
    770      * Java Cryptography Architecture API Specification &amp; Reference </a>.
    771      *
    772      * <p>Also, if there is a security manager, its
    773      * <code>checkSecurityAccess</code> method is called with the string
    774      * <code>"putProviderProperty."+name</code>, where <code>name</code> is
    775      * the provider name, to see if it's ok to set this provider's property
    776      * values. If the default implementation of <code>checkSecurityAccess</code>
    777      * is used (that is, that method is not overriden), then this results in
    778      * a call to the security manager's <code>checkPermission</code> method with
    779      * a <code>SecurityPermission("putProviderProperty."+name)</code>
    780      * permission.
    781      *
    782      * @param s the Service to add
    783      *
    784      * @throws SecurityException
    785      *      if a security manager exists and its <code>{@link
    786      *      java.lang.SecurityManager#checkSecurityAccess}</code> method denies
    787      *      access to set property values.
    788      * @throws NullPointerException if s is null
    789      *
    790      * @since 1.5
    791      */
    792     protected synchronized void putService(Service s) {
    793         check("putProviderProperty." + name);
    794         if (debug != null) {
    795             debug.println(name + ".putService(): " + s);
    796         }
    797         if (s == null) {
    798             throw new NullPointerException();
    799         }
    800         if (s.getProvider() != this) {
    801             throw new IllegalArgumentException
    802                     ("service.getProvider() must match this Provider object");
    803         }
    804         if (serviceMap == null) {
    805             serviceMap = new LinkedHashMap<ServiceKey,Service>();
    806         }
    807         servicesChanged = true;
    808         String type = s.getType();
    809         String algorithm = s.getAlgorithm();
    810         ServiceKey key = new ServiceKey(type, algorithm, true);
    811         // remove existing service
    812         implRemoveService(serviceMap.get(key));
    813         serviceMap.put(key, s);
    814         for (String alias : s.getAliases()) {
    815             serviceMap.put(new ServiceKey(type, alias, true), s);
    816         }
    817         putPropertyStrings(s);
    818     }
    819 
    820     /**
    821      * Put the string properties for this Service in this Provider's
    822      * Hashtable.
    823      */
    824     private void putPropertyStrings(Service s) {
    825         String type = s.getType();
    826         String algorithm = s.getAlgorithm();
    827         // use super() to avoid permission check and other processing
    828         super.put(type + "." + algorithm, s.getClassName());
    829         for (String alias : s.getAliases()) {
    830             super.put(ALIAS_PREFIX + type + "." + alias, algorithm);
    831         }
    832         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
    833             String key = type + "." + algorithm + " " + entry.getKey();
    834             super.put(key, entry.getValue());
    835         }
    836         if (registered) {
    837             Security.increaseVersion();
    838         }
    839     }
    840 
    841     /**
    842      * Remove the string properties for this Service from this Provider's
    843      * Hashtable.
    844      */
    845     private void removePropertyStrings(Service s) {
    846         String type = s.getType();
    847         String algorithm = s.getAlgorithm();
    848         // use super() to avoid permission check and other processing
    849         super.remove(type + "." + algorithm);
    850         for (String alias : s.getAliases()) {
    851             super.remove(ALIAS_PREFIX + type + "." + alias);
    852         }
    853         for (Map.Entry<UString,String> entry : s.attributes.entrySet()) {
    854             String key = type + "." + algorithm + " " + entry.getKey();
    855             super.remove(key);
    856         }
    857         if (registered) {
    858           Security.increaseVersion();
    859         }
    860     }
    861 
    862     /**
    863      * Remove a service previously added using
    864      * {@link #putService putService()}. The specified service is removed from
    865      * this provider. It will no longer be returned by
    866      * {@link #getService getService()} and its information will be removed
    867      * from this provider's Hashtable.
    868      *
    869      * <p>Also, if there is a security manager, its
    870      * <code>checkSecurityAccess</code> method is called with the string
    871      * <code>"removeProviderProperty."+name</code>, where <code>name</code> is
    872      * the provider name, to see if it's ok to remove this provider's
    873      * properties. If the default implementation of
    874      * <code>checkSecurityAccess</code> is used (that is, that method is not
    875      * overriden), then this results in a call to the security manager's
    876      * <code>checkPermission</code> method with a
    877      * <code>SecurityPermission("removeProviderProperty."+name)</code>
    878      * permission.
    879      *
    880      * @param s the Service to be removed
    881      *
    882      * @throws  SecurityException
    883      *          if a security manager exists and its <code>{@link
    884      *          java.lang.SecurityManager#checkSecurityAccess}</code> method denies
    885      *          access to remove this provider's properties.
    886      * @throws NullPointerException if s is null
    887      *
    888      * @since 1.5
    889      */
    890     protected synchronized void removeService(Service s) {
    891         check("removeProviderProperty." + name);
    892         if (debug != null) {
    893             debug.println(name + ".removeService(): " + s);
    894         }
    895         if (s == null) {
    896             throw new NullPointerException();
    897         }
    898         implRemoveService(s);
    899     }
    900 
    901     private void implRemoveService(Service s) {
    902         if ((s == null) || (serviceMap == null)) {
    903             return;
    904         }
    905         String type = s.getType();
    906         String algorithm = s.getAlgorithm();
    907         ServiceKey key = new ServiceKey(type, algorithm, false);
    908         Service oldService = serviceMap.get(key);
    909         if (s != oldService) {
    910             return;
    911         }
    912         servicesChanged = true;
    913         serviceMap.remove(key);
    914         for (String alias : s.getAliases()) {
    915             serviceMap.remove(new ServiceKey(type, alias, false));
    916         }
    917         removePropertyStrings(s);
    918     }
    919 
    920     // Wrapped String that behaves in a case insensitive way for equals/hashCode
    921     private static class UString {
    922         final String string;
    923         final String lowerString;
    924 
    925         UString(String s) {
    926             this.string = s;
    927             this.lowerString = s.toLowerCase(ENGLISH);
    928         }
    929 
    930         public int hashCode() {
    931             return lowerString.hashCode();
    932         }
    933 
    934         public boolean equals(Object obj) {
    935             if (this == obj) {
    936                 return true;
    937             }
    938             if (obj instanceof UString == false) {
    939                 return false;
    940             }
    941             UString other = (UString)obj;
    942             return lowerString.equals(other.lowerString);
    943         }
    944 
    945         public String toString() {
    946             return string;
    947         }
    948     }
    949 
    950     // describe relevant properties of a type of engine
    951     private static class EngineDescription {
    952         final String name;
    953         final boolean supportsParameter;
    954         final String constructorParameterClassName;
    955         private volatile Class constructorParameterClass;
    956 
    957         EngineDescription(String name, boolean sp, String paramName) {
    958             this.name = name;
    959             this.supportsParameter = sp;
    960             this.constructorParameterClassName = paramName;
    961         }
    962         Class getConstructorParameterClass() throws ClassNotFoundException {
    963             Class clazz = constructorParameterClass;
    964             if (clazz == null) {
    965                 clazz = Class.forName(constructorParameterClassName);
    966                 constructorParameterClass = clazz;
    967             }
    968             return clazz;
    969         }
    970     }
    971 
    972     // built in knowledge of the engine types shipped as part of the JDK
    973     private static final Map<String,EngineDescription> knownEngines;
    974 
    975     private static void addEngine(String name, boolean sp, String paramName) {
    976         EngineDescription ed = new EngineDescription(name, sp, paramName);
    977         // NOTE: The original OpenJDK code supported case-insensitive lookups on the list
    978         // of known engines.
    979         //
    980         // knownEngines.put(name.toLowerCase(ENGLISH), ed);
    981         knownEngines.put(name, ed);
    982     }
    983 
    984     static {
    985         knownEngines = new HashMap<String,EngineDescription>();
    986         // JCA
    987         addEngine("AlgorithmParameterGenerator",        false, null);
    988         addEngine("AlgorithmParameters",                false, null);
    989         addEngine("KeyFactory",                         false, null);
    990         addEngine("KeyPairGenerator",                   false, null);
    991         addEngine("KeyStore",                           false, null);
    992         addEngine("MessageDigest",                      false, null);
    993         addEngine("SecureRandom",                       false, null);
    994         addEngine("Signature",                          true,  null);
    995         addEngine("CertificateFactory",                 false, null);
    996         addEngine("CertPathBuilder",                    false, null);
    997         addEngine("CertPathValidator",                  false, null);
    998         addEngine("CertStore",                          false,
    999                             "java.security.cert.CertStoreParameters");
   1000         // JCE
   1001         addEngine("Cipher",                             true,  null);
   1002         addEngine("ExemptionMechanism",                 false, null);
   1003         addEngine("Mac",                                true,  null);
   1004         addEngine("KeyAgreement",                       true,  null);
   1005         addEngine("KeyGenerator",                       false, null);
   1006         addEngine("SecretKeyFactory",                   false, null);
   1007         // JSSE
   1008         addEngine("KeyManagerFactory",                  false, null);
   1009         addEngine("SSLContext",                         false, null);
   1010         addEngine("TrustManagerFactory",                false, null);
   1011         // JGSS
   1012         addEngine("GssApiMechanism",                    false, null);
   1013         // SASL
   1014         addEngine("SaslClientFactory",                  false, null);
   1015         addEngine("SaslServerFactory",                  false, null);
   1016         // POLICY
   1017         addEngine("Policy",                             false,
   1018                             "java.security.Policy$Parameters");
   1019         // CONFIGURATION
   1020         addEngine("Configuration",                      false,
   1021                             "javax.security.auth.login.Configuration$Parameters");
   1022         // XML DSig
   1023         addEngine("XMLSignatureFactory",                false, null);
   1024         addEngine("KeyInfoFactory",                     false, null);
   1025         addEngine("TransformService",                   false, null);
   1026         // Smart Card I/O
   1027         addEngine("TerminalFactory",                    false,
   1028                             "java.lang.Object");
   1029     }
   1030 
   1031     /**
   1032      * The description of a security service. It encapsulates the properties
   1033      * of a service and contains a factory method to obtain new implementation
   1034      * instances of this service.
   1035      *
   1036      * <p>Each service has a provider that offers the service, a type,
   1037      * an algorithm name, and the name of the class that implements the
   1038      * service. Optionally, it also includes a list of alternate algorithm
   1039      * names for this service (aliases) and attributes, which are a map of
   1040      * (name, value) String pairs.
   1041      *
   1042      * <p>This class defines the methods {@link #supportsParameter
   1043      * supportsParameter()} and {@link #newInstance newInstance()}
   1044      * which are used by the Java security framework when it searches for
   1045      * suitable services and instantes them. The valid arguments to those
   1046      * methods depend on the type of service. For the service types defined
   1047      * within Java SE, see the
   1048      * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html">
   1049      * Java Cryptography Architecture API Specification &amp; Reference </a>
   1050      * for the valid values.
   1051      * Note that components outside of Java SE can define additional types of
   1052      * services and their behavior.
   1053      *
   1054      * <p>Instances of this class are immutable.
   1055      *
   1056      * @since 1.5
   1057      */
   1058     public static class Service {
   1059 
   1060         private String type, algorithm, className;
   1061         private final Provider provider;
   1062         private List<String> aliases;
   1063         private Map<UString,String> attributes;
   1064 
   1065         // Reference to the cached implementation Class object
   1066         private volatile Reference<Class> classRef;
   1067 
   1068         // flag indicating whether this service has its attributes for
   1069         // supportedKeyFormats or supportedKeyClasses set
   1070         // if null, the values have not been initialized
   1071         // if TRUE, at least one of supportedFormats/Classes is non null
   1072         private volatile Boolean hasKeyAttributes;
   1073 
   1074         // supported encoding formats
   1075         private String[] supportedFormats;
   1076 
   1077         // names of the supported key (super) classes
   1078         private Class[] supportedClasses;
   1079 
   1080         // whether this service has been registered with the Provider
   1081         private boolean registered;
   1082 
   1083         private static final Class[] CLASS0 = new Class[0];
   1084 
   1085         // this constructor and these methods are used for parsing
   1086         // the legacy string properties.
   1087 
   1088         private Service(Provider provider) {
   1089             this.provider = provider;
   1090             aliases = Collections.<String>emptyList();
   1091             attributes = Collections.<UString,String>emptyMap();
   1092         }
   1093 
   1094         private boolean isValid() {
   1095             return (type != null) && (algorithm != null) && (className != null);
   1096         }
   1097 
   1098         private void addAlias(String alias) {
   1099             if (aliases.isEmpty()) {
   1100                 aliases = new ArrayList<String>(2);
   1101             }
   1102             aliases.add(alias);
   1103         }
   1104 
   1105         void addAttribute(String type, String value) {
   1106             if (attributes.isEmpty()) {
   1107                 attributes = new HashMap<UString,String>(8);
   1108             }
   1109             attributes.put(new UString(type), value);
   1110         }
   1111 
   1112         /**
   1113          * Construct a new service.
   1114          *
   1115          * @param provider the provider that offers this service
   1116          * @param type the type of this service
   1117          * @param algorithm the algorithm name
   1118          * @param className the name of the class implementing this service
   1119          * @param aliases List of aliases or null if algorithm has no aliases
   1120          * @param attributes Map of attributes or null if this implementation
   1121          *                   has no attributes
   1122          *
   1123          * @throws NullPointerException if provider, type, algorithm, or
   1124          * className is null
   1125          */
   1126         public Service(Provider provider, String type, String algorithm,
   1127                 String className, List<String> aliases,
   1128                 Map<String,String> attributes) {
   1129             if ((provider == null) || (type == null) ||
   1130                     (algorithm == null) || (className == null)) {
   1131                 throw new NullPointerException();
   1132             }
   1133             this.provider = provider;
   1134             // Android-changed.
   1135             this.type = type;
   1136             this.algorithm = algorithm;
   1137             this.className = className;
   1138             if (aliases == null) {
   1139                 this.aliases = Collections.<String>emptyList();
   1140             } else {
   1141                 this.aliases = new ArrayList<String>(aliases);
   1142             }
   1143             if (attributes == null) {
   1144                 this.attributes = Collections.<UString,String>emptyMap();
   1145             } else {
   1146                 this.attributes = new HashMap<UString,String>();
   1147                 for (Map.Entry<String,String> entry : attributes.entrySet()) {
   1148                     this.attributes.put(new UString(entry.getKey()), entry.getValue());
   1149                 }
   1150             }
   1151         }
   1152 
   1153         /**
   1154          * Get the type of this service. For example, <code>MessageDigest</code>.
   1155          *
   1156          * @return the type of this service
   1157          */
   1158         public final String getType() {
   1159             return type;
   1160         }
   1161 
   1162         /**
   1163          * Return the name of the algorithm of this service. For example,
   1164          * <code>SHA-1</code>.
   1165          *
   1166          * @return the algorithm of this service
   1167          */
   1168         public final String getAlgorithm() {
   1169             return algorithm;
   1170         }
   1171 
   1172         /**
   1173          * Return the Provider of this service.
   1174          *
   1175          * @return the Provider of this service
   1176          */
   1177         public final Provider getProvider() {
   1178             return provider;
   1179         }
   1180 
   1181         /**
   1182          * Return the name of the class implementing this service.
   1183          *
   1184          * @return the name of the class implementing this service
   1185          */
   1186         public final String getClassName() {
   1187             return className;
   1188         }
   1189 
   1190         // internal only
   1191         private final List<String> getAliases() {
   1192             return aliases;
   1193         }
   1194 
   1195         /**
   1196          * Return the value of the specified attribute or null if this
   1197          * attribute is not set for this Service.
   1198          *
   1199          * @param name the name of the requested attribute
   1200          *
   1201          * @return the value of the specified attribute or null if the
   1202          *         attribute is not present
   1203          *
   1204          * @throws NullPointerException if name is null
   1205          */
   1206         public final String getAttribute(String name) {
   1207             if (name == null) {
   1208                 throw new NullPointerException();
   1209             }
   1210             return attributes.get(new UString(name));
   1211         }
   1212 
   1213         /**
   1214          * Return a new instance of the implementation described by this
   1215          * service. The security provider framework uses this method to
   1216          * construct implementations. Applications will typically not need
   1217          * to call it.
   1218          *
   1219          * <p>The default implementation uses reflection to invoke the
   1220          * standard constructor for this type of service.
   1221          * Security providers can override this method to implement
   1222          * instantiation in a different way.
   1223          * For details and the values of constructorParameter that are
   1224          * valid for the various types of services see the
   1225          * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html">
   1226          * Java Cryptography Architecture API Specification &amp;
   1227          * Reference</a>.
   1228          *
   1229          * @param constructorParameter the value to pass to the constructor,
   1230          * or null if this type of service does not use a constructorParameter.
   1231          *
   1232          * @return a new implementation of this service
   1233          *
   1234          * @throws InvalidParameterException if the value of
   1235          * constructorParameter is invalid for this type of service.
   1236          * @throws NoSuchAlgorithmException if instantation failed for
   1237          * any other reason.
   1238          */
   1239         public Object newInstance(Object constructorParameter)
   1240                 throws NoSuchAlgorithmException {
   1241             if (registered == false) {
   1242                 if (provider.getService(type, algorithm) != this) {
   1243                     throw new NoSuchAlgorithmException
   1244                         ("Service not registered with Provider "
   1245                         + provider.getName() + ": " + this);
   1246                 }
   1247                 registered = true;
   1248             }
   1249             try {
   1250                 EngineDescription cap = knownEngines.get(type);
   1251                 if (cap == null) {
   1252                     // unknown engine type, use generic code
   1253                     // this is the code path future for non-core
   1254                     // optional packages
   1255                     return newInstanceGeneric(constructorParameter);
   1256                 }
   1257                 if (cap.constructorParameterClassName == null) {
   1258                     if (constructorParameter != null) {
   1259                         throw new InvalidParameterException
   1260                             ("constructorParameter not used with " + type
   1261                             + " engines");
   1262                     }
   1263                     Class clazz = getImplClass();
   1264                     return clazz.newInstance();
   1265                 } else {
   1266                     Class paramClass = cap.getConstructorParameterClass();
   1267                     if (constructorParameter != null) {
   1268                         Class argClass = constructorParameter.getClass();
   1269                         if (paramClass.isAssignableFrom(argClass) == false) {
   1270                             throw new InvalidParameterException
   1271                             ("constructorParameter must be instanceof "
   1272                             + cap.constructorParameterClassName.replace('$', '.')
   1273                             + " for engine type " + type);
   1274                         }
   1275                     }
   1276                     Class clazz = getImplClass();
   1277                     Constructor cons = clazz.getConstructor(paramClass);
   1278                     return cons.newInstance(constructorParameter);
   1279                 }
   1280             } catch (NoSuchAlgorithmException e) {
   1281                 throw e;
   1282             } catch (InvocationTargetException e) {
   1283                 throw new NoSuchAlgorithmException
   1284                     ("Error constructing implementation (algorithm: "
   1285                     + algorithm + ", provider: " + provider.getName()
   1286                     + ", class: " + className + ")", e.getCause());
   1287             } catch (Exception e) {
   1288                 throw new NoSuchAlgorithmException
   1289                     ("Error constructing implementation (algorithm: "
   1290                     + algorithm + ", provider: " + provider.getName()
   1291                     + ", class: " + className + ")", e);
   1292             }
   1293         }
   1294 
   1295         // return the implementation Class object for this service
   1296         private Class getImplClass() throws NoSuchAlgorithmException {
   1297             try {
   1298                 Reference<Class> ref = classRef;
   1299                 Class clazz = (ref == null) ? null : ref.get();
   1300                 if (clazz == null) {
   1301                     ClassLoader cl = provider.getClass().getClassLoader();
   1302                     if (cl == null) {
   1303                         clazz = Class.forName(className);
   1304                     } else {
   1305                         clazz = cl.loadClass(className);
   1306                     }
   1307                     classRef = new WeakReference<Class>(clazz);
   1308                 }
   1309                 return clazz;
   1310             } catch (ClassNotFoundException e) {
   1311                 throw new NoSuchAlgorithmException
   1312                     ("class configured for " + type + "(provider: " +
   1313                     provider.getName() + ")" + "cannot be found.", e);
   1314             }
   1315         }
   1316 
   1317         /**
   1318          * Generic code path for unknown engine types. Call the
   1319          * no-args constructor if constructorParameter is null, otherwise
   1320          * use the first matching constructor.
   1321          */
   1322         private Object newInstanceGeneric(Object constructorParameter)
   1323                 throws Exception {
   1324             Class clazz = getImplClass();
   1325             if (constructorParameter == null) {
   1326                 Object o = clazz.newInstance();
   1327                 return o;
   1328             }
   1329             Class argClass = constructorParameter.getClass();
   1330             Constructor[] cons = clazz.getConstructors();
   1331             // find first public constructor that can take the
   1332             // argument as parameter
   1333             for (int i = 0; i < cons.length; i++) {
   1334                 Constructor con = cons[i];
   1335                 Class[] paramTypes = con.getParameterTypes();
   1336                 if (paramTypes.length != 1) {
   1337                     continue;
   1338                 }
   1339                 if (paramTypes[0].isAssignableFrom(argClass) == false) {
   1340                     continue;
   1341                 }
   1342                 Object o = con.newInstance(new Object[] {constructorParameter});
   1343                 return o;
   1344             }
   1345             throw new NoSuchAlgorithmException("No constructor matching "
   1346                 + argClass.getName() + " found in class " + className);
   1347         }
   1348 
   1349         /**
   1350          * Test whether this Service can use the specified parameter.
   1351          * Returns false if this service cannot use the parameter. Returns
   1352          * true if this service can use the parameter, if a fast test is
   1353          * infeasible, or if the status is unknown.
   1354          *
   1355          * <p>The security provider framework uses this method with
   1356          * some types of services to quickly exclude non-matching
   1357          * implementations for consideration.
   1358          * Applications will typically not need to call it.
   1359          *
   1360          * <p>For details and the values of parameter that are valid for the
   1361          * various types of services see the top of this class and the
   1362          * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html">
   1363          * Java Cryptography Architecture API Specification &amp;
   1364          * Reference</a>.
   1365          * Security providers can override it to implement their own test.
   1366          *
   1367          * @param parameter the parameter to test
   1368          *
   1369          * @return false if this this service cannot use the specified
   1370          * parameter; true if it can possibly use the parameter
   1371          *
   1372          * @throws InvalidParameterException if the value of parameter is
   1373          * invalid for this type of service or if this method cannot be
   1374          * used with this type of service
   1375          */
   1376         public boolean supportsParameter(Object parameter) {
   1377             EngineDescription cap = knownEngines.get(type);
   1378             if (cap == null) {
   1379                 // unknown engine type, return true by default
   1380                 return true;
   1381             }
   1382             if (cap.supportsParameter == false) {
   1383                 throw new InvalidParameterException("supportsParameter() not "
   1384                     + "used with " + type + " engines");
   1385             }
   1386             // allow null for keys without attributes for compatibility
   1387             if ((parameter != null) && (parameter instanceof Key == false)) {
   1388                 throw new InvalidParameterException
   1389                     ("Parameter must be instanceof Key for engine " + type);
   1390             }
   1391             if (hasKeyAttributes() == false) {
   1392                 return true;
   1393             }
   1394             if (parameter == null) {
   1395                 return false;
   1396             }
   1397             Key key = (Key)parameter;
   1398             if (supportsKeyFormat(key)) {
   1399                 return true;
   1400             }
   1401             if (supportsKeyClass(key)) {
   1402                 return true;
   1403             }
   1404             return false;
   1405         }
   1406 
   1407         /**
   1408          * Return whether this service has its Supported* properties for
   1409          * keys defined. Parses the attributes if not yet initialized.
   1410          */
   1411         private boolean hasKeyAttributes() {
   1412             Boolean b = hasKeyAttributes;
   1413             if (b == null) {
   1414                 synchronized (this) {
   1415                     String s;
   1416                     s = getAttribute("SupportedKeyFormats");
   1417                     if (s != null) {
   1418                         supportedFormats = s.split("\\|");
   1419                     }
   1420                     s = getAttribute("SupportedKeyClasses");
   1421                     if (s != null) {
   1422                         String[] classNames = s.split("\\|");
   1423                         List<Class> classList =
   1424                             new ArrayList<>(classNames.length);
   1425                         for (String className : classNames) {
   1426                             Class clazz = getKeyClass(className);
   1427                             if (clazz != null) {
   1428                                 classList.add(clazz);
   1429                             }
   1430                         }
   1431                         supportedClasses = classList.toArray(CLASS0);
   1432                     }
   1433                     boolean bool = (supportedFormats != null)
   1434                         || (supportedClasses != null);
   1435                     b = Boolean.valueOf(bool);
   1436                     hasKeyAttributes = b;
   1437                 }
   1438             }
   1439             return b.booleanValue();
   1440         }
   1441 
   1442         // get the key class object of the specified name
   1443         private Class getKeyClass(String name) {
   1444             try {
   1445                 return Class.forName(name);
   1446             } catch (ClassNotFoundException e) {
   1447                 // ignore
   1448             }
   1449             try {
   1450                 ClassLoader cl = provider.getClass().getClassLoader();
   1451                 if (cl != null) {
   1452                     return cl.loadClass(name);
   1453                 }
   1454             } catch (ClassNotFoundException e) {
   1455                 // ignore
   1456             }
   1457             return null;
   1458         }
   1459 
   1460         private boolean supportsKeyFormat(Key key) {
   1461             if (supportedFormats == null) {
   1462                 return false;
   1463             }
   1464             String format = key.getFormat();
   1465             if (format == null) {
   1466                 return false;
   1467             }
   1468             for (String supportedFormat : supportedFormats) {
   1469                 if (supportedFormat.equals(format)) {
   1470                     return true;
   1471                 }
   1472             }
   1473             return false;
   1474         }
   1475 
   1476         private boolean supportsKeyClass(Key key) {
   1477             if (supportedClasses == null) {
   1478                 return false;
   1479             }
   1480             Class keyClass = key.getClass();
   1481             for (Class clazz : supportedClasses) {
   1482                 if (clazz.isAssignableFrom(keyClass)) {
   1483                     return true;
   1484                 }
   1485             }
   1486             return false;
   1487         }
   1488 
   1489         /**
   1490          * Return a String representation of this service.
   1491          *
   1492          * @return a String representation of this service.
   1493          */
   1494         public String toString() {
   1495             String aString = aliases.isEmpty()
   1496                 ? "" : "\r\n  aliases: " + aliases.toString();
   1497             String attrs = attributes.isEmpty()
   1498                 ? "" : "\r\n  attributes: " + attributes.toString();
   1499             return provider.getName() + ": " + type + "." + algorithm
   1500                 + " -> " + className + aString + attrs + "\r\n";
   1501         }
   1502 
   1503     }
   1504 
   1505     /**
   1506      * @hide
   1507      */
   1508     public void setRegistered() {
   1509         registered = true;
   1510     }
   1511 
   1512     /**
   1513      * @hide
   1514      */
   1515     public void setUnregistered() {
   1516         registered = false;
   1517     }
   1518 
   1519     /**
   1520      * @hide
   1521      */
   1522     public boolean isRegistered() {
   1523         return registered;
   1524     }
   1525 
   1526     /**
   1527      * Ensure the values cached by {@link #getServices} and {@link #getService} are already computed
   1528      *
   1529      * Used by the zygote so that initialization is performed during preload for the providers
   1530      * available at that point.
   1531      *
   1532      * @hide
   1533      */
   1534     public synchronized void warmUpServiceProvision() {
   1535         checkInitialized();
   1536         // Further calls do nothing if the services didn't change. If not called here, it would
   1537         // parse legacy strings the first time that a service is requested.
   1538         ensureLegacyParsed();
   1539         // This call to getServices will update fields so that further calls will just return a
   1540         // stored field, if the services didn't change in the meantime.
   1541         getServices();
   1542     }
   1543 }
   1544