1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.harmony.security.fortress; 19 20 import java.security.Provider; 21 import java.security.Security; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.Locale; 25 26 27 /** 28 * This class contains information about all registered providers and preferred 29 * implementations for all "serviceName.algName". 30 */ 31 public class Services { 32 /** 33 * Save default SecureRandom service as well. 34 * Avoids similar provider/services iteration in SecureRandom constructor. 35 */ 36 private static Provider.Service cachedSecureRandomService; 37 38 /** 39 * Need refresh flag. 40 */ 41 private static boolean needRefresh; 42 43 /** 44 * The cacheVersion is changed on every update of service 45 * information. It is used by external callers to validate their 46 * own caches of Service information. 47 */ 48 private static int cacheVersion = 1; 49 50 /** 51 * Registered providers. 52 */ 53 private static final ArrayList<Provider> providers = new ArrayList<Provider>(20); 54 55 /** 56 * Try to load and register a provider by name from the given class-loader. 57 */ 58 private static boolean initProvider(String providerClassName, ClassLoader classLoader) { 59 try { 60 Class<?> providerClass = Class.forName(providerClassName.trim(), true, classLoader); 61 Provider p = (Provider) providerClass.newInstance(); 62 providers.add(p); 63 providersNames.put(p.getName(), p); 64 return true; 65 } catch (ClassNotFoundException ignored) { 66 } catch (IllegalAccessException ignored) { 67 } catch (InstantiationException ignored) { 68 } 69 return false; 70 } 71 72 /** 73 * Hash for quick provider access by name. 74 */ 75 private static final HashMap<String, Provider> providersNames 76 = new HashMap<String, Provider>(20); 77 static { 78 String providerClassName = null; 79 int i = 1; 80 ClassLoader cl = Services.class.getClassLoader(); 81 82 while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) { 83 if (!initProvider(providerClassName, cl)) { 84 // Not on the boot classpath. Try the system class-loader. 85 // Note: DO NOT USE A LOCAL FOR GETSYSTEMCLASSLOADER! This will break compile-time 86 // initialization. 87 if (!initProvider(providerClassName, ClassLoader.getSystemClassLoader())) { 88 // TODO: Logging? 89 } 90 } 91 } 92 Engine.door.renumProviders(); 93 setNeedRefresh(); 94 } 95 96 /** 97 * Returns the actual registered providers. 98 */ 99 public static synchronized ArrayList<Provider> getProviders() { 100 return providers; 101 } 102 103 /** 104 * Returns the provider with the specified name. 105 */ 106 public static synchronized Provider getProvider(String name) { 107 if (name == null) { 108 return null; 109 } 110 return providersNames.get(name); 111 } 112 113 /** 114 * Inserts a provider at a specified 1-based position. 115 */ 116 public static synchronized int insertProviderAt(Provider provider, int position) { 117 int size = providers.size(); 118 if ((position < 1) || (position > size)) { 119 position = size + 1; 120 } 121 providers.add(position - 1, provider); 122 providersNames.put(provider.getName(), provider); 123 setNeedRefresh(); 124 return position; 125 } 126 127 /** 128 * Removes the provider at the specified 1-based position. 129 */ 130 public static synchronized void removeProvider(int providerNumber) { 131 Provider p = providers.remove(providerNumber - 1); 132 providersNames.remove(p.getName()); 133 setNeedRefresh(); 134 } 135 136 /** 137 * Looks up the requested service by type and algorithm. The service 138 * {@code type} and should be provided in the same format used when 139 * registering a service with a provider, for example, "KeyFactory.RSA". 140 * Callers can cache the returned service information but such caches should 141 * be validated against the result of Service.getCacheVersion() before use. 142 * Returns {@code null} if there are no services found. 143 */ 144 public static synchronized ArrayList<Provider.Service> getServices(String type, 145 String algorithm) { 146 ArrayList<Provider.Service> services = null; 147 for (Provider p : providers) { 148 Provider.Service s = p.getService(type, algorithm); 149 if (s != null) { 150 if (services == null) { 151 services = new ArrayList<>(providers.size()); 152 } 153 services.add(s); 154 } 155 } 156 return services; 157 } 158 159 /** 160 * Finds the first service offered of {@code type} and returns it. 161 */ 162 private static synchronized Provider.Service getFirstServiceOfType(String type) { 163 for (Provider p : providers) { 164 Provider.Service s = Engine.door.getService(p, type); 165 if (s != null) { 166 return s; 167 } 168 } 169 return null; 170 } 171 172 /** 173 * Returns the default SecureRandom service description. 174 */ 175 public static synchronized Provider.Service getSecureRandomService() { 176 getCacheVersion(); // used for side effect of updating cache if needed 177 return cachedSecureRandomService; 178 } 179 180 /** 181 * In addition to being used here when the list of providers 182 * changes, this method is also used by the Provider 183 * implementation to indicate that a provides list of services has 184 * changed. 185 */ 186 public static synchronized void setNeedRefresh() { 187 needRefresh = true; 188 } 189 190 /** 191 * Returns the current cache version. This has the possible side 192 * effect of updating the cache if needed. 193 */ 194 public static synchronized int getCacheVersion() { 195 if (needRefresh) { 196 cacheVersion++; 197 cachedSecureRandomService = getFirstServiceOfType("SecureRandom"); 198 needRefresh = false; 199 } 200 return cacheVersion; 201 } 202 } 203