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 18package java.security; 19 20import java.io.BufferedInputStream; 21import java.io.InputStream; 22import java.io.InputStreamReader; 23import java.io.Reader; 24import java.util.ArrayList; 25import java.util.Enumeration; 26import java.util.HashMap; 27import java.util.HashSet; 28import java.util.Iterator; 29import java.util.List; 30import java.util.Map; 31import java.util.Map.Entry; 32import java.util.Properties; 33import java.util.Set; 34import org.apache.harmony.security.fortress.Engine; 35import org.apache.harmony.security.fortress.SecurityAccess; 36import org.apache.harmony.security.fortress.Services; 37 38/** 39 * {@code Security} is the central class in the Java Security API. It manages 40 * the list of security {@code Provider} that have been installed into this 41 * runtime environment. 42 */ 43public final class Security { 44 45 // Security properties 46 private static final Properties secprops = new Properties(); 47 48 // static initialization 49 // - load security properties files 50 // - load statically registered providers 51 // - if no provider description file found then load default providers 52 // Note: Getting the input stream for the security.properties file is factored into its own 53 // function, which will be intercepted during boot image creation. 54 static { 55 boolean loaded = false; 56 Reader input = null; 57 try { 58 input = getSecurityPropertiesReader(); 59 secprops.load(input); 60 loaded = true; 61 } catch (Exception ex) { 62 System.logE("Could not load 'security.properties'", ex); 63 } finally { 64 if (input != null) { 65 try { 66 input.close(); 67 } catch (Exception ex) { 68 System.logW("Could not close 'security.properties'", ex); 69 } 70 } 71 } 72 if (!loaded) { 73 registerDefaultProviders(); 74 } 75 Engine.door = new SecurityDoor(); 76 } 77 78 private static Reader getSecurityPropertiesReader() throws Exception { 79 InputStream configStream = Security.class.getResourceAsStream("security.properties"); 80 return new InputStreamReader(new BufferedInputStream(configStream), "ISO-8859-1"); 81 } 82 83 /** 84 * This class can't be instantiated. 85 */ 86 private Security() { 87 } 88 89 // Register default providers 90 private static void registerDefaultProviders() { 91 secprops.put("security.provider.1", "com.android.org.conscrypt.OpenSSLProvider"); 92 secprops.put("security.provider.2", "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider"); 93 secprops.put("security.provider.3", "org.apache.harmony.security.provider.crypto.CryptoProvider"); 94 secprops.put("security.provider.4", "com.android.org.conscrypt.JSSEProvider"); 95 } 96 97 /** 98 * Returns value for the specified algorithm with the specified name. 99 * 100 * @param algName 101 * the name of the algorithm. 102 * @param propName 103 * the name of the property. 104 * @return value of the property. 105 * @deprecated Use {@link AlgorithmParameters} and {@link KeyFactory} instead. 106 */ 107 @Deprecated 108 public static String getAlgorithmProperty(String algName, String propName) { 109 if (algName == null || propName == null) { 110 return null; 111 } 112 String prop = "Alg." + propName + "." + algName; 113 Provider[] providers = getProviders(); 114 for (Provider provider : providers) { 115 for (Enumeration<?> e = provider.propertyNames(); e.hasMoreElements();) { 116 String propertyName = (String) e.nextElement(); 117 if (propertyName.equalsIgnoreCase(prop)) { 118 return provider.getProperty(propertyName); 119 } 120 } 121 } 122 return null; 123 } 124 125 /** 126 * Insert the given {@code Provider} at the specified {@code position}. The 127 * positions define the preference order in which providers are searched for 128 * requested algorithms. 129 * 130 * @param provider 131 * the provider to insert. 132 * @param position 133 * the position (starting from 1). 134 * @return the actual position or {@code -1} if the given {@code provider} 135 * was already in the list. The actual position may be different 136 * from the desired position. 137 */ 138 public static synchronized int insertProviderAt(Provider provider, int position) { 139 // check that provider is not already 140 // installed, else return -1; if (position <1) or (position > max 141 // position) position = max position + 1; insert provider, shift up 142 // one position for next providers; Note: The position is 1-based 143 if (getProvider(provider.getName()) != null) { 144 return -1; 145 } 146 int result = Services.insertProviderAt(provider, position); 147 renumProviders(); 148 return result; 149 } 150 151 /** 152 * Adds the given {@code provider} to the collection of providers at the 153 * next available position. 154 * 155 * @param provider 156 * the provider to be added. 157 * @return the actual position or {@code -1} if the given {@code provider} 158 * was already in the list. 159 */ 160 public static int addProvider(Provider provider) { 161 return insertProviderAt(provider, 0); 162 } 163 164 /** 165 * Removes the {@code Provider} with the specified name form the collection 166 * of providers. If the the {@code Provider} with the specified name is 167 * removed, all provider at a greater position are shifted down one 168 * position. 169 * 170 * <p>Returns silently if {@code name} is {@code null} or no provider with the 171 * specified name is installed. 172 * 173 * @param name 174 * the name of the provider to remove. 175 */ 176 public static synchronized void removeProvider(String name) { 177 // It is not clear from spec.: 178 // 1. if name is null, should we checkSecurityAccess or not? 179 // throw SecurityException or not? 180 // 2. as 1 but provider is not installed 181 // 3. behavior if name is empty string? 182 183 Provider p; 184 if ((name == null) || (name.length() == 0)) { 185 return; 186 } 187 p = getProvider(name); 188 if (p == null) { 189 return; 190 } 191 Services.removeProvider(p.getProviderNumber()); 192 renumProviders(); 193 p.setProviderNumber(-1); 194 } 195 196 /** 197 * Returns an array containing all installed providers. The providers are 198 * ordered according their preference order. 199 * 200 * @return an array containing all installed providers. 201 */ 202 public static synchronized Provider[] getProviders() { 203 ArrayList<Provider> providers = Services.getProviders(); 204 return providers.toArray(new Provider[providers.size()]); 205 } 206 207 /** 208 * Returns the {@code Provider} with the specified name. Returns {@code 209 * null} if name is {@code null} or no provider with the specified name is 210 * installed. 211 * 212 * @param name 213 * the name of the requested provider. 214 * @return the provider with the specified name, maybe {@code null}. 215 */ 216 public static synchronized Provider getProvider(String name) { 217 return Services.getProvider(name); 218 } 219 220 /** 221 * Returns the array of providers which meet the user supplied string 222 * filter. The specified filter must be supplied in one of two formats: 223 * <nl> 224 * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE 225 * <p> 226 * (for example: "MessageDigest.SHA") 227 * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE 228 * ATTR_NAME:ATTR_VALUE 229 * <p> 230 * (for example: "Signature.MD2withRSA KeySize:512") 231 * </nl> 232 * 233 * @param filter 234 * case-insensitive filter. 235 * @return the providers which meet the user supplied string filter {@code 236 * filter}. A {@code null} value signifies that none of the 237 * installed providers meets the filter specification. 238 * @throws InvalidParameterException 239 * if an unusable filter is supplied. 240 * @throws NullPointerException 241 * if {@code filter} is {@code null}. 242 */ 243 public static Provider[] getProviders(String filter) { 244 if (filter == null) { 245 throw new NullPointerException("filter == null"); 246 } 247 if (filter.length() == 0) { 248 throw new InvalidParameterException(); 249 } 250 HashMap<String, String> hm = new HashMap<String, String>(); 251 int i = filter.indexOf(':'); 252 if ((i == filter.length() - 1) || (i == 0)) { 253 throw new InvalidParameterException(); 254 } 255 if (i < 1) { 256 hm.put(filter, ""); 257 } else { 258 hm.put(filter.substring(0, i), filter.substring(i + 1)); 259 } 260 return getProviders(hm); 261 } 262 263 /** 264 * Returns the array of providers which meet the user supplied set of 265 * filters. The filter must be supplied in one of two formats: 266 * <nl> 267 * <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE 268 * <p> 269 * for example: "MessageDigest.SHA" The value associated with the key must 270 * be an empty string. <li> CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE 271 * ATTR_NAME:ATTR_VALUE 272 * <p> 273 * for example: "Signature.MD2withRSA KeySize:512" where "KeySize:512" is 274 * the value of the filter map entry. 275 * </nl> 276 * 277 * @param filter 278 * case-insensitive filter. 279 * @return the providers which meet the user supplied string filter {@code 280 * filter}. A {@code null} value signifies that none of the 281 * installed providers meets the filter specification. 282 * @throws InvalidParameterException 283 * if an unusable filter is supplied. 284 * @throws NullPointerException 285 * if {@code filter} is {@code null}. 286 */ 287 public static synchronized Provider[] getProviders(Map<String,String> filter) { 288 if (filter == null) { 289 throw new NullPointerException("filter == null"); 290 } 291 if (filter.isEmpty()) { 292 return null; 293 } 294 ArrayList<Provider> result = new ArrayList<Provider>(Services.getProviders()); 295 Set<Entry<String, String>> keys = filter.entrySet(); 296 Map.Entry<String, String> entry; 297 for (Iterator<Entry<String, String>> it = keys.iterator(); it.hasNext();) { 298 entry = it.next(); 299 String key = entry.getKey(); 300 String val = entry.getValue(); 301 String attribute = null; 302 int i = key.indexOf(' '); 303 int j = key.indexOf('.'); 304 if (j == -1) { 305 throw new InvalidParameterException(); 306 } 307 if (i == -1) { // <crypto_service>.<algorithm_or_type> 308 if (val.length() != 0) { 309 throw new InvalidParameterException(); 310 } 311 } else { // <crypto_service>.<algorithm_or_type> <attribute_name> 312 if (val.length() == 0) { 313 throw new InvalidParameterException(); 314 } 315 attribute = key.substring(i + 1); 316 if (attribute.trim().length() == 0) { 317 throw new InvalidParameterException(); 318 } 319 key = key.substring(0, i); 320 } 321 String serv = key.substring(0, j); 322 String alg = key.substring(j + 1); 323 if (serv.length() == 0 || alg.length() == 0) { 324 throw new InvalidParameterException(); 325 } 326 filterProviders(result, serv, alg, attribute, val); 327 } 328 if (result.size() > 0) { 329 return result.toArray(new Provider[result.size()]); 330 } 331 return null; 332 } 333 334 private static void filterProviders(ArrayList<Provider> providers, String service, 335 String algorithm, String attribute, String attrValue) { 336 Iterator<Provider> it = providers.iterator(); 337 while (it.hasNext()) { 338 Provider p = it.next(); 339 if (!p.implementsAlg(service, algorithm, attribute, attrValue)) { 340 it.remove(); 341 } 342 } 343 } 344 345 /** 346 * Returns the value of the security property named by the argument. 347 * 348 * @param key 349 * the name of the requested security property. 350 * @return the value of the security property. 351 */ 352 public static String getProperty(String key) { 353 if (key == null) { 354 throw new NullPointerException("key == null"); 355 } 356 String property = secprops.getProperty(key); 357 if (property != null) { 358 property = property.trim(); 359 } 360 return property; 361 } 362 363 /** 364 * Sets the value of the specified security property. 365 */ 366 public static void setProperty(String key, String value) { 367 Services.setNeedRefresh(); 368 secprops.put(key, value); 369 } 370 371 /** 372 * Returns a {@code Set} of all registered algorithms for the specified 373 * cryptographic service. {@code "Signature"}, {@code "Cipher"} and {@code 374 * "KeyStore"} are examples for such kind of services. 375 * 376 * @param serviceName 377 * the case-insensitive name of the service. 378 * @return a {@code Set} of all registered algorithms for the specified 379 * cryptographic service, or an empty {@code Set} if {@code 380 * serviceName} is {@code null} or if no registered provider 381 * provides the requested service. 382 */ 383 public static Set<String> getAlgorithms(String serviceName) { 384 Set<String> result = new HashSet<String>(); 385 // compatibility with RI 386 if (serviceName == null) { 387 return result; 388 } 389 for (Provider provider : getProviders()) { 390 for (Provider.Service service: provider.getServices()) { 391 if (service.getType().equalsIgnoreCase(serviceName)) { 392 result.add(service.getAlgorithm()); 393 } 394 } 395 } 396 return result; 397 } 398 399 /** 400 * 401 * Update sequence numbers of all providers. 402 * 403 */ 404 private static void renumProviders() { 405 ArrayList<Provider> providers = Services.getProviders(); 406 for (int i = 0; i < providers.size(); i++) { 407 providers.get(i).setProviderNumber(i + 1); 408 } 409 } 410 411 private static class SecurityDoor implements SecurityAccess { 412 // Access to Security.renumProviders() 413 public void renumProviders() { 414 Security.renumProviders(); 415 } 416 417 // Access to Security.getAliases() 418 public List<String> getAliases(Provider.Service s) { 419 return s.getAliases(); 420 } 421 422 // Access to Provider.getService() 423 public Provider.Service getService(Provider p, String type) { 424 return p.getService(type); 425 } 426 } 427} 428