1 /* 2 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.security; 27 28 import java.lang.reflect.*; 29 import java.util.*; 30 import java.util.concurrent.ConcurrentHashMap; 31 import java.io.*; 32 import java.net.URL; 33 import sun.security.util.Debug; 34 import sun.security.util.PropertyExpander; 35 36 import java.security.Provider.Service; 37 38 import sun.security.jca.*; 39 40 /** 41 * <p>This class centralizes all security properties and common security 42 * methods. One of its primary uses is to manage providers. 43 * 44 * @author Benjamin Renaud 45 */ 46 47 public final class Security { 48 49 /* Are we debugging? -- for developers */ 50 private static final Debug sdebug = 51 Debug.getInstance("properties"); 52 53 /* The java.security properties */ 54 private static Properties props; 55 56 // An element in the cache 57 private static class ProviderProperty { 58 String className; 59 Provider provider; 60 } 61 62 static { 63 // doPrivileged here because there are multiple 64 // things in initialize that might require privs. 65 // (the FileInputStream call and the File.exists call, 66 // the securityPropFile call, etc) 67 AccessController.doPrivileged(new PrivilegedAction<Void>() { 68 public Void run() { 69 initialize(); 70 return null; 71 } 72 }); 73 } 74 75 private static void initialize() { 76 props = new Properties(); 77 boolean loadedProps = false; 78 boolean overrideAll = false; 79 80 // first load the system properties file 81 // to determine the value of security.overridePropertiesFile 82 File propFile = securityPropFile("java.security"); 83 if (propFile.exists()) { 84 InputStream is = null; 85 try { 86 FileInputStream fis = new FileInputStream(propFile); 87 is = new BufferedInputStream(fis); 88 props.load(is); 89 loadedProps = true; 90 91 if (sdebug != null) { 92 sdebug.println("reading security properties file: " + 93 propFile); 94 } 95 } catch (IOException e) { 96 if (sdebug != null) { 97 sdebug.println("unable to load security properties from " + 98 propFile); 99 e.printStackTrace(); 100 } 101 } finally { 102 if (is != null) { 103 try { 104 is.close(); 105 } catch (IOException ioe) { 106 if (sdebug != null) { 107 sdebug.println("unable to close input stream"); 108 } 109 } 110 } 111 } 112 } 113 114 if ("true".equalsIgnoreCase(props.getProperty 115 ("security.overridePropertiesFile"))) { 116 117 String extraPropFile = System.getProperty 118 ("java.security.properties"); 119 if (extraPropFile != null && extraPropFile.startsWith("=")) { 120 overrideAll = true; 121 extraPropFile = extraPropFile.substring(1); 122 } 123 124 if (overrideAll) { 125 props = new Properties(); 126 if (sdebug != null) { 127 sdebug.println 128 ("overriding other security properties files!"); 129 } 130 } 131 132 // now load the user-specified file so its values 133 // will win if they conflict with the earlier values 134 if (extraPropFile != null) { 135 BufferedInputStream bis = null; 136 try { 137 URL propURL; 138 139 extraPropFile = PropertyExpander.expand(extraPropFile); 140 propFile = new File(extraPropFile); 141 if (propFile.exists()) { 142 propURL = new URL 143 ("file:" + propFile.getCanonicalPath()); 144 } else { 145 propURL = new URL(extraPropFile); 146 } 147 bis = new BufferedInputStream(propURL.openStream()); 148 props.load(bis); 149 loadedProps = true; 150 151 if (sdebug != null) { 152 sdebug.println("reading security properties file: " + 153 propURL); 154 if (overrideAll) { 155 sdebug.println 156 ("overriding other security properties files!"); 157 } 158 } 159 } catch (Exception e) { 160 if (sdebug != null) { 161 sdebug.println 162 ("unable to load security properties from " + 163 extraPropFile); 164 e.printStackTrace(); 165 } 166 } finally { 167 if (bis != null) { 168 try { 169 bis.close(); 170 } catch (IOException ioe) { 171 if (sdebug != null) { 172 sdebug.println("unable to close input stream"); 173 } 174 } 175 } 176 } 177 } 178 } 179 180 if (!loadedProps) { 181 initializeStatic(); 182 if (sdebug != null) { 183 sdebug.println("unable to load security properties " + 184 "-- using defaults"); 185 } 186 } 187 188 } 189 190 /* 191 * Initialize to default values, if <java.home>/lib/java.security 192 * is not found. 193 */ 194 private static void initializeStatic() { 195 props.put("security.provider.1", "sun.security.provider.Sun"); 196 props.put("security.provider.2", "sun.security.rsa.SunRsaSign"); 197 props.put("security.provider.3", "com.sun.net.ssl.internal.ssl.Provider"); 198 props.put("security.provider.4", "com.sun.crypto.provider.SunJCE"); 199 props.put("security.provider.5", "sun.security.jgss.SunProvider"); 200 props.put("security.provider.6", "com.sun.security.sasl.Provider"); 201 } 202 203 /** 204 * Don't let anyone instantiate this. 205 */ 206 private Security() { 207 } 208 209 private static File securityPropFile(String filename) { 210 // maybe check for a system property which will specify where to 211 // look. Someday. 212 String sep = File.separator; 213 return new File(System.getProperty("java.home") + sep + "lib" + sep + 214 "security" + sep + filename); 215 } 216 217 /** 218 * Looks up providers, and returns the property (and its associated 219 * provider) mapping the key, if any. 220 * The order in which the providers are looked up is the 221 * provider-preference order, as specificed in the security 222 * properties file. 223 */ 224 private static ProviderProperty getProviderProperty(String key) { 225 ProviderProperty entry = null; 226 227 List<Provider> providers = Providers.getProviderList().providers(); 228 for (int i = 0; i < providers.size(); i++) { 229 230 String matchKey = null; 231 Provider prov = providers.get(i); 232 String prop = prov.getProperty(key); 233 234 if (prop == null) { 235 // Is there a match if we do a case-insensitive property name 236 // comparison? Let's try ... 237 for (Enumeration<Object> e = prov.keys(); 238 e.hasMoreElements() && prop == null; ) { 239 matchKey = (String)e.nextElement(); 240 if (key.equalsIgnoreCase(matchKey)) { 241 prop = prov.getProperty(matchKey); 242 break; 243 } 244 } 245 } 246 247 if (prop != null) { 248 ProviderProperty newEntry = new ProviderProperty(); 249 newEntry.className = prop; 250 newEntry.provider = prov; 251 return newEntry; 252 } 253 } 254 255 return entry; 256 } 257 258 /** 259 * Returns the property (if any) mapping the key for the given provider. 260 */ 261 private static String getProviderProperty(String key, Provider provider) { 262 String prop = provider.getProperty(key); 263 if (prop == null) { 264 // Is there a match if we do a case-insensitive property name 265 // comparison? Let's try ... 266 for (Enumeration<Object> e = provider.keys(); 267 e.hasMoreElements() && prop == null; ) { 268 String matchKey = (String)e.nextElement(); 269 if (key.equalsIgnoreCase(matchKey)) { 270 prop = provider.getProperty(matchKey); 271 break; 272 } 273 } 274 } 275 return prop; 276 } 277 278 /** 279 * Gets a specified property for an algorithm. The algorithm name 280 * should be a standard name. See Appendix A in the <a href= 281 * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> 282 * Java Cryptography Architecture API Specification & Reference </a> 283 * for information about standard algorithm names. 284 * One possible use is by specialized algorithm parsers, which may map 285 * classes to algorithms which they understand (much like Key parsers 286 * do). 287 * 288 * @param algName the algorithm name. 289 * 290 * @param propName the name of the property to get. 291 * 292 * @return the value of the specified property. 293 * 294 * @deprecated This method used to return the value of a proprietary 295 * property in the master file of the "SUN" Cryptographic Service 296 * Provider in order to determine how to parse algorithm-specific 297 * parameters. Use the new provider-based and algorithm-independent 298 * <code>AlgorithmParameters</code> and <code>KeyFactory</code> engine 299 * classes (introduced in the J2SE version 1.2 platform) instead. 300 */ 301 @Deprecated 302 public static String getAlgorithmProperty(String algName, 303 String propName) { 304 ProviderProperty entry = getProviderProperty("Alg." + propName 305 + "." + algName); 306 if (entry != null) { 307 return entry.className; 308 } else { 309 return null; 310 } 311 } 312 313 /** 314 * Adds a new provider, at a specified position. The position is 315 * the preference order in which providers are searched for 316 * requested algorithms. The position is 1-based, that is, 317 * 1 is most preferred, followed by 2, and so on. 318 * 319 * <p>If the given provider is installed at the requested position, 320 * the provider that used to be at that position, and all providers 321 * with a position greater than <code>position</code>, are shifted up 322 * one position (towards the end of the list of installed providers). 323 * 324 * <p>A provider cannot be added if it is already installed. 325 * 326 * <p>First, if there is a security manager, its 327 * <code>checkSecurityAccess</code> 328 * method is called with the string 329 * <code>"insertProvider."+provider.getName()</code> 330 * to see if it's ok to add a new provider. 331 * If the default implementation of <code>checkSecurityAccess</code> 332 * is used (i.e., that method is not overriden), then this will result in 333 * a call to the security manager's <code>checkPermission</code> method 334 * with a 335 * <code>SecurityPermission("insertProvider."+provider.getName())</code> 336 * permission. 337 * 338 * @param provider the provider to be added. 339 * 340 * @param position the preference position that the caller would 341 * like for this provider. 342 * 343 * @return the actual preference position in which the provider was 344 * added, or -1 if the provider was not added because it is 345 * already installed. 346 * 347 * @throws NullPointerException if provider is null 348 * @throws SecurityException 349 * if a security manager exists and its <code>{@link 350 * java.lang.SecurityManager#checkSecurityAccess}</code> method 351 * denies access to add a new provider 352 * 353 * @see #getProvider 354 * @see #removeProvider 355 * @see java.security.SecurityPermission 356 */ 357 public static synchronized int insertProviderAt(Provider provider, 358 int position) { 359 String providerName = provider.getName(); 360 check("insertProvider." + providerName); 361 ProviderList list = Providers.getFullProviderList(); 362 ProviderList newList = ProviderList.insertAt(list, provider, position - 1); 363 if (list == newList) { 364 return -1; 365 } 366 Providers.setProviderList(newList); 367 return newList.getIndex(providerName) + 1; 368 } 369 370 /** 371 * Adds a provider to the next position available. 372 * 373 * <p>First, if there is a security manager, its 374 * <code>checkSecurityAccess</code> 375 * method is called with the string 376 * <code>"insertProvider."+provider.getName()</code> 377 * to see if it's ok to add a new provider. 378 * If the default implementation of <code>checkSecurityAccess</code> 379 * is used (i.e., that method is not overriden), then this will result in 380 * a call to the security manager's <code>checkPermission</code> method 381 * with a 382 * <code>SecurityPermission("insertProvider."+provider.getName())</code> 383 * permission. 384 * 385 * @param provider the provider to be added. 386 * 387 * @return the preference position in which the provider was 388 * added, or -1 if the provider was not added because it is 389 * already installed. 390 * 391 * @throws NullPointerException if provider is null 392 * @throws SecurityException 393 * if a security manager exists and its <code>{@link 394 * java.lang.SecurityManager#checkSecurityAccess}</code> method 395 * denies access to add a new provider 396 * 397 * @see #getProvider 398 * @see #removeProvider 399 * @see java.security.SecurityPermission 400 */ 401 public static int addProvider(Provider provider) { 402 /* 403 * We can't assign a position here because the statically 404 * registered providers may not have been installed yet. 405 * insertProviderAt() will fix that value after it has 406 * loaded the static providers. 407 */ 408 return insertProviderAt(provider, 0); 409 } 410 411 /** 412 * Removes the provider with the specified name. 413 * 414 * <p>When the specified provider is removed, all providers located 415 * at a position greater than where the specified provider was are shifted 416 * down one position (towards the head of the list of installed 417 * providers). 418 * 419 * <p>This method returns silently if the provider is not installed or 420 * if name is null. 421 * 422 * <p>First, if there is a security manager, its 423 * <code>checkSecurityAccess</code> 424 * method is called with the string <code>"removeProvider."+name</code> 425 * to see if it's ok to remove the provider. 426 * If the default implementation of <code>checkSecurityAccess</code> 427 * is used (i.e., that method is not overriden), then this will result in 428 * a call to the security manager's <code>checkPermission</code> method 429 * with a <code>SecurityPermission("removeProvider."+name)</code> 430 * permission. 431 * 432 * @param name the name of the provider to remove. 433 * 434 * @throws SecurityException 435 * if a security manager exists and its <code>{@link 436 * java.lang.SecurityManager#checkSecurityAccess}</code> method 437 * denies 438 * access to remove the provider 439 * 440 * @see #getProvider 441 * @see #addProvider 442 */ 443 public static synchronized void removeProvider(String name) { 444 check("removeProvider." + name); 445 ProviderList list = Providers.getFullProviderList(); 446 ProviderList newList = ProviderList.remove(list, name); 447 Providers.setProviderList(newList); 448 } 449 450 /** 451 * Returns an array containing all the installed providers. The order of 452 * the providers in the array is their preference order. 453 * 454 * @return an array of all the installed providers. 455 */ 456 public static Provider[] getProviders() { 457 return Providers.getFullProviderList().toArray(); 458 } 459 460 /** 461 * Returns the provider installed with the specified name, if 462 * any. Returns null if no provider with the specified name is 463 * installed or if name is null. 464 * 465 * @param name the name of the provider to get. 466 * 467 * @return the provider of the specified name. 468 * 469 * @see #removeProvider 470 * @see #addProvider 471 */ 472 public static Provider getProvider(String name) { 473 return Providers.getProviderList().getProvider(name); 474 } 475 476 /** 477 * Returns an array containing all installed providers that satisfy the 478 * specified selection criterion, or null if no such providers have been 479 * installed. The returned providers are ordered 480 * according to their <a href= 481 * "#insertProviderAt(java.security.Provider, int)">preference order</a>. 482 * 483 * <p> A cryptographic service is always associated with a particular 484 * algorithm or type. For example, a digital signature service is 485 * always associated with a particular algorithm (e.g., DSA), 486 * and a CertificateFactory service is always associated with 487 * a particular certificate type (e.g., X.509). 488 * 489 * <p>The selection criterion must be specified in one of the following two 490 * formats: 491 * <ul> 492 * <li> <i><crypto_service>.<algorithm_or_type></i> <p> The 493 * cryptographic service name must not contain any dots. 494 * <p> A 495 * provider satisfies the specified selection criterion iff the provider 496 * implements the 497 * specified algorithm or type for the specified cryptographic service. 498 * <p> For example, "CertificateFactory.X.509" 499 * would be satisfied by any provider that supplied 500 * a CertificateFactory implementation for X.509 certificates. 501 * <li> <i><crypto_service>.<algorithm_or_type> 502 * <attribute_name>:< attribute_value></i> 503 * <p> The cryptographic service name must not contain any dots. There 504 * must be one or more space charaters between the 505 * <i><algorithm_or_type></i> and the <i><attribute_name></i>. 506 * <p> A provider satisfies this selection criterion iff the 507 * provider implements the specified algorithm or type for the specified 508 * cryptographic service and its implementation meets the 509 * constraint expressed by the specified attribute name/value pair. 510 * <p> For example, "Signature.SHA1withDSA KeySize:1024" would be 511 * satisfied by any provider that implemented 512 * the SHA1withDSA signature algorithm with a keysize of 1024 (or larger). 513 * 514 * </ul> 515 * 516 * <p> See Appendix A in the <a href= 517 * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> 518 * Java Cryptography Architecture API Specification & Reference </a> 519 * for information about standard cryptographic service names, standard 520 * algorithm names and standard attribute names. 521 * 522 * @param filter the criterion for selecting 523 * providers. The filter is case-insensitive. 524 * 525 * @return all the installed providers that satisfy the selection 526 * criterion, or null if no such providers have been installed. 527 * 528 * @throws InvalidParameterException 529 * if the filter is not in the required format 530 * @throws NullPointerException if filter is null 531 * 532 * @see #getProviders(java.util.Map) 533 * @since 1.3 534 */ 535 public static Provider[] getProviders(String filter) { 536 String key = null; 537 String value = null; 538 int index = filter.indexOf(':'); 539 540 if (index == -1) { 541 key = filter; 542 value = ""; 543 } else { 544 key = filter.substring(0, index); 545 value = filter.substring(index + 1); 546 } 547 548 Hashtable<String, String> hashtableFilter = 549 new Hashtable<String, String>(1); 550 hashtableFilter.put(key, value); 551 552 return (getProviders(hashtableFilter)); 553 } 554 555 /** 556 * Returns an array containing all installed providers that satisfy the 557 * specified* selection criteria, or null if no such providers have been 558 * installed. The returned providers are ordered 559 * according to their <a href= 560 * "#insertProviderAt(java.security.Provider, int)">preference order</a>. 561 * 562 * <p>The selection criteria are represented by a map. 563 * Each map entry represents a selection criterion. 564 * A provider is selected iff it satisfies all selection 565 * criteria. The key for any entry in such a map must be in one of the 566 * following two formats: 567 * <ul> 568 * <li> <i><crypto_service>.<algorithm_or_type></i> 569 * <p> The cryptographic service name must not contain any dots. 570 * <p> The value associated with the key must be an empty string. 571 * <p> A provider 572 * satisfies this selection criterion iff the provider implements the 573 * specified algorithm or type for the specified cryptographic service. 574 * <li> <i><crypto_service>.<algorithm_or_type> <attribute_name></i> 575 * <p> The cryptographic service name must not contain any dots. There 576 * must be one or more space charaters between the <i><algorithm_or_type></i> 577 * and the <i><attribute_name></i>. 578 * <p> The value associated with the key must be a non-empty string. 579 * A provider satisfies this selection criterion iff the 580 * provider implements the specified algorithm or type for the specified 581 * cryptographic service and its implementation meets the 582 * constraint expressed by the specified attribute name/value pair. 583 * </ul> 584 * 585 * <p> See Appendix A in the <a href= 586 * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> 587 * Java Cryptography Architecture API Specification & Reference </a> 588 * for information about standard cryptographic service names, standard 589 * algorithm names and standard attribute names. 590 * 591 * @param filter the criteria for selecting 592 * providers. The filter is case-insensitive. 593 * 594 * @return all the installed providers that satisfy the selection 595 * criteria, or null if no such providers have been installed. 596 * 597 * @throws InvalidParameterException 598 * if the filter is not in the required format 599 * @throws NullPointerException if filter is null 600 * 601 * @see #getProviders(java.lang.String) 602 * @since 1.3 603 */ 604 public static Provider[] getProviders(Map<String,String> filter) { 605 // Get all installed providers first. 606 // Then only return those providers who satisfy the selection criteria. 607 Provider[] allProviders = Security.getProviders(); 608 Set<String> keySet = filter.keySet(); 609 LinkedHashSet<Provider> candidates = new LinkedHashSet<Provider>(5); 610 611 // Returns all installed providers 612 // if the selection criteria is null. 613 if ((keySet == null) || (allProviders == null)) { 614 return allProviders; 615 } 616 617 boolean firstSearch = true; 618 619 // For each selection criterion, remove providers 620 // which don't satisfy the criterion from the candidate set. 621 for (Iterator<String> ite = keySet.iterator(); ite.hasNext(); ) { 622 String key = ite.next(); 623 String value = filter.get(key); 624 625 LinkedHashSet<Provider> newCandidates = getAllQualifyingCandidates(key, value, 626 allProviders); 627 if (firstSearch) { 628 candidates = newCandidates; 629 firstSearch = false; 630 } 631 632 if ((newCandidates != null) && !newCandidates.isEmpty()) { 633 // For each provider in the candidates set, if it 634 // isn't in the newCandidate set, we should remove 635 // it from the candidate set. 636 for (Iterator<Provider> cansIte = candidates.iterator(); 637 cansIte.hasNext(); ) { 638 Provider prov = cansIte.next(); 639 if (!newCandidates.contains(prov)) { 640 cansIte.remove(); 641 } 642 } 643 } else { 644 candidates = null; 645 break; 646 } 647 } 648 649 if ((candidates == null) || (candidates.isEmpty())) 650 return null; 651 652 Object[] candidatesArray = candidates.toArray(); 653 Provider[] result = new Provider[candidatesArray.length]; 654 655 for (int i = 0; i < result.length; i++) { 656 result[i] = (Provider)candidatesArray[i]; 657 } 658 659 return result; 660 } 661 662 // Map containing cached Spi Class objects of the specified type 663 private static final Map<String,Class> spiMap = 664 new ConcurrentHashMap<String,Class>(); 665 666 /** 667 * Return the Class object for the given engine type 668 * (e.g. "MessageDigest"). Works for Spis in the java.security package 669 * only. 670 */ 671 private static Class getSpiClass(String type) { 672 Class clazz = spiMap.get(type); 673 if (clazz != null) { 674 return clazz; 675 } 676 try { 677 clazz = Class.forName("java.security." + type + "Spi"); 678 spiMap.put(type, clazz); 679 return clazz; 680 } catch (ClassNotFoundException e) { 681 throw new AssertionError("Spi class not found", e); 682 } 683 } 684 685 /* 686 * Returns an array of objects: the first object in the array is 687 * an instance of an implementation of the requested algorithm 688 * and type, and the second object in the array identifies the provider 689 * of that implementation. 690 * The <code>provider</code> argument can be null, in which case all 691 * configured providers will be searched in order of preference. 692 */ 693 static Object[] getImpl(String algorithm, String type, String provider) 694 throws NoSuchAlgorithmException, NoSuchProviderException { 695 if (provider == null) { 696 return GetInstance.getInstance 697 (type, getSpiClass(type), algorithm).toArray(); 698 } else { 699 return GetInstance.getInstance 700 (type, getSpiClass(type), algorithm, provider).toArray(); 701 } 702 } 703 704 static Object[] getImpl(String algorithm, String type, String provider, 705 Object params) throws NoSuchAlgorithmException, 706 NoSuchProviderException, InvalidAlgorithmParameterException { 707 if (provider == null) { 708 return GetInstance.getInstance 709 (type, getSpiClass(type), algorithm, params).toArray(); 710 } else { 711 return GetInstance.getInstance 712 (type, getSpiClass(type), algorithm, params, provider).toArray(); 713 } 714 } 715 716 /* 717 * Returns an array of objects: the first object in the array is 718 * an instance of an implementation of the requested algorithm 719 * and type, and the second object in the array identifies the provider 720 * of that implementation. 721 * The <code>provider</code> argument cannot be null. 722 */ 723 static Object[] getImpl(String algorithm, String type, Provider provider) 724 throws NoSuchAlgorithmException { 725 return GetInstance.getInstance 726 (type, getSpiClass(type), algorithm, provider).toArray(); 727 } 728 729 static Object[] getImpl(String algorithm, String type, Provider provider, 730 Object params) throws NoSuchAlgorithmException, 731 InvalidAlgorithmParameterException { 732 return GetInstance.getInstance 733 (type, getSpiClass(type), algorithm, params, provider).toArray(); 734 } 735 736 /** 737 * Gets a security property value. 738 * 739 * <p>First, if there is a security manager, its 740 * <code>checkPermission</code> method is called with a 741 * <code>java.security.SecurityPermission("getProperty."+key)</code> 742 * permission to see if it's ok to retrieve the specified 743 * security property value.. 744 * 745 * @param key the key of the property being retrieved. 746 * 747 * @return the value of the security property corresponding to key. 748 * 749 * @throws SecurityException 750 * if a security manager exists and its <code>{@link 751 * java.lang.SecurityManager#checkPermission}</code> method 752 * denies 753 * access to retrieve the specified security property value 754 * @throws NullPointerException is key is null 755 * 756 * @see #setProperty 757 * @see java.security.SecurityPermission 758 */ 759 public static String getProperty(String key) { 760 SecurityManager sm = System.getSecurityManager(); 761 if (sm != null) { 762 sm.checkPermission(new SecurityPermission("getProperty."+ 763 key)); 764 } 765 String name = props.getProperty(key); 766 if (name != null) 767 name = name.trim(); // could be a class name with trailing ws 768 return name; 769 } 770 771 /** 772 * Sets a security property value. 773 * 774 * <p>First, if there is a security manager, its 775 * <code>checkPermission</code> method is called with a 776 * <code>java.security.SecurityPermission("setProperty."+key)</code> 777 * permission to see if it's ok to set the specified 778 * security property value. 779 * 780 * @param key the name of the property to be set. 781 * 782 * @param datum the value of the property to be set. 783 * 784 * @throws SecurityException 785 * if a security manager exists and its <code>{@link 786 * java.lang.SecurityManager#checkPermission}</code> method 787 * denies access to set the specified security property value 788 * @throws NullPointerException if key or datum is null 789 * 790 * @see #getProperty 791 * @see java.security.SecurityPermission 792 */ 793 public static void setProperty(String key, String datum) { 794 check("setProperty."+key); 795 props.put(key, datum); 796 invalidateSMCache(key); /* See below. */ 797 } 798 799 /* 800 * Implementation detail: If the property we just set in 801 * setProperty() was either "package.access" or 802 * "package.definition", we need to signal to the SecurityManager 803 * class that the value has just changed, and that it should 804 * invalidate it's local cache values. 805 * 806 * Rather than create a new API entry for this function, 807 * we use reflection to set a private variable. 808 */ 809 private static void invalidateSMCache(String key) { 810 811 final boolean pa = key.equals("package.access"); 812 final boolean pd = key.equals("package.definition"); 813 814 if (pa || pd) { 815 AccessController.doPrivileged(new PrivilegedAction<Void>() { 816 public Void run() { 817 try { 818 /* Get the class via the bootstrap class loader. */ 819 Class cl = Class.forName( 820 "java.lang.SecurityManager", false, null); 821 Field f = null; 822 boolean accessible = false; 823 824 if (pa) { 825 f = cl.getDeclaredField("packageAccessValid"); 826 accessible = f.isAccessible(); 827 f.setAccessible(true); 828 } else { 829 f = cl.getDeclaredField("packageDefinitionValid"); 830 accessible = f.isAccessible(); 831 f.setAccessible(true); 832 } 833 f.setBoolean(f, false); 834 f.setAccessible(accessible); 835 } 836 catch (Exception e1) { 837 /* If we couldn't get the class, it hasn't 838 * been loaded yet. If there is no such 839 * field, we shouldn't try to set it. There 840 * shouldn't be a security execption, as we 841 * are loaded by boot class loader, and we 842 * are inside a doPrivileged() here. 843 * 844 * NOOP: don't do anything... 845 */ 846 } 847 return null; 848 } /* run */ 849 }); /* PrivilegedAction */ 850 } /* if */ 851 } 852 853 private static void check(String directive) { 854 SecurityManager security = System.getSecurityManager(); 855 if (security != null) { 856 security.checkSecurityAccess(directive); 857 } 858 } 859 860 /* 861 * Returns all providers who satisfy the specified 862 * criterion. 863 */ 864 private static LinkedHashSet<Provider> getAllQualifyingCandidates( 865 String filterKey, 866 String filterValue, 867 Provider[] allProviders) { 868 String[] filterComponents = getFilterComponents(filterKey, 869 filterValue); 870 871 // The first component is the service name. 872 // The second is the algorithm name. 873 // If the third isn't null, that is the attrinute name. 874 String serviceName = filterComponents[0]; 875 String algName = filterComponents[1]; 876 String attrName = filterComponents[2]; 877 878 return getProvidersNotUsingCache(serviceName, algName, attrName, 879 filterValue, allProviders); 880 } 881 882 private static LinkedHashSet<Provider> getProvidersNotUsingCache( 883 String serviceName, 884 String algName, 885 String attrName, 886 String filterValue, 887 Provider[] allProviders) { 888 LinkedHashSet<Provider> candidates = new LinkedHashSet<Provider>(5); 889 for (int i = 0; i < allProviders.length; i++) { 890 if (isCriterionSatisfied(allProviders[i], serviceName, 891 algName, 892 attrName, filterValue)) { 893 candidates.add(allProviders[i]); 894 } 895 } 896 return candidates; 897 } 898 899 /* 900 * Returns true if the given provider satisfies 901 * the selection criterion key:value. 902 */ 903 private static boolean isCriterionSatisfied(Provider prov, 904 String serviceName, 905 String algName, 906 String attrName, 907 String filterValue) { 908 String key = serviceName + '.' + algName; 909 910 if (attrName != null) { 911 key += ' ' + attrName; 912 } 913 // Check whether the provider has a property 914 // whose key is the same as the given key. 915 String propValue = getProviderProperty(key, prov); 916 917 if (propValue == null) { 918 // Check whether we have an alias instead 919 // of a standard name in the key. 920 String standardName = getProviderProperty("Alg.Alias." + 921 serviceName + "." + 922 algName, 923 prov); 924 if (standardName != null) { 925 key = serviceName + "." + standardName; 926 927 if (attrName != null) { 928 key += ' ' + attrName; 929 } 930 931 propValue = getProviderProperty(key, prov); 932 } 933 934 if (propValue == null) { 935 // The provider doesn't have the given 936 // key in its property list. 937 return false; 938 } 939 } 940 941 // If the key is in the format of: 942 // <crypto_service>.<algorithm_or_type>, 943 // there is no need to check the value. 944 945 if (attrName == null) { 946 return true; 947 } 948 949 // If we get here, the key must be in the 950 // format of <crypto_service>.<algorithm_or_provider> <attribute_name>. 951 if (isStandardAttr(attrName)) { 952 return isConstraintSatisfied(attrName, filterValue, propValue); 953 } else { 954 return filterValue.equalsIgnoreCase(propValue); 955 } 956 } 957 958 /* 959 * Returns true if the attribute is a standard attribute; 960 * otherwise, returns false. 961 */ 962 private static boolean isStandardAttr(String attribute) { 963 // For now, we just have two standard attributes: 964 // KeySize and ImplementedIn. 965 if (attribute.equalsIgnoreCase("KeySize")) 966 return true; 967 968 if (attribute.equalsIgnoreCase("ImplementedIn")) 969 return true; 970 971 return false; 972 } 973 974 /* 975 * Returns true if the requested attribute value is supported; 976 * otherwise, returns false. 977 */ 978 private static boolean isConstraintSatisfied(String attribute, 979 String value, 980 String prop) { 981 // For KeySize, prop is the max key size the 982 // provider supports for a specific <crypto_service>.<algorithm>. 983 if (attribute.equalsIgnoreCase("KeySize")) { 984 int requestedSize = Integer.parseInt(value); 985 int maxSize = Integer.parseInt(prop); 986 if (requestedSize <= maxSize) { 987 return true; 988 } else { 989 return false; 990 } 991 } 992 993 // For Type, prop is the type of the implementation 994 // for a specific <crypto service>.<algorithm>. 995 if (attribute.equalsIgnoreCase("ImplementedIn")) { 996 return value.equalsIgnoreCase(prop); 997 } 998 999 return false; 1000 } 1001 1002 static String[] getFilterComponents(String filterKey, String filterValue) { 1003 int algIndex = filterKey.indexOf('.'); 1004 1005 if (algIndex < 0) { 1006 // There must be a dot in the filter, and the dot 1007 // shouldn't be at the beginning of this string. 1008 throw new InvalidParameterException("Invalid filter"); 1009 } 1010 1011 String serviceName = filterKey.substring(0, algIndex); 1012 String algName = null; 1013 String attrName = null; 1014 1015 if (filterValue.length() == 0) { 1016 // The filterValue is an empty string. So the filterKey 1017 // should be in the format of <crypto_service>.<algorithm_or_type>. 1018 algName = filterKey.substring(algIndex + 1).trim(); 1019 if (algName.length() == 0) { 1020 // There must be a algorithm or type name. 1021 throw new InvalidParameterException("Invalid filter"); 1022 } 1023 } else { 1024 // The filterValue is a non-empty string. So the filterKey must be 1025 // in the format of 1026 // <crypto_service>.<algorithm_or_type> <attribute_name> 1027 int attrIndex = filterKey.indexOf(' '); 1028 1029 if (attrIndex == -1) { 1030 // There is no attribute name in the filter. 1031 throw new InvalidParameterException("Invalid filter"); 1032 } else { 1033 attrName = filterKey.substring(attrIndex + 1).trim(); 1034 if (attrName.length() == 0) { 1035 // There is no attribute name in the filter. 1036 throw new InvalidParameterException("Invalid filter"); 1037 } 1038 } 1039 1040 // There must be an algorithm name in the filter. 1041 if ((attrIndex < algIndex) || 1042 (algIndex == attrIndex - 1)) { 1043 throw new InvalidParameterException("Invalid filter"); 1044 } else { 1045 algName = filterKey.substring(algIndex + 1, attrIndex); 1046 } 1047 } 1048 1049 String[] result = new String[3]; 1050 result[0] = serviceName; 1051 result[1] = algName; 1052 result[2] = attrName; 1053 1054 return result; 1055 } 1056 1057 /** 1058 * Returns a Set of Strings containing the names of all available 1059 * algorithms or types for the specified Java cryptographic service 1060 * (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore). Returns 1061 * an empty Set if there is no provider that supports the 1062 * specified service or if serviceName is null. For a complete list 1063 * of Java cryptographic services, please see the 1064 * <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">Java 1065 * Cryptography Architecture API Specification & Reference</a>. 1066 * Note: the returned set is immutable. 1067 * 1068 * @param serviceName the name of the Java cryptographic 1069 * service (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore). 1070 * Note: this parameter is case-insensitive. 1071 * 1072 * @return a Set of Strings containing the names of all available 1073 * algorithms or types for the specified Java cryptographic service 1074 * or an empty set if no provider supports the specified service. 1075 * 1076 * @since 1.4 1077 **/ 1078 public static Set<String> getAlgorithms(String serviceName) { 1079 1080 if ((serviceName == null) || (serviceName.length() == 0) || 1081 (serviceName.endsWith("."))) { 1082 return Collections.EMPTY_SET; 1083 } 1084 1085 HashSet<String> result = new HashSet<String>(); 1086 Provider[] providers = Security.getProviders(); 1087 1088 for (int i = 0; i < providers.length; i++) { 1089 // Check the keys for each provider. 1090 for (Enumeration<Object> e = providers[i].keys(); 1091 e.hasMoreElements(); ) { 1092 String currentKey = ((String)e.nextElement()).toUpperCase(); 1093 if (currentKey.startsWith(serviceName.toUpperCase())) { 1094 // We should skip the currentKey if it contains a 1095 // whitespace. The reason is: such an entry in the 1096 // provider property contains attributes for the 1097 // implementation of an algorithm. We are only interested 1098 // in entries which lead to the implementation 1099 // classes. 1100 if (currentKey.indexOf(" ") < 0) { 1101 result.add(currentKey.substring(serviceName.length() + 1)); 1102 } 1103 } 1104 } 1105 } 1106 return Collections.unmodifiableSet(result); 1107 } 1108 }