Home | History | Annotate | Download | only in security
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.security;
     19 
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.io.NotActiveException;
     23 import java.util.ArrayList;
     24 import java.util.Collection;
     25 import java.util.Collections;
     26 import java.util.Enumeration;
     27 import java.util.HashMap;
     28 import java.util.Iterator;
     29 import java.util.LinkedHashMap;
     30 import java.util.LinkedHashSet;
     31 import java.util.List;
     32 import java.util.Locale;
     33 import java.util.Map;
     34 import java.util.Properties;
     35 import java.util.Set;
     36 import org.apache.harmony.security.fortress.Services;
     37 
     38 /**
     39  * {@code Provider} is the abstract superclass for all security providers in the
     40  * Java security infrastructure.
     41  */
     42 public abstract class Provider extends Properties {
     43     private static final long serialVersionUID = -4298000515446427739L;
     44 
     45     private String name;
     46 
     47     private double version;
     48 
     49     // String representation of the provider version number.
     50     private transient String versionString;
     51 
     52     private String info;
     53 
     54     //The provider preference order number.
     55     // Equals -1 for non registered provider.
     56     private transient int providerNumber = -1;
     57 
     58     // Contains "Service.Algorithm" and Provider.Service classes added using
     59     // putService()
     60     private transient LinkedHashMap<String, Service> serviceTable;
     61 
     62     // Contains "Service.Alias" and Provider.Service classes added using
     63     // putService()
     64     private transient LinkedHashMap<String, Service> aliasTable;
     65 
     66     // Contains "Service.Algorithm" and Provider.Service classes added using
     67     // put()
     68     private transient LinkedHashMap<String, Service> propertyServiceTable;
     69 
     70     // Contains "Service.Alias" and Provider.Service classes added using put()
     71     private transient LinkedHashMap<String, Service> propertyAliasTable;
     72 
     73     // The properties changed via put()
     74     private transient LinkedHashMap<Object, Object> changedProperties;
     75 
     76     // For getService(String type, String algorithm) optimization:
     77     // previous result
     78     private transient Provider.Service returnedService;
     79     // previous parameters
     80     private transient String lastAlgorithm;
     81     // last name
     82     private transient String lastServiceName;
     83 
     84     // For getServices() optimization:
     85     private transient Set<Service> lastServicesSet;
     86 
     87     // For getService(String type) optimization:
     88     private transient String lastType;
     89     // last Service found by type
     90     private transient Provider.Service lastServicesByType;
     91 
     92     /**
     93      * Constructs a new instance of {@code Provider} with its name, version and
     94      * description.
     95      *
     96      * @param name
     97      *            the name of the provider.
     98      * @param version
     99      *            the version of the provider.
    100      * @param info
    101      *            a description of the provider.
    102      */
    103     protected Provider(String name, double version, String info) {
    104         this.name = name;
    105         this.version = version;
    106         this.info = info;
    107         versionString = String.valueOf(version);
    108         putProviderInfo();
    109     }
    110 
    111     /**
    112      * Returns the name of this provider.
    113      *
    114      * @return the name of this provider.
    115      */
    116     public String getName() {
    117         return name;
    118     }
    119 
    120     /**
    121      * Returns the version number for the services being provided.
    122      *
    123      * @return the version number for the services being provided.
    124      */
    125     public double getVersion() {
    126         return version;
    127     }
    128 
    129     /**
    130      * Returns a description of the services being provided.
    131      *
    132      * @return a description of the services being provided.
    133      */
    134     public String getInfo() {
    135         return info;
    136     }
    137 
    138     /**
    139      * Returns a string containing a concise, human-readable description of
    140      * this {@code Provider} including its name and its version.
    141      *
    142      * @return a printable representation for this {@code Provider}.
    143      */
    144     @Override
    145     public String toString() {
    146         return name + " version " + version;
    147     }
    148 
    149     /**
    150      * Clears all properties used to look up services implemented by this
    151      * {@code Provider}.
    152      */
    153     @Override
    154     public synchronized void clear() {
    155         super.clear();
    156         if (serviceTable != null) {
    157             serviceTable.clear();
    158         }
    159         if (propertyServiceTable != null) {
    160             propertyServiceTable.clear();
    161         }
    162         if (aliasTable != null) {
    163             aliasTable.clear();
    164         }
    165         if (propertyAliasTable != null) {
    166             propertyAliasTable.clear();
    167         }
    168         changedProperties = null;
    169         putProviderInfo();
    170         if (providerNumber != -1) {
    171             // if registered then refresh Services
    172             Services.setNeedRefresh();
    173         }
    174         servicesChanged();
    175     }
    176 
    177     @Override
    178     public synchronized void load(InputStream inStream) throws IOException {
    179         Properties tmp = new Properties();
    180         tmp.load(inStream);
    181         myPutAll(tmp);
    182     }
    183 
    184     /**
    185      * Copies all from the provided map to this {@code Provider}.
    186      * @param t
    187      *            the mappings to copy to this provider.
    188      */
    189     @Override
    190     public synchronized void putAll(Map<?,?> t) {
    191         myPutAll(t);
    192     }
    193 
    194     private void myPutAll(Map<?,?> t) {
    195         if (changedProperties == null) {
    196             changedProperties = new LinkedHashMap<Object, Object>();
    197         }
    198         Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator();
    199         Object key;
    200         Object value;
    201         while (it.hasNext()) {
    202             Map.Entry<?, ?> entry = it.next();
    203             key = entry.getKey();
    204             if (key instanceof String && ((String) key).startsWith("Provider.")) {
    205                 // Provider service type is reserved
    206                 continue;
    207             }
    208             value = entry.getValue();
    209             super.put(key, value);
    210             if (changedProperties.remove(key) == null) {
    211                 removeFromPropertyServiceTable(key);
    212             }
    213             changedProperties.put(key, value);
    214         }
    215         if (providerNumber != -1) {
    216             // if registered then refresh Services
    217             Services.setNeedRefresh();
    218         }
    219     }
    220 
    221     @Override
    222     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
    223         return Collections.unmodifiableSet(super.entrySet());
    224     }
    225 
    226     @Override
    227     public Set<Object> keySet() {
    228         return Collections.unmodifiableSet(super.keySet());
    229     }
    230 
    231     @Override
    232     public Collection<Object> values() {
    233         return Collections.unmodifiableCollection(super.values());
    234     }
    235 
    236     /**
    237      * Maps the specified {@code key} property name to the specified {@code
    238      * value}.
    239      *
    240      * @param key
    241      *            the name of the property.
    242      * @param value
    243      *            the value of the property.
    244      * @return the value that was previously mapped to the specified {@code key}
    245      *         ,or {@code null} if it did not have one.
    246      */
    247     @Override
    248     public synchronized Object put(Object key, Object value) {
    249         if (key instanceof String && ((String) key).startsWith("Provider.")) {
    250             // Provider service type is reserved
    251             return null;
    252         }
    253         if (providerNumber != -1) {
    254             // if registered then refresh Services
    255             Services.setNeedRefresh();
    256         }
    257         if (changedProperties != null && changedProperties.remove(key) == null) {
    258             removeFromPropertyServiceTable(key);
    259         }
    260         if (changedProperties == null) {
    261             changedProperties = new LinkedHashMap<Object, Object>();
    262         }
    263         changedProperties.put(key, value);
    264         return super.put(key, value);
    265     }
    266 
    267     /**
    268      * Removes the specified {@code key} and its associated value from this
    269      * {@code Provider}.
    270      *
    271      * @param key
    272      *            the name of the property
    273      * @return the value that was mapped to the specified {@code key} ,or
    274      *         {@code null} if no mapping was present
    275      */
    276     @Override
    277     public synchronized Object remove(Object key) {
    278         if (key instanceof String && ((String) key).startsWith("Provider.")) {
    279             // Provider service type is reserved
    280             return null;
    281         }
    282         if (providerNumber != -1) {
    283             // if registered then refresh Services
    284             Services.setNeedRefresh();
    285         }
    286         if (changedProperties != null && changedProperties.remove(key) == null) {
    287             removeFromPropertyServiceTable(key);
    288             if (changedProperties.size() == 0) {
    289                 changedProperties = null;
    290             }
    291         }
    292         return super.remove(key);
    293     }
    294 
    295     /**
    296      * Returns true if this provider implements the given algorithm. Caller
    297      * must specify the cryptographic service and specify constraints via the
    298      * attribute name and value.
    299      *
    300      * @param serv
    301      *            Crypto service.
    302      * @param alg
    303      *            Algorithm or type.
    304      * @param attribute
    305      *            The attribute name or {@code null}.
    306      * @param val
    307      *            The attribute value.
    308      * @return
    309      */
    310     boolean implementsAlg(String serv, String alg, String attribute, String val) {
    311         String servAlg = serv + "." + alg;
    312         String prop = getPropertyIgnoreCase(servAlg);
    313         if (prop == null) {
    314             alg = getPropertyIgnoreCase("Alg.Alias." + servAlg);
    315             if (alg != null) {
    316                 servAlg = serv + "." + alg;
    317                 prop = getPropertyIgnoreCase(servAlg);
    318             }
    319         }
    320         if (prop != null) {
    321             if (attribute == null) {
    322                 return true;
    323             }
    324             return checkAttribute(servAlg, attribute, val);
    325         }
    326         return false;
    327     }
    328 
    329     /**
    330      * Returns true if this provider has the same value as is given for the
    331      * given attribute
    332      */
    333     private boolean checkAttribute(String servAlg, String attribute, String val) {
    334 
    335         String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute);
    336         if (attributeValue != null) {
    337             if (attribute.equalsIgnoreCase("KeySize")) {
    338                 if (Integer.parseInt(attributeValue) >= Integer.parseInt(val)) {
    339                     return true;
    340                 }
    341             } else { // other attributes
    342                 if (attributeValue.equalsIgnoreCase(val)) {
    343                     return true;
    344                 }
    345             }
    346         }
    347         return false;
    348     }
    349 
    350     /**
    351      *
    352      * Set the provider preference order number.
    353      *
    354      * @param n
    355      */
    356     void setProviderNumber(int n) {
    357         providerNumber = n;
    358     }
    359 
    360     /**
    361      *
    362      * Get the provider preference order number.
    363      *
    364      * @return
    365      */
    366     int getProviderNumber() {
    367         return providerNumber;
    368     }
    369 
    370     /**
    371      * Get the service of the specified type
    372      *
    373      */
    374     synchronized Provider.Service getService(String type) {
    375         updatePropertyServiceTable();
    376         if (lastServicesByType != null && type.equals(lastType)) {
    377             return lastServicesByType;
    378         }
    379         Provider.Service service;
    380         for (Iterator<Service> it = getServices().iterator(); it.hasNext();) {
    381             service = it.next();
    382             if (type.equals(service.type)) {
    383                 lastType = type;
    384                 lastServicesByType = service;
    385                 return service;
    386             }
    387         }
    388         return null;
    389     }
    390 
    391     /**
    392      * Returns the service with the specified {@code type} implementing the
    393      * specified {@code algorithm}, or {@code null} if no such implementation
    394      * exists.
    395      * <p>
    396      * If two services match the requested type and algorithm, the one added
    397      * with the {@link #putService(Service)} is returned (as opposed to the one
    398      * added via {@link #put(Object, Object)}.
    399      *
    400      * @param type
    401      *            the type of the service (for example {@code KeyPairGenerator})
    402      * @param algorithm
    403      *            the algorithm name (case insensitive)
    404      * @return the requested service, or {@code null} if no such implementation
    405      *         exists
    406      */
    407     public synchronized Provider.Service getService(String type,
    408             String algorithm) {
    409         if (type == null) {
    410             throw new NullPointerException("type == null");
    411         } else if (algorithm == null) {
    412             throw new NullPointerException("algorithm == null");
    413         }
    414 
    415         if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) {
    416             return returnedService;
    417         }
    418 
    419         String key = key(type, algorithm);
    420         Object o = null;
    421         if (serviceTable != null) {
    422             o = serviceTable.get(key);
    423         }
    424         if (o == null && aliasTable != null) {
    425             o = aliasTable.get(key);
    426         }
    427         if (o == null) {
    428             updatePropertyServiceTable();
    429         }
    430         if (o == null && propertyServiceTable != null) {
    431             o = propertyServiceTable.get(key);
    432         }
    433         if (o == null && propertyAliasTable != null) {
    434             o = propertyAliasTable.get(key);
    435         }
    436 
    437         if (o != null) {
    438             lastServiceName = type;
    439             lastAlgorithm = algorithm;
    440             returnedService = (Provider.Service) o;
    441             return returnedService;
    442         }
    443         return null;
    444     }
    445 
    446     /**
    447      * Returns an unmodifiable {@code Set} of all services registered by this
    448      * provider.
    449      *
    450      * @return an unmodifiable {@code Set} of all services registered by this
    451      *         provider
    452      */
    453     public synchronized Set<Provider.Service> getServices() {
    454         updatePropertyServiceTable();
    455         if (lastServicesSet != null) {
    456             return lastServicesSet;
    457         }
    458         if (serviceTable != null) {
    459             lastServicesSet = new LinkedHashSet<Service>(serviceTable.values());
    460         } else {
    461             lastServicesSet = new LinkedHashSet<Service>();
    462         }
    463         if (propertyServiceTable != null) {
    464             lastServicesSet.addAll(propertyServiceTable.values());
    465         }
    466         lastServicesSet = Collections.unmodifiableSet(lastServicesSet);
    467         return lastServicesSet;
    468     }
    469 
    470     /**
    471      * Adds a {@code Service} to this {@code Provider}. If a service with the
    472      * same name was registered via this method, it is replace.
    473      *
    474      * @param s
    475      *            the {@code Service} to register
    476      */
    477     protected synchronized void putService(Provider.Service s) {
    478         if (s == null) {
    479             throw new NullPointerException("s == null");
    480         }
    481         if ("Provider".equals(s.getType())) { // Provider service type cannot be added
    482             return;
    483         }
    484         servicesChanged();
    485         if (serviceTable == null) {
    486             serviceTable = new LinkedHashMap<String, Service>(128);
    487         }
    488         serviceTable.put(key(s.type, s.algorithm), s);
    489         if (s.aliases != null) {
    490             if (aliasTable == null) {
    491                 aliasTable = new LinkedHashMap<String, Service>(256);
    492             }
    493             for (String alias : s.getAliases()) {
    494                 aliasTable.put(key(s.type, alias), s);
    495             }
    496         }
    497         serviceInfoToProperties(s);
    498     }
    499 
    500     /**
    501      * Removes a previously registered {@code Service} from this {@code
    502      * Provider}.
    503      *
    504      * @param s
    505      *            the {@code Service} to remove
    506      * @throws NullPointerException
    507      *             if {@code s} is {@code null}
    508      */
    509     protected synchronized void removeService(Provider.Service s) {
    510         if (s == null) {
    511             throw new NullPointerException("s == null");
    512         }
    513         servicesChanged();
    514         if (serviceTable != null) {
    515             serviceTable.remove(key(s.type, s.algorithm));
    516         }
    517         if (aliasTable != null && s.aliases != null) {
    518             for (String alias: s.getAliases()) {
    519                 aliasTable.remove(key(s.type, alias));
    520             }
    521         }
    522         serviceInfoFromProperties(s);
    523     }
    524 
    525     /**
    526      * Add Service information to the provider's properties.
    527      */
    528     private void serviceInfoToProperties(Provider.Service s) {
    529         super.put(s.type + "." + s.algorithm, s.className);
    530         if (s.aliases != null) {
    531             for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) {
    532                 super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm);
    533             }
    534         }
    535         if (s.attributes != null) {
    536             for (Map.Entry<String, String> entry : s.attributes.entrySet()) {
    537                 super.put(s.type + "." + s.algorithm + " " + entry.getKey(),
    538                         entry.getValue());
    539             }
    540         }
    541         if (providerNumber != -1) {
    542             // if registered then refresh Services
    543             Services.setNeedRefresh();
    544         }
    545     }
    546 
    547     /**
    548      * Remove Service information from the provider's properties.
    549      */
    550     private void serviceInfoFromProperties(Provider.Service s) {
    551         super.remove(s.type + "." + s.algorithm);
    552         if (s.aliases != null) {
    553             for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) {
    554                 super.remove("Alg.Alias." + s.type + "." + i.next());
    555             }
    556         }
    557         if (s.attributes != null) {
    558             for (Map.Entry<String, String> entry : s.attributes.entrySet()) {
    559                 super.remove(s.type + "." + s.algorithm + " " + entry.getKey());
    560             }
    561         }
    562         if (providerNumber != -1) {
    563             // if registered then refresh Services
    564             Services.setNeedRefresh();
    565         }
    566     }
    567 
    568     // Remove property information from provider Services
    569     private void removeFromPropertyServiceTable(Object key) {
    570         if (key == null || !(key instanceof String)) {
    571             return;
    572         }
    573         String k = (String) key;
    574         if (k.startsWith("Provider.")) { // Provider service type is reserved
    575             return;
    576         }
    577         Provider.Service s;
    578         String serviceName;
    579         String algorithm = null;
    580         String attribute = null;
    581         int i;
    582         if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<standardName>
    583             String aliasName;
    584             String service_alias = k.substring(10);
    585             i = service_alias.indexOf('.');
    586             serviceName = service_alias.substring(0, i);
    587             aliasName = service_alias.substring(i + 1);
    588             if (propertyAliasTable != null) {
    589                 propertyAliasTable.remove(key(serviceName, aliasName));
    590             }
    591             if (propertyServiceTable != null) {
    592                 for (Iterator<Service> it = propertyServiceTable.values().iterator(); it
    593                         .hasNext();) {
    594                     s = it.next();
    595                     if (s.aliases.contains(aliasName)) {
    596                         s.aliases.remove(aliasName);
    597                         return;
    598                     }
    599                 }
    600             }
    601             return;
    602         }
    603         int j = k.indexOf('.');
    604         if (j == -1) { // unknown format
    605             return;
    606         }
    607 
    608         i = k.indexOf(' ');
    609         if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
    610             serviceName = k.substring(0, j);
    611             algorithm = k.substring(j + 1);
    612             if (propertyServiceTable != null) {
    613                 Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm));
    614                 if (ser != null && propertyAliasTable != null
    615                         && ser.aliases != null) {
    616                     for (String alias : ser.aliases) {
    617                         propertyAliasTable.remove(key(serviceName, alias));
    618                     }
    619                 }
    620             }
    621         } else {
    622             // <crypto_service>.<algorithm_or_type>
    623             // <attribute_name>=<attrValue>
    624             attribute = k.substring(i + 1);
    625             serviceName = k.substring(0, j);
    626             algorithm = k.substring(j + 1, i);
    627             if (propertyServiceTable != null) {
    628                 Object o = propertyServiceTable.get(key(serviceName, algorithm));
    629                 if (o != null) {
    630                     s = (Provider.Service) o;
    631                     s.attributes.remove(attribute);
    632                 }
    633             }
    634         }
    635     }
    636 
    637     // Update provider Services if the properties was changed
    638     private void updatePropertyServiceTable() {
    639         Object _key;
    640         Object _value;
    641         Provider.Service s;
    642         String serviceName;
    643         String algorithm;
    644         if (changedProperties == null || changedProperties.isEmpty()) {
    645             return;
    646         }
    647         for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it
    648                 .hasNext();) {
    649             Map.Entry<Object, Object> entry = it.next();
    650             _key = entry.getKey();
    651             _value = entry.getValue();
    652             if (_key == null || _value == null || !(_key instanceof String)
    653                     || !(_value instanceof String)) {
    654                 continue;
    655             }
    656             String key = (String) _key;
    657             String value = (String) _value;
    658             if (key.startsWith("Provider")) {
    659                 // Provider service type is reserved
    660                 continue;
    661             }
    662             int i;
    663             if (key.startsWith("Alg.Alias.")) {
    664                 // Alg.Alias.<crypto_service>.<aliasName>=<standardName>
    665                 String aliasName;
    666                 String service_alias = key.substring(10);
    667                 i = service_alias.indexOf('.');
    668                 serviceName = service_alias.substring(0, i);
    669                 aliasName = service_alias.substring(i + 1);
    670                 algorithm = value;
    671                 String propertyServiceTableKey = key(serviceName, algorithm);
    672                 Object o = null;
    673                 if (propertyServiceTable == null) {
    674                     propertyServiceTable = new LinkedHashMap<String, Service>(128);
    675                 } else {
    676                     o = propertyServiceTable.get(propertyServiceTableKey);
    677                 }
    678                 if (o != null) {
    679                     s = (Provider.Service) o;
    680                     s.addAlias(aliasName);
    681                     if (propertyAliasTable == null) {
    682                         propertyAliasTable = new LinkedHashMap<String, Service>(256);
    683                     }
    684                     propertyAliasTable.put(key(serviceName, aliasName), s);
    685                 } else {
    686                     String className = (String) changedProperties
    687                             .get(serviceName + "." + algorithm);
    688                     if (className != null) {
    689                         List<String> l = new ArrayList<String>();
    690                         l.add(aliasName);
    691                         s = new Provider.Service(this, serviceName, algorithm,
    692                                 className, l, new HashMap<String, String>());
    693                         propertyServiceTable.put(propertyServiceTableKey, s);
    694                         if (propertyAliasTable == null) {
    695                             propertyAliasTable = new LinkedHashMap<String, Service>(256);
    696                         }
    697                         propertyAliasTable.put(key(serviceName, aliasName), s);
    698                     }
    699                 }
    700                 continue;
    701             }
    702             int j = key.indexOf('.');
    703             if (j == -1) { // unknown format
    704                 continue;
    705             }
    706             i = key.indexOf(' ');
    707             if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
    708                 serviceName = key.substring(0, j);
    709                 algorithm = key.substring(j + 1);
    710                 String propertyServiceTableKey = key(serviceName, algorithm);
    711                 Object o = null;
    712                 if (propertyServiceTable != null) {
    713                     o = propertyServiceTable.get(propertyServiceTableKey);
    714                 }
    715                 if (o != null) {
    716                     s = (Provider.Service) o;
    717                     s.className = value;
    718                 } else {
    719                     s = new Provider.Service(this, serviceName, algorithm,
    720                             value, Collections.<String>emptyList(),
    721                             Collections.<String,String>emptyMap());
    722                     if (propertyServiceTable == null) {
    723                         propertyServiceTable = new LinkedHashMap<String, Service>(128);
    724                     }
    725                     propertyServiceTable.put(propertyServiceTableKey, s);
    726 
    727                 }
    728             } else {
    729                 // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue>
    730                 serviceName = key.substring(0, j);
    731                 algorithm = key.substring(j + 1, i);
    732                 String attribute = key.substring(i + 1);
    733                 String propertyServiceTableKey = key(serviceName, algorithm);
    734                 Object o = null;
    735                 if (propertyServiceTable != null) {
    736                     o = propertyServiceTable.get(propertyServiceTableKey);
    737                 }
    738                 if (o != null) {
    739                     s = (Provider.Service) o;
    740                     s.putAttribute(attribute, value);
    741                 } else {
    742                     String className = (String) changedProperties
    743                             .get(serviceName + "." + algorithm);
    744                     if (className != null) {
    745                         Map<String, String> m = new HashMap<String, String>();
    746                         m.put(attribute, value);
    747                         s = new Provider.Service(this, serviceName, algorithm,
    748                                 className, new ArrayList<String>(), m);
    749                         if (propertyServiceTable == null) {
    750                             propertyServiceTable = new LinkedHashMap<String, Service>(128);
    751                         }
    752                         propertyServiceTable.put(propertyServiceTableKey, s);
    753                     }
    754                 }
    755             }
    756         }
    757         servicesChanged();
    758         changedProperties = null;
    759     }
    760 
    761     private void servicesChanged() {
    762         lastServicesByType = null;
    763         lastServiceName = null;
    764         lastServicesSet = null;
    765     }
    766 
    767     /**
    768      * These attributes should be placed in each Provider object:
    769      * Provider.id name, Provider.id version, Provider.id info,
    770      * Provider.id className
    771      */
    772     private void putProviderInfo() {
    773         super.put("Provider.id name", (name != null) ? name : "null");
    774         super.put("Provider.id version", versionString);
    775         super.put("Provider.id info", (info != null) ? info : "null");
    776         super.put("Provider.id className", this.getClass().getName());
    777     }
    778 
    779     /**
    780      * Returns the property with the specified key in the provider properties.
    781      * The name is not case-sensitive.
    782      */
    783     private String getPropertyIgnoreCase(String key) {
    784         String res = getProperty(key);
    785         if (res != null) {
    786             return res;
    787         }
    788         for (Enumeration<?> e = propertyNames(); e.hasMoreElements(); ) {
    789             String propertyName = (String) e.nextElement();
    790             if (key.equalsIgnoreCase(propertyName)) {
    791                 return getProperty(propertyName);
    792             }
    793         }
    794         return null;
    795     }
    796 
    797     private static String key(String type, String algorithm) {
    798         return type + '.' + algorithm.toUpperCase(Locale.US);
    799     }
    800 
    801     /**
    802      * {@code Service} represents a service in the Java Security infrastructure.
    803      * Each service describes its type, the algorithm it implements, to which
    804      * provider it belongs and other properties.
    805      */
    806     public static class Service {
    807         /** Attribute name of supported key classes. */
    808         private static final String ATTR_SUPPORTED_KEY_CLASSES = "SupportedKeyClasses";
    809 
    810         /** Attribute name of supported key formats. */
    811         private static final String ATTR_SUPPORTED_KEY_FORMATS = "SupportedKeyFormats";
    812 
    813         /** Whether this type supports calls to {@link #supportsParameter(Object)}. */
    814         private static final HashMap<String, Boolean> supportsParameterTypes
    815                 = new HashMap<String, Boolean>();
    816         static {
    817             // Does not support parameter
    818             supportsParameterTypes.put("AlgorithmParameterGenerator", false);
    819             supportsParameterTypes.put("AlgorithmParameters", false);
    820             supportsParameterTypes.put("CertificateFactory", false);
    821             supportsParameterTypes.put("CertPathBuilder", false);
    822             supportsParameterTypes.put("CertPathValidator", false);
    823             supportsParameterTypes.put("CertStore", false);
    824             supportsParameterTypes.put("KeyFactory", false);
    825             supportsParameterTypes.put("KeyGenerator", false);
    826             supportsParameterTypes.put("KeyManagerFactory", false);
    827             supportsParameterTypes.put("KeyPairGenerator", false);
    828             supportsParameterTypes.put("KeyStore", false);
    829             supportsParameterTypes.put("MessageDigest", false);
    830             supportsParameterTypes.put("SecretKeyFactory", false);
    831             supportsParameterTypes.put("SecureRandom", false);
    832             supportsParameterTypes.put("SSLContext", false);
    833             supportsParameterTypes.put("TrustManagerFactory", false);
    834 
    835             // Supports parameter
    836             supportsParameterTypes.put("Cipher", true);
    837             supportsParameterTypes.put("KeyAgreement", true);
    838             supportsParameterTypes.put("Mac", true);
    839             supportsParameterTypes.put("Signature", true);
    840         }
    841 
    842         /** Constructor argument classes for calls to {@link #newInstance(Object)}. */
    843         private static final HashMap<String, Class<?>> constructorParameterClasses = new HashMap<String, Class<?>>();
    844         static {
    845             // Types that take a parameter to newInstance
    846             constructorParameterClasses.put("CertStore",
    847                     loadClassOrThrow("java.security.cert.CertStoreParameters"));
    848 
    849             // Types that do not take any kind of parameter
    850             constructorParameterClasses.put("AlgorithmParameterGenerator", null);
    851             constructorParameterClasses.put("AlgorithmParameters", null);
    852             constructorParameterClasses.put("CertificateFactory", null);
    853             constructorParameterClasses.put("CertPathBuilder", null);
    854             constructorParameterClasses.put("CertPathValidator", null);
    855             constructorParameterClasses.put("KeyFactory", null);
    856             constructorParameterClasses.put("KeyGenerator", null);
    857             constructorParameterClasses.put("KeyManagerFactory", null);
    858             constructorParameterClasses.put("KeyPairGenerator", null);
    859             constructorParameterClasses.put("KeyStore", null);
    860             constructorParameterClasses.put("MessageDigest", null);
    861             constructorParameterClasses.put("SecretKeyFactory", null);
    862             constructorParameterClasses.put("SecureRandom", null);
    863             constructorParameterClasses.put("SSLContext", null);
    864             constructorParameterClasses.put("TrustManagerFactory", null);
    865             constructorParameterClasses.put("Cipher", null);
    866             constructorParameterClasses.put("KeyAgreement", null);
    867             constructorParameterClasses.put("Mac", null);
    868             constructorParameterClasses.put("Signature", null);
    869         }
    870 
    871         /** Called to load a class if it's critical that the class exists. */
    872         private static Class<?> loadClassOrThrow(String className) {
    873             try {
    874                 return Provider.class.getClassLoader().loadClass(className);
    875             } catch (Exception e) {
    876                 throw new AssertionError(e);
    877             }
    878         }
    879 
    880         // The provider
    881         private Provider provider;
    882 
    883         // The type of this service
    884         private String type;
    885 
    886         // The algorithm name
    887         private String algorithm;
    888 
    889         // The class implementing this service
    890         private String className;
    891 
    892         // The aliases
    893         private List<String> aliases;
    894 
    895         // The attributes
    896         private Map<String,String> attributes;
    897 
    898         // Service implementation
    899         private Class<?> implementation;
    900 
    901         // For newInstance() optimization
    902         private String lastClassName;
    903 
    904         /** Indicates whether supportedKeyClasses and supportedKeyFormats. */
    905         private volatile boolean supportedKeysInitialized;
    906 
    907         /** List of classes that this service supports. */
    908         private Class<?>[] keyClasses;
    909 
    910         /** List of key formats this service supports. */
    911         private String[] keyFormats;
    912 
    913         /**
    914          * Constructs a new instance of {@code Service} with the given
    915          * attributes.
    916          *
    917          * @param provider
    918          *            the provider to which this service belongs.
    919          * @param type
    920          *            the type of this service (for example {@code
    921          *            KeyPairGenerator}).
    922          * @param algorithm
    923          *            the algorithm this service implements.
    924          * @param className
    925          *            the name of the class implementing this service.
    926          * @param aliases
    927          *            {@code List} of aliases for the algorithm name, or {@code
    928          *            null} if the implemented algorithm has no aliases.
    929          * @param attributes
    930          *            {@code Map} of additional attributes, or {@code null} if
    931          *            this {@code Service} has no attributed.
    932          * @throws NullPointerException
    933          *             if {@code provider, type, algorithm} or {@code className}
    934          *             is {@code null}.
    935          */
    936         public Service(Provider provider, String type, String algorithm,
    937                 String className, List<String> aliases, Map<String, String> attributes) {
    938             if (provider == null) {
    939                 throw new NullPointerException("provider == null");
    940             } else if (type == null) {
    941                 throw new NullPointerException("type == null");
    942             } else if (algorithm == null) {
    943                 throw new NullPointerException("algorithm == null");
    944             } else if (className == null) {
    945                 throw new NullPointerException("className == null");
    946             }
    947             this.provider = provider;
    948             this.type = type;
    949             this.algorithm = algorithm;
    950             this.className = className;
    951             this.aliases = ((aliases != null) && (aliases.size() == 0))
    952                     ? Collections.<String>emptyList() : aliases;
    953             this.attributes =
    954                     ((attributes != null) && (attributes.size() == 0))
    955                     ? Collections.<String,String>emptyMap() : attributes;
    956         }
    957 
    958         /**
    959          * Adds an alias.
    960          *
    961          * @param alias the alias to add
    962          */
    963         /*package*/ void addAlias(String alias) {
    964             if ((aliases == null) || (aliases.size() == 0)) {
    965                 aliases = new ArrayList<String>();
    966             }
    967             aliases.add(alias);
    968         }
    969 
    970         /**
    971          * Puts a new attribute mapping.
    972          *
    973          * @param name the attribute name.
    974          * @param value the attribute value.
    975          */
    976         /*package*/ void putAttribute(String name, String value) {
    977             if ((attributes == null) || (attributes.size() == 0)) {
    978                 attributes = new HashMap<String,String>();
    979             }
    980             attributes.put(name, value);
    981         }
    982 
    983         /**
    984          * Returns the type of this {@code Service}. For example {@code
    985          * KeyPairGenerator}.
    986          *
    987          * @return the type of this {@code Service}.
    988          */
    989         public final String getType() {
    990             return type;
    991         }
    992 
    993         /**
    994          * Returns the name of the algorithm implemented by this {@code
    995          * Service}.
    996          *
    997          * @return the name of the algorithm implemented by this {@code
    998          *         Service}.
    999          */
   1000         public final String getAlgorithm() {
   1001             return algorithm;
   1002         }
   1003 
   1004         /**
   1005          * Returns the {@code Provider} this {@code Service} belongs to.
   1006          *
   1007          * @return the {@code Provider} this {@code Service} belongs to.
   1008          */
   1009         public final Provider getProvider() {
   1010             return provider;
   1011         }
   1012 
   1013         /**
   1014          * Returns the name of the class implementing this {@code Service}.
   1015          *
   1016          * @return the name of the class implementing this {@code Service}.
   1017          */
   1018         public final String getClassName() {
   1019             return className;
   1020         }
   1021 
   1022         /**
   1023          * Returns the value of the attribute with the specified {@code name}.
   1024          *
   1025          * @param name
   1026          *            the name of the attribute.
   1027          * @return the value of the attribute, or {@code null} if no attribute
   1028          *         with the given name is set.
   1029          * @throws NullPointerException
   1030          *             if {@code name} is {@code null}.
   1031          */
   1032         public final String getAttribute(String name) {
   1033             if (name == null) {
   1034                 throw new NullPointerException("name == null");
   1035             }
   1036             if (attributes == null) {
   1037                 return null;
   1038             }
   1039             return attributes.get(name);
   1040         }
   1041 
   1042         List<String> getAliases() {
   1043             if (aliases == null){
   1044                 aliases = new ArrayList<String>(0);
   1045             }
   1046             return aliases;
   1047         }
   1048 
   1049         /**
   1050          * Creates and returns a new instance of the implementation described by
   1051          * this {@code Service}.
   1052          *
   1053          * @param constructorParameter
   1054          *            the parameter that is used by the constructor, or {@code
   1055          *            null} if the implementation does not declare a constructor
   1056          *            parameter.
   1057          * @return a new instance of the implementation described by this
   1058          *         {@code Service}.
   1059          * @throws NoSuchAlgorithmException
   1060          *             if the instance could not be constructed.
   1061          * @throws InvalidParameterException
   1062          *             if the implementation does not support the specified
   1063          *             {@code constructorParameter}.
   1064          */
   1065         public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException {
   1066             if (implementation == null || !className.equals(lastClassName)) {
   1067                 ClassLoader cl = provider.getClass().getClassLoader();
   1068                 if (cl == null) {
   1069                     cl = ClassLoader.getSystemClassLoader();
   1070                 }
   1071                 try {
   1072                     implementation = Class.forName(className, true, cl);
   1073                     lastClassName = className;
   1074                 } catch (Exception e) {
   1075                     throw new NoSuchAlgorithmException(type + " " + algorithm + " implementation not found: " + e);
   1076                 }
   1077             }
   1078 
   1079             // We don't know whether this takes a parameter or not.
   1080             if (!constructorParameterClasses.containsKey(type)) {
   1081                 if (constructorParameter == null) {
   1082                     return newInstanceNoParameter();
   1083                 } else {
   1084                     return newInstanceWithParameter(constructorParameter,
   1085                             constructorParameter.getClass());
   1086                 }
   1087             }
   1088 
   1089             // A known type, but it's not required to have a parameter even if a
   1090             // class is specified.
   1091             if (constructorParameter == null) {
   1092                 return newInstanceNoParameter();
   1093             }
   1094 
   1095             // Make sure the provided constructor class is valid.
   1096             final Class<?> expectedClass = constructorParameterClasses.get(type);
   1097             if (expectedClass == null) {
   1098                 throw new IllegalArgumentException("Constructor parameter not supported for "
   1099                         + type);
   1100             }
   1101             if (!expectedClass.isAssignableFrom(constructorParameter.getClass())) {
   1102                 throw new IllegalArgumentException("Expecting constructor parameter of type "
   1103                         + expectedClass.getName() + " but was "
   1104                         + constructorParameter.getClass().getName());
   1105             }
   1106             return newInstanceWithParameter(constructorParameter, expectedClass);
   1107         }
   1108 
   1109         private Object newInstanceWithParameter(Object constructorParameter,
   1110                 Class<?> parameterClass) throws NoSuchAlgorithmException {
   1111             try {
   1112                 Class<?>[] parameterTypes = { parameterClass };
   1113                 Object[] initargs = { constructorParameter };
   1114                 return implementation.getConstructor(parameterTypes).newInstance(initargs);
   1115             } catch (Exception e) {
   1116                 throw new NoSuchAlgorithmException(type + " " + algorithm
   1117                         + " implementation not found", e);
   1118             }
   1119         }
   1120 
   1121         private Object newInstanceNoParameter() throws NoSuchAlgorithmException {
   1122             try {
   1123                 return implementation.newInstance();
   1124             } catch (Exception e) {
   1125                 throw new NoSuchAlgorithmException(type + " " + algorithm
   1126                         + " implementation not found", e);
   1127             }
   1128         }
   1129 
   1130         /**
   1131          * Indicates whether this {@code Service} supports the specified
   1132          * constructor parameter.
   1133          *
   1134          * @param parameter
   1135          *            the parameter to test.
   1136          * @return {@code true} if this {@code Service} supports the specified
   1137          *         constructor parameter, {@code false} otherwise.
   1138          */
   1139         public boolean supportsParameter(Object parameter) {
   1140             Boolean supportsParameter = supportsParameterTypes.get(type);
   1141             if (supportsParameter == null) {
   1142                 return true;
   1143             }
   1144             if (!supportsParameter) {
   1145                 throw new InvalidParameterException("Cannot use a parameter with " + type);
   1146             }
   1147 
   1148             /*
   1149              * Only Key parameters are allowed, but allow null since there might
   1150              * not be any listed classes or formats for this instance.
   1151              */
   1152             if (parameter != null && !(parameter instanceof Key)) {
   1153                 throw new InvalidParameterException("Parameter should be of type Key");
   1154             }
   1155 
   1156             ensureSupportedKeysInitialized();
   1157 
   1158             // No restriction specified by Provider registration.
   1159             if (keyClasses == null && keyFormats == null) {
   1160                 return true;
   1161             }
   1162 
   1163             // Restriction specified by registration, so null is not acceptable.
   1164             if (parameter == null) {
   1165                 return false;
   1166             }
   1167 
   1168             Key keyParam = (Key) parameter;
   1169             if (keyClasses != null && isInArray(keyClasses, keyParam.getClass())) {
   1170                 return true;
   1171             }
   1172             if (keyFormats != null && isInArray(keyFormats, keyParam.getFormat())) {
   1173                 return true;
   1174             }
   1175 
   1176             return false;
   1177         }
   1178 
   1179         /**
   1180          * Initialize the list of supported key classes and formats.
   1181          */
   1182         private void ensureSupportedKeysInitialized() {
   1183             if (supportedKeysInitialized) {
   1184                 return;
   1185             }
   1186 
   1187             final String supportedClassesString = getAttribute(ATTR_SUPPORTED_KEY_CLASSES);
   1188             if (supportedClassesString != null) {
   1189                 String[] keyClassNames = supportedClassesString.split("\\|");
   1190                 ArrayList<Class<?>> supportedClassList = new ArrayList<Class<?>>(
   1191                         keyClassNames.length);
   1192                 final ClassLoader classLoader = getProvider().getClass().getClassLoader();
   1193                 for (String keyClassName : keyClassNames) {
   1194                     try {
   1195                         Class<?> keyClass = classLoader.loadClass(keyClassName);
   1196                         if (Key.class.isAssignableFrom(keyClass)) {
   1197                             supportedClassList.add(keyClass);
   1198                         }
   1199                     } catch (ClassNotFoundException ignored) {
   1200                     }
   1201                 }
   1202                 keyClasses = supportedClassList.toArray(new Class<?>[supportedClassList.size()]);
   1203             }
   1204 
   1205             final String supportedFormatString = getAttribute(ATTR_SUPPORTED_KEY_FORMATS);
   1206             if (supportedFormatString != null) {
   1207                 keyFormats = supportedFormatString.split("\\|");
   1208             }
   1209 
   1210             supportedKeysInitialized = true;
   1211         }
   1212 
   1213         /**
   1214          * Check if an item is in the array. The array of supported key classes
   1215          * and formats is usually just a length of 1, so a simple array is
   1216          * faster than a Set.
   1217          */
   1218         private static <T> boolean isInArray(T[] itemList, T target) {
   1219             if (target == null) {
   1220                 return false;
   1221             }
   1222             for (T item : itemList) {
   1223                 if (target.equals(item)) {
   1224                     return true;
   1225                 }
   1226             }
   1227             return false;
   1228         }
   1229 
   1230         /**
   1231          * Check if an item is in the array. The array of supported key classes
   1232          * and formats is usually just a length of 1, so a simple array is
   1233          * faster than a Set.
   1234          */
   1235         private static boolean isInArray(Class<?>[] itemList, Class<?> target) {
   1236             if (target == null) {
   1237                 return false;
   1238             }
   1239             for (Class<?> item : itemList) {
   1240                 if (item.isAssignableFrom(target)) {
   1241                     return true;
   1242                 }
   1243             }
   1244             return false;
   1245         }
   1246 
   1247         /**
   1248          * Returns a string containing a concise, human-readable description of
   1249          * this {@code Service}.
   1250          *
   1251          * @return a printable representation for this {@code Service}.
   1252          */
   1253         @Override
   1254         public String toString() {
   1255             String result = "Provider " + provider.getName() + " Service "
   1256                     + type + "." + algorithm + " " + className;
   1257             if (aliases != null) {
   1258                 result = result + "\nAliases " + aliases.toString();
   1259             }
   1260             if (attributes != null) {
   1261                 result = result + "\nAttributes " + attributes.toString();
   1262             }
   1263             return result;
   1264         }
   1265     }
   1266 
   1267     private void readObject(java.io.ObjectInputStream in)
   1268             throws NotActiveException, IOException, ClassNotFoundException {
   1269         in.defaultReadObject();
   1270         versionString = String.valueOf(version);
   1271         providerNumber = -1;
   1272     }
   1273 }
   1274