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      */
Provider(String name, double version, String info)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      */
getName()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      */
getVersion()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      */
getInfo()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
toString()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
clear()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
load(InputStream inStream)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
putAll(Map<?,?> t)190     public synchronized void putAll(Map<?,?> t) {
191         myPutAll(t);
192     }
193 
myPutAll(Map<?,?> t)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
entrySet()222     public synchronized Set<Map.Entry<Object,Object>> entrySet() {
223         return Collections.unmodifiableSet(super.entrySet());
224     }
225 
226     @Override
keySet()227     public Set<Object> keySet() {
228         return Collections.unmodifiableSet(super.keySet());
229     }
230 
231     @Override
values()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
put(Object key, Object value)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
remove(Object key)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      */
implementsAlg(String serv, String alg, String attribute, String val)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      */
checkAttribute(String servAlg, String attribute, String val)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      */
setProviderNumber(int n)356     void setProviderNumber(int n) {
357         providerNumber = n;
358     }
359 
360     /**
361      *
362      * Get the provider preference order number.
363      *
364      * @return
365      */
getProviderNumber()366     int getProviderNumber() {
367         return providerNumber;
368     }
369 
370     /**
371      * Get the service of the specified type
372      *
373      */
getService(String type)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      */
getService(String type, String algorithm)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      */
getServices()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      */
putService(Provider.Service s)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      */
removeService(Provider.Service s)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      */
serviceInfoToProperties(Provider.Service s)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      */
serviceInfoFromProperties(Provider.Service s)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
removeFromPropertyServiceTable(Object key)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
updatePropertyServiceTable()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 
servicesChanged()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      */
putProviderInfo()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      */
getPropertyIgnoreCase(String key)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 
key(String type, String algorithm)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. */
loadClassOrThrow(String className)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          */
Service(Provider provider, String type, String algorithm, String className, List<String> aliases, Map<String, String> attributes)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          */
addAlias(String alias)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          */
putAttribute(String name, String value)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          */
getType()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          */
getAlgorithm()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          */
getProvider()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          */
getClassName()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          */
getAttribute(String name)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 
getAliases()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          */
newInstance(Object constructorParameter)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 
newInstanceWithParameter(Object constructorParameter, Class<?> parameterClass)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 
newInstanceNoParameter()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          */
supportsParameter(Object parameter)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          */
ensureSupportedKeysInitialized()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          */
isInArray(T[] itemList, T target)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          */
isInArray(Class<?>[] itemList, Class<?> target)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
toString()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 
readObject(java.io.ObjectInputStream in)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