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