1082 lines
42 KiB
Java
1082 lines
42 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package java.security;
|
|
|
|
import java.lang.reflect.*;
|
|
import java.util.*;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.io.*;
|
|
import sun.security.jca.GetInstance;
|
|
import sun.security.jca.ProviderList;
|
|
import sun.security.jca.Providers;
|
|
|
|
/**
|
|
* <p>This class centralizes all security properties and common security
|
|
* methods. One of its primary uses is to manage providers.
|
|
*
|
|
* <p>The default values of security properties are read from an
|
|
* implementation-specific location, which is typically the properties file
|
|
* {@code lib/security/java.security} in the Java installation directory.
|
|
*
|
|
* @author Benjamin Renaud
|
|
*/
|
|
|
|
public final class Security {
|
|
|
|
// Android-added: Track the version to allow callers know when something has changed.
|
|
private static final AtomicInteger version = new AtomicInteger();
|
|
|
|
// Android-removed: Debug is stubbed and disabled on Android.
|
|
// /* Are we debugging? -- for developers */
|
|
// private static final Debug sdebug =
|
|
// Debug.getInstance("properties");
|
|
|
|
/* The java.security properties */
|
|
// Android-changed: Added final.
|
|
private static final Properties props;
|
|
|
|
// An element in the cache
|
|
private static class ProviderProperty {
|
|
String className;
|
|
Provider provider;
|
|
}
|
|
|
|
static {
|
|
// BEGIN Android-changed: doPrivileged is stubbed on Android.
|
|
// Also, because props is final it must be assigned in the static block, not a method.
|
|
/*
|
|
// doPrivileged here because there are multiple
|
|
// things in initialize that might require privs.
|
|
// (the FileInputStream call and the File.exists call,
|
|
// the securityPropFile call, etc)
|
|
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
|
public Void run() {
|
|
initialize();
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
private static void initialize() {
|
|
*/
|
|
// END Android-changed: doPrivileged is stubbed on Android.
|
|
props = new Properties();
|
|
boolean loadedProps = false;
|
|
// BEGIN Android-changed: Use a resource file, Android logging, and only one file.
|
|
InputStream is = null;
|
|
try {
|
|
/*
|
|
* Android keeps the property file in a resource file.
|
|
*/
|
|
InputStream propStream = Security.class.getResourceAsStream("security.properties");
|
|
if (propStream == null) {
|
|
System.logE("Could not find 'security.properties'.");
|
|
} else {
|
|
is = new BufferedInputStream(propStream);
|
|
props.load(is);
|
|
loadedProps = true;
|
|
}
|
|
} catch (IOException ex) {
|
|
System.logE("Could not load 'security.properties'", ex);
|
|
} finally {
|
|
if (is != null) {
|
|
try {
|
|
is.close();
|
|
} catch (IOException ignored) {}
|
|
}
|
|
}
|
|
// END Android-changed: Use a resource file, Android logging, and only one file.
|
|
|
|
if (!loadedProps) {
|
|
initializeStatic();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize to default values, if <java.home>/lib/java.security
|
|
* is not found.
|
|
*/
|
|
private static void initializeStatic() {
|
|
// Android-changed: Use Conscrypt and BC, not the sun.security providers.
|
|
/*
|
|
props.put("security.provider.1", "sun.security.provider.Sun");
|
|
props.put("security.provider.2", "sun.security.rsa.SunRsaSign");
|
|
props.put("security.provider.3", "com.sun.net.ssl.internal.ssl.Provider");
|
|
props.put("security.provider.4", "com.sun.crypto.provider.SunJCE");
|
|
props.put("security.provider.5", "sun.security.jgss.SunProvider");
|
|
props.put("security.provider.6", "com.sun.security.sasl.Provider");
|
|
*/
|
|
props.put("security.provider.1", "com.android.org.conscrypt.OpenSSLProvider");
|
|
props.put("security.provider.2", "sun.security.provider.CertPathProvider");
|
|
props.put("security.provider.3", "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");
|
|
props.put("security.provider.4", "com.android.org.conscrypt.JSSEProvider");
|
|
}
|
|
|
|
/**
|
|
* Don't let anyone instantiate this.
|
|
*/
|
|
private Security() {
|
|
}
|
|
|
|
/**
|
|
* Looks up providers, and returns the property (and its associated
|
|
* provider) mapping the key, if any.
|
|
* The order in which the providers are looked up is the
|
|
* provider-preference order, as specificed in the security
|
|
* properties file.
|
|
*/
|
|
private static ProviderProperty getProviderProperty(String key) {
|
|
ProviderProperty entry = null;
|
|
|
|
List<Provider> providers = Providers.getProviderList().providers();
|
|
for (int i = 0; i < providers.size(); i++) {
|
|
|
|
String matchKey = null;
|
|
Provider prov = providers.get(i);
|
|
String prop = prov.getProperty(key);
|
|
|
|
if (prop == null) {
|
|
// Is there a match if we do a case-insensitive property name
|
|
// comparison? Let's try ...
|
|
for (Enumeration<Object> e = prov.keys();
|
|
e.hasMoreElements() && prop == null; ) {
|
|
matchKey = (String)e.nextElement();
|
|
if (key.equalsIgnoreCase(matchKey)) {
|
|
prop = prov.getProperty(matchKey);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prop != null) {
|
|
ProviderProperty newEntry = new ProviderProperty();
|
|
newEntry.className = prop;
|
|
newEntry.provider = prov;
|
|
return newEntry;
|
|
}
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* Returns the property (if any) mapping the key for the given provider.
|
|
*/
|
|
private static String getProviderProperty(String key, Provider provider) {
|
|
String prop = provider.getProperty(key);
|
|
if (prop == null) {
|
|
// Is there a match if we do a case-insensitive property name
|
|
// comparison? Let's try ...
|
|
for (Enumeration<Object> e = provider.keys();
|
|
e.hasMoreElements() && prop == null; ) {
|
|
String matchKey = (String)e.nextElement();
|
|
if (key.equalsIgnoreCase(matchKey)) {
|
|
prop = provider.getProperty(matchKey);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return prop;
|
|
}
|
|
|
|
/**
|
|
* Gets a specified property for an algorithm. The algorithm name
|
|
* should be a standard name. See the <a href=
|
|
* "{@docRoot}/../technotes/guides/security/StandardNames.html">
|
|
* Java Cryptography Architecture Standard Algorithm Name Documentation</a>
|
|
* for information about standard algorithm names.
|
|
*
|
|
* One possible use is by specialized algorithm parsers, which may map
|
|
* classes to algorithms which they understand (much like Key parsers
|
|
* do).
|
|
*
|
|
* @param algName the algorithm name.
|
|
*
|
|
* @param propName the name of the property to get.
|
|
*
|
|
* @return the value of the specified property.
|
|
*
|
|
* @deprecated This method used to return the value of a proprietary
|
|
* property in the master file of the "SUN" Cryptographic Service
|
|
* Provider in order to determine how to parse algorithm-specific
|
|
* parameters. Use the new provider-based and algorithm-independent
|
|
* {@code AlgorithmParameters} and {@code KeyFactory} engine
|
|
* classes (introduced in the J2SE version 1.2 platform) instead.
|
|
*/
|
|
@Deprecated
|
|
public static String getAlgorithmProperty(String algName,
|
|
String propName) {
|
|
ProviderProperty entry = getProviderProperty("Alg." + propName
|
|
+ "." + algName);
|
|
if (entry != null) {
|
|
return entry.className;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a new provider, at a specified position. The position is
|
|
* the preference order in which providers are searched for
|
|
* requested algorithms. The position is 1-based, that is,
|
|
* 1 is most preferred, followed by 2, and so on.
|
|
*
|
|
* <p>If the given provider is installed at the requested position,
|
|
* the provider that used to be at that position, and all providers
|
|
* with a position greater than {@code position}, are shifted up
|
|
* one position (towards the end of the list of installed providers).
|
|
*
|
|
* <p>A provider cannot be added if it is already installed.
|
|
*
|
|
* <p>If there is a security manager, the
|
|
* {@link java.lang.SecurityManager#checkSecurityAccess} method is called
|
|
* with the {@code "insertProvider"} permission target name to see if
|
|
* it's ok to add a new provider. If this permission check is denied,
|
|
* {@code checkSecurityAccess} is called again with the
|
|
* {@code "insertProvider."+provider.getName()} permission target name. If
|
|
* both checks are denied, a {@code SecurityException} is thrown.
|
|
*
|
|
* @param provider the provider to be added.
|
|
*
|
|
* @param position the preference position that the caller would
|
|
* like for this provider.
|
|
*
|
|
* @return the actual preference position in which the provider was
|
|
* added, or -1 if the provider was not added because it is
|
|
* already installed.
|
|
*
|
|
* @throws NullPointerException if provider is null
|
|
* @throws SecurityException
|
|
* if a security manager exists and its {@link
|
|
* java.lang.SecurityManager#checkSecurityAccess} method
|
|
* denies access to add a new provider
|
|
*
|
|
* @see #getProvider
|
|
* @see #removeProvider
|
|
* @see java.security.SecurityPermission
|
|
*/
|
|
public static synchronized int insertProviderAt(Provider provider,
|
|
int position) {
|
|
String providerName = provider.getName();
|
|
// Android-removed: Checks using SecurityManager, which is not functional in Android.
|
|
// checkInsertProvider(providerName);
|
|
ProviderList list = Providers.getFullProviderList();
|
|
ProviderList newList = ProviderList.insertAt(list, provider, position - 1);
|
|
if (list == newList) {
|
|
return -1;
|
|
}
|
|
// Android-added: Version tracking call.
|
|
increaseVersion();
|
|
Providers.setProviderList(newList);
|
|
return newList.getIndex(providerName) + 1;
|
|
}
|
|
|
|
/**
|
|
* Adds a provider to the next position available.
|
|
*
|
|
* <p>If there is a security manager, the
|
|
* {@link java.lang.SecurityManager#checkSecurityAccess} method is called
|
|
* with the {@code "insertProvider"} permission target name to see if
|
|
* it's ok to add a new provider. If this permission check is denied,
|
|
* {@code checkSecurityAccess} is called again with the
|
|
* {@code "insertProvider."+provider.getName()} permission target name. If
|
|
* both checks are denied, a {@code SecurityException} is thrown.
|
|
*
|
|
* @param provider the provider to be added.
|
|
*
|
|
* @return the preference position in which the provider was
|
|
* added, or -1 if the provider was not added because it is
|
|
* already installed.
|
|
*
|
|
* @throws NullPointerException if provider is null
|
|
* @throws SecurityException
|
|
* if a security manager exists and its {@link
|
|
* java.lang.SecurityManager#checkSecurityAccess} method
|
|
* denies access to add a new provider
|
|
*
|
|
* @see #getProvider
|
|
* @see #removeProvider
|
|
* @see java.security.SecurityPermission
|
|
*/
|
|
public static int addProvider(Provider provider) {
|
|
/*
|
|
* We can't assign a position here because the statically
|
|
* registered providers may not have been installed yet.
|
|
* insertProviderAt() will fix that value after it has
|
|
* loaded the static providers.
|
|
*/
|
|
return insertProviderAt(provider, 0);
|
|
}
|
|
|
|
/**
|
|
* Removes the provider with the specified name.
|
|
*
|
|
* <p>When the specified provider is removed, all providers located
|
|
* at a position greater than where the specified provider was are shifted
|
|
* down one position (towards the head of the list of installed
|
|
* providers).
|
|
*
|
|
* <p>This method returns silently if the provider is not installed or
|
|
* if name is null.
|
|
*
|
|
* <p>First, if there is a security manager, its
|
|
* {@code checkSecurityAccess}
|
|
* method is called with the string {@code "removeProvider."+name}
|
|
* to see if it's ok to remove the provider.
|
|
* If the default implementation of {@code checkSecurityAccess}
|
|
* is used (i.e., that method is not overriden), then this will result in
|
|
* a call to the security manager's {@code checkPermission} method
|
|
* with a {@code SecurityPermission("removeProvider."+name)}
|
|
* permission.
|
|
*
|
|
* @param name the name of the provider to remove.
|
|
*
|
|
* @throws SecurityException
|
|
* if a security manager exists and its {@link
|
|
* java.lang.SecurityManager#checkSecurityAccess} method
|
|
* denies
|
|
* access to remove the provider
|
|
*
|
|
* @see #getProvider
|
|
* @see #addProvider
|
|
*/
|
|
public static synchronized void removeProvider(String name) {
|
|
// Android-removed: Checks using SecurityManager, which is not functional in Android.
|
|
// check("removeProvider." + name);
|
|
ProviderList list = Providers.getFullProviderList();
|
|
ProviderList newList = ProviderList.remove(list, name);
|
|
Providers.setProviderList(newList);
|
|
// Android-added: Version tracking call.
|
|
increaseVersion();
|
|
}
|
|
|
|
/**
|
|
* Returns an array containing all the installed providers. The order of
|
|
* the providers in the array is their preference order.
|
|
*
|
|
* @return an array of all the installed providers.
|
|
*/
|
|
public static Provider[] getProviders() {
|
|
return Providers.getFullProviderList().toArray();
|
|
}
|
|
|
|
/**
|
|
* Returns the provider installed with the specified name, if
|
|
* any. Returns null if no provider with the specified name is
|
|
* installed or if name is null.
|
|
*
|
|
* @param name the name of the provider to get.
|
|
*
|
|
* @return the provider of the specified name.
|
|
*
|
|
* @see #removeProvider
|
|
* @see #addProvider
|
|
*/
|
|
public static Provider getProvider(String name) {
|
|
return Providers.getProviderList().getProvider(name);
|
|
}
|
|
|
|
/**
|
|
* Returns an array containing all installed providers that satisfy the
|
|
* specified selection criterion, or null if no such providers have been
|
|
* installed. The returned providers are ordered
|
|
* according to their
|
|
* {@linkplain #insertProviderAt(java.security.Provider, int) preference order}.
|
|
*
|
|
* <p> A cryptographic service is always associated with a particular
|
|
* algorithm or type. For example, a digital signature service is
|
|
* always associated with a particular algorithm (e.g., DSA),
|
|
* and a CertificateFactory service is always associated with
|
|
* a particular certificate type (e.g., X.509).
|
|
*
|
|
* <p>The selection criterion must be specified in one of the following two
|
|
* formats:
|
|
* <ul>
|
|
* <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
|
|
* <p> The cryptographic service name must not contain any dots.
|
|
* <p> A
|
|
* provider satisfies the specified selection criterion iff the provider
|
|
* implements the
|
|
* specified algorithm or type for the specified cryptographic service.
|
|
* <p> For example, "CertificateFactory.X.509"
|
|
* would be satisfied by any provider that supplied
|
|
* a CertificateFactory implementation for X.509 certificates.
|
|
* <li> <i>{@literal <crypto_service>.<algorithm_or_type>
|
|
* <attribute_name>:<attribute_value>}</i>
|
|
* <p> The cryptographic service name must not contain any dots. There
|
|
* must be one or more space characters between the
|
|
* <i>{@literal <algorithm_or_type>}</i> and the
|
|
* <i>{@literal <attribute_name>}</i>.
|
|
* <p> A provider satisfies this selection criterion iff the
|
|
* provider implements the specified algorithm or type for the specified
|
|
* cryptographic service and its implementation meets the
|
|
* constraint expressed by the specified attribute name/value pair.
|
|
* <p> For example, "Signature.SHA1withDSA KeySize:1024" would be
|
|
* satisfied by any provider that implemented
|
|
* the SHA1withDSA signature algorithm with a keysize of 1024 (or larger).
|
|
*
|
|
* </ul>
|
|
*
|
|
* <p> See the <a href=
|
|
* "{@docRoot}/../technotes/guides/security/StandardNames.html">
|
|
* Java Cryptography Architecture Standard Algorithm Name Documentation</a>
|
|
* for information about standard cryptographic service names, standard
|
|
* algorithm names and standard attribute names.
|
|
*
|
|
* @param filter the criterion for selecting
|
|
* providers. The filter is case-insensitive.
|
|
*
|
|
* @return all the installed providers that satisfy the selection
|
|
* criterion, or null if no such providers have been installed.
|
|
*
|
|
* @throws InvalidParameterException
|
|
* if the filter is not in the required format
|
|
* @throws NullPointerException if filter is null
|
|
*
|
|
* @see #getProviders(java.util.Map)
|
|
* @since 1.3
|
|
*/
|
|
public static Provider[] getProviders(String filter) {
|
|
String key = null;
|
|
String value = null;
|
|
int index = filter.indexOf(':');
|
|
|
|
if (index == -1) {
|
|
key = filter;
|
|
value = "";
|
|
} else {
|
|
key = filter.substring(0, index);
|
|
value = filter.substring(index + 1);
|
|
}
|
|
|
|
Hashtable<String, String> hashtableFilter = new Hashtable<>(1);
|
|
hashtableFilter.put(key, value);
|
|
|
|
return (getProviders(hashtableFilter));
|
|
}
|
|
|
|
/**
|
|
* Returns an array containing all installed providers that satisfy the
|
|
* specified* selection criteria, or null if no such providers have been
|
|
* installed. The returned providers are ordered
|
|
* according to their
|
|
* {@linkplain #insertProviderAt(java.security.Provider, int)
|
|
* preference order}.
|
|
*
|
|
* <p>The selection criteria are represented by a map.
|
|
* Each map entry represents a selection criterion.
|
|
* A provider is selected iff it satisfies all selection
|
|
* criteria. The key for any entry in such a map must be in one of the
|
|
* following two formats:
|
|
* <ul>
|
|
* <li> <i>{@literal <crypto_service>.<algorithm_or_type>}</i>
|
|
* <p> The cryptographic service name must not contain any dots.
|
|
* <p> The value associated with the key must be an empty string.
|
|
* <p> A provider
|
|
* satisfies this selection criterion iff the provider implements the
|
|
* specified algorithm or type for the specified cryptographic service.
|
|
* <li> <i>{@literal <crypto_service>}.
|
|
* {@literal <algorithm_or_type> <attribute_name>}</i>
|
|
* <p> The cryptographic service name must not contain any dots. There
|
|
* must be one or more space characters between the
|
|
* <i>{@literal <algorithm_or_type>}</i>
|
|
* and the <i>{@literal <attribute_name>}</i>.
|
|
* <p> The value associated with the key must be a non-empty string.
|
|
* A provider satisfies this selection criterion iff the
|
|
* provider implements the specified algorithm or type for the specified
|
|
* cryptographic service and its implementation meets the
|
|
* constraint expressed by the specified attribute name/value pair.
|
|
* </ul>
|
|
*
|
|
* <p> See the <a href=
|
|
* "../../../technotes/guides/security/StandardNames.html">
|
|
* Java Cryptography Architecture Standard Algorithm Name Documentation</a>
|
|
* for information about standard cryptographic service names, standard
|
|
* algorithm names and standard attribute names.
|
|
*
|
|
* @param filter the criteria for selecting
|
|
* providers. The filter is case-insensitive.
|
|
*
|
|
* @return all the installed providers that satisfy the selection
|
|
* criteria, or null if no such providers have been installed.
|
|
*
|
|
* @throws InvalidParameterException
|
|
* if the filter is not in the required format
|
|
* @throws NullPointerException if filter is null
|
|
*
|
|
* @see #getProviders(java.lang.String)
|
|
* @since 1.3
|
|
*/
|
|
public static Provider[] getProviders(Map<String,String> filter) {
|
|
// Get all installed providers first.
|
|
// Then only return those providers who satisfy the selection criteria.
|
|
Provider[] allProviders = Security.getProviders();
|
|
Set<String> keySet = filter.keySet();
|
|
LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5);
|
|
|
|
// Returns all installed providers
|
|
// if the selection criteria is null.
|
|
if ((keySet == null) || (allProviders == null)) {
|
|
return allProviders;
|
|
}
|
|
|
|
boolean firstSearch = true;
|
|
|
|
// For each selection criterion, remove providers
|
|
// which don't satisfy the criterion from the candidate set.
|
|
for (Iterator<String> ite = keySet.iterator(); ite.hasNext(); ) {
|
|
String key = ite.next();
|
|
String value = filter.get(key);
|
|
|
|
LinkedHashSet<Provider> newCandidates = getAllQualifyingCandidates(key, value,
|
|
allProviders);
|
|
if (firstSearch) {
|
|
candidates = newCandidates;
|
|
firstSearch = false;
|
|
}
|
|
|
|
if ((newCandidates != null) && !newCandidates.isEmpty()) {
|
|
// For each provider in the candidates set, if it
|
|
// isn't in the newCandidate set, we should remove
|
|
// it from the candidate set.
|
|
for (Iterator<Provider> cansIte = candidates.iterator();
|
|
cansIte.hasNext(); ) {
|
|
Provider prov = cansIte.next();
|
|
if (!newCandidates.contains(prov)) {
|
|
cansIte.remove();
|
|
}
|
|
}
|
|
} else {
|
|
candidates = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((candidates == null) || (candidates.isEmpty()))
|
|
return null;
|
|
|
|
Object[] candidatesArray = candidates.toArray();
|
|
Provider[] result = new Provider[candidatesArray.length];
|
|
|
|
for (int i = 0; i < result.length; i++) {
|
|
result[i] = (Provider)candidatesArray[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Map containing cached Spi Class objects of the specified type
|
|
private static final Map<String, Class<?>> spiMap =
|
|
new ConcurrentHashMap<>();
|
|
|
|
/**
|
|
* Return the Class object for the given engine type
|
|
* (e.g. "MessageDigest"). Works for Spis in the java.security package
|
|
* only.
|
|
*/
|
|
private static Class<?> getSpiClass(String type) {
|
|
Class<?> clazz = spiMap.get(type);
|
|
if (clazz != null) {
|
|
return clazz;
|
|
}
|
|
try {
|
|
clazz = Class.forName("java.security." + type + "Spi");
|
|
spiMap.put(type, clazz);
|
|
return clazz;
|
|
} catch (ClassNotFoundException e) {
|
|
throw new AssertionError("Spi class not found", e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns an array of objects: the first object in the array is
|
|
* an instance of an implementation of the requested algorithm
|
|
* and type, and the second object in the array identifies the provider
|
|
* of that implementation.
|
|
* The {@code provider} argument can be null, in which case all
|
|
* configured providers will be searched in order of preference.
|
|
*/
|
|
static Object[] getImpl(String algorithm, String type, String provider)
|
|
throws NoSuchAlgorithmException, NoSuchProviderException {
|
|
if (provider == null) {
|
|
return GetInstance.getInstance
|
|
(type, getSpiClass(type), algorithm).toArray();
|
|
} else {
|
|
return GetInstance.getInstance
|
|
(type, getSpiClass(type), algorithm, provider).toArray();
|
|
}
|
|
}
|
|
|
|
static Object[] getImpl(String algorithm, String type, String provider,
|
|
Object params) throws NoSuchAlgorithmException,
|
|
NoSuchProviderException, InvalidAlgorithmParameterException {
|
|
if (provider == null) {
|
|
return GetInstance.getInstance
|
|
(type, getSpiClass(type), algorithm, params).toArray();
|
|
} else {
|
|
return GetInstance.getInstance
|
|
(type, getSpiClass(type), algorithm, params, provider).toArray();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns an array of objects: the first object in the array is
|
|
* an instance of an implementation of the requested algorithm
|
|
* and type, and the second object in the array identifies the provider
|
|
* of that implementation.
|
|
* The {@code provider} argument cannot be null.
|
|
*/
|
|
static Object[] getImpl(String algorithm, String type, Provider provider)
|
|
throws NoSuchAlgorithmException {
|
|
return GetInstance.getInstance
|
|
(type, getSpiClass(type), algorithm, provider).toArray();
|
|
}
|
|
|
|
static Object[] getImpl(String algorithm, String type, Provider provider,
|
|
Object params) throws NoSuchAlgorithmException,
|
|
InvalidAlgorithmParameterException {
|
|
return GetInstance.getInstance
|
|
(type, getSpiClass(type), algorithm, params, provider).toArray();
|
|
}
|
|
|
|
/**
|
|
* Gets a security property value.
|
|
*
|
|
* <p>First, if there is a security manager, its
|
|
* {@code checkPermission} method is called with a
|
|
* {@code java.security.SecurityPermission("getProperty."+key)}
|
|
* permission to see if it's ok to retrieve the specified
|
|
* security property value..
|
|
*
|
|
* @param key the key of the property being retrieved.
|
|
*
|
|
* @return the value of the security property corresponding to key.
|
|
*
|
|
* @throws SecurityException
|
|
* if a security manager exists and its {@link
|
|
* java.lang.SecurityManager#checkPermission} method
|
|
* denies
|
|
* access to retrieve the specified security property value
|
|
* @throws NullPointerException is key is null
|
|
*
|
|
* @see #setProperty
|
|
* @see java.security.SecurityPermission
|
|
*/
|
|
public static String getProperty(String key) {
|
|
SecurityManager sm = System.getSecurityManager();
|
|
if (sm != null) {
|
|
sm.checkPermission(new SecurityPermission("getProperty."+
|
|
key));
|
|
}
|
|
String name = props.getProperty(key);
|
|
if (name != null)
|
|
name = name.trim(); // could be a class name with trailing ws
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Sets a security property value.
|
|
*
|
|
* <p>First, if there is a security manager, its
|
|
* {@code checkPermission} method is called with a
|
|
* {@code java.security.SecurityPermission("setProperty."+key)}
|
|
* permission to see if it's ok to set the specified
|
|
* security property value.
|
|
*
|
|
* @param key the name of the property to be set.
|
|
*
|
|
* @param datum the value of the property to be set.
|
|
*
|
|
* @throws SecurityException
|
|
* if a security manager exists and its {@link
|
|
* java.lang.SecurityManager#checkPermission} method
|
|
* denies access to set the specified security property value
|
|
* @throws NullPointerException if key or datum is null
|
|
*
|
|
* @see #getProperty
|
|
* @see java.security.SecurityPermission
|
|
*/
|
|
public static void setProperty(String key, String datum) {
|
|
// Android-removed: Checks using SecurityManager, which is not functional in Android.
|
|
// check("setProperty."+key);
|
|
props.put(key, datum);
|
|
// Android-added: Version tracking call.
|
|
increaseVersion();
|
|
invalidateSMCache(key); /* See below. */
|
|
}
|
|
|
|
/*
|
|
* Implementation detail: If the property we just set in
|
|
* setProperty() was either "package.access" or
|
|
* "package.definition", we need to signal to the SecurityManager
|
|
* class that the value has just changed, and that it should
|
|
* invalidate it's local cache values.
|
|
*
|
|
* Rather than create a new API entry for this function,
|
|
* we use reflection to set a private variable.
|
|
*/
|
|
private static void invalidateSMCache(String key) {
|
|
|
|
final boolean pa = key.equals("package.access");
|
|
final boolean pd = key.equals("package.definition");
|
|
|
|
if (pa || pd) {
|
|
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
|
public Void run() {
|
|
try {
|
|
/* Get the class via the bootstrap class loader. */
|
|
Class<?> cl = Class.forName(
|
|
"java.lang.SecurityManager", false, null);
|
|
Field f = null;
|
|
boolean accessible = false;
|
|
|
|
if (pa) {
|
|
f = cl.getDeclaredField("packageAccessValid");
|
|
accessible = f.isAccessible();
|
|
f.setAccessible(true);
|
|
} else {
|
|
f = cl.getDeclaredField("packageDefinitionValid");
|
|
accessible = f.isAccessible();
|
|
f.setAccessible(true);
|
|
}
|
|
f.setBoolean(f, false);
|
|
f.setAccessible(accessible);
|
|
}
|
|
catch (Exception e1) {
|
|
/* If we couldn't get the class, it hasn't
|
|
* been loaded yet. If there is no such
|
|
* field, we shouldn't try to set it. There
|
|
* shouldn't be a security execption, as we
|
|
* are loaded by boot class loader, and we
|
|
* are inside a doPrivileged() here.
|
|
*
|
|
* NOOP: don't do anything...
|
|
*/
|
|
}
|
|
return null;
|
|
} /* run */
|
|
}); /* PrivilegedAction */
|
|
} /* if */
|
|
}
|
|
|
|
// BEGIN Android-removed: SecurityManager is stubbed on Android.
|
|
/*
|
|
private static void check(String directive) {
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkSecurityAccess(directive);
|
|
}
|
|
}
|
|
|
|
private static void checkInsertProvider(String name) {
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
try {
|
|
security.checkSecurityAccess("insertProvider");
|
|
} catch (SecurityException se1) {
|
|
try {
|
|
security.checkSecurityAccess("insertProvider." + name);
|
|
} catch (SecurityException se2) {
|
|
// throw first exception, but add second to suppressed
|
|
se1.addSuppressed(se2);
|
|
throw se1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
// END Android-removed: SecurityManager is stubbed on Android.
|
|
|
|
/*
|
|
* Returns all providers who satisfy the specified
|
|
* criterion.
|
|
*/
|
|
private static LinkedHashSet<Provider> getAllQualifyingCandidates(
|
|
String filterKey,
|
|
String filterValue,
|
|
Provider[] allProviders) {
|
|
String[] filterComponents = getFilterComponents(filterKey,
|
|
filterValue);
|
|
|
|
// The first component is the service name.
|
|
// The second is the algorithm name.
|
|
// If the third isn't null, that is the attrinute name.
|
|
String serviceName = filterComponents[0];
|
|
String algName = filterComponents[1];
|
|
String attrName = filterComponents[2];
|
|
|
|
return getProvidersNotUsingCache(serviceName, algName, attrName,
|
|
filterValue, allProviders);
|
|
}
|
|
|
|
private static LinkedHashSet<Provider> getProvidersNotUsingCache(
|
|
String serviceName,
|
|
String algName,
|
|
String attrName,
|
|
String filterValue,
|
|
Provider[] allProviders) {
|
|
LinkedHashSet<Provider> candidates = new LinkedHashSet<>(5);
|
|
for (int i = 0; i < allProviders.length; i++) {
|
|
if (isCriterionSatisfied(allProviders[i], serviceName,
|
|
algName,
|
|
attrName, filterValue)) {
|
|
candidates.add(allProviders[i]);
|
|
}
|
|
}
|
|
return candidates;
|
|
}
|
|
|
|
/*
|
|
* Returns true if the given provider satisfies
|
|
* the selection criterion key:value.
|
|
*/
|
|
private static boolean isCriterionSatisfied(Provider prov,
|
|
String serviceName,
|
|
String algName,
|
|
String attrName,
|
|
String filterValue) {
|
|
String key = serviceName + '.' + algName;
|
|
|
|
if (attrName != null) {
|
|
key += ' ' + attrName;
|
|
}
|
|
// Check whether the provider has a property
|
|
// whose key is the same as the given key.
|
|
String propValue = getProviderProperty(key, prov);
|
|
|
|
if (propValue == null) {
|
|
// Check whether we have an alias instead
|
|
// of a standard name in the key.
|
|
String standardName = getProviderProperty("Alg.Alias." +
|
|
serviceName + "." +
|
|
algName,
|
|
prov);
|
|
if (standardName != null) {
|
|
key = serviceName + "." + standardName;
|
|
|
|
if (attrName != null) {
|
|
key += ' ' + attrName;
|
|
}
|
|
|
|
propValue = getProviderProperty(key, prov);
|
|
}
|
|
|
|
if (propValue == null) {
|
|
// The provider doesn't have the given
|
|
// key in its property list.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If the key is in the format of:
|
|
// <crypto_service>.<algorithm_or_type>,
|
|
// there is no need to check the value.
|
|
|
|
if (attrName == null) {
|
|
return true;
|
|
}
|
|
|
|
// If we get here, the key must be in the
|
|
// format of <crypto_service>.<algorithm_or_provider> <attribute_name>.
|
|
if (isStandardAttr(attrName)) {
|
|
return isConstraintSatisfied(attrName, filterValue, propValue);
|
|
} else {
|
|
return filterValue.equalsIgnoreCase(propValue);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns true if the attribute is a standard attribute;
|
|
* otherwise, returns false.
|
|
*/
|
|
private static boolean isStandardAttr(String attribute) {
|
|
// For now, we just have two standard attributes:
|
|
// KeySize and ImplementedIn.
|
|
if (attribute.equalsIgnoreCase("KeySize"))
|
|
return true;
|
|
|
|
if (attribute.equalsIgnoreCase("ImplementedIn"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Returns true if the requested attribute value is supported;
|
|
* otherwise, returns false.
|
|
*/
|
|
private static boolean isConstraintSatisfied(String attribute,
|
|
String value,
|
|
String prop) {
|
|
// For KeySize, prop is the max key size the
|
|
// provider supports for a specific <crypto_service>.<algorithm>.
|
|
if (attribute.equalsIgnoreCase("KeySize")) {
|
|
int requestedSize = Integer.parseInt(value);
|
|
int maxSize = Integer.parseInt(prop);
|
|
if (requestedSize <= maxSize) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// For Type, prop is the type of the implementation
|
|
// for a specific <crypto service>.<algorithm>.
|
|
if (attribute.equalsIgnoreCase("ImplementedIn")) {
|
|
return value.equalsIgnoreCase(prop);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static String[] getFilterComponents(String filterKey, String filterValue) {
|
|
int algIndex = filterKey.indexOf('.');
|
|
|
|
if (algIndex < 0) {
|
|
// There must be a dot in the filter, and the dot
|
|
// shouldn't be at the beginning of this string.
|
|
throw new InvalidParameterException("Invalid filter");
|
|
}
|
|
|
|
String serviceName = filterKey.substring(0, algIndex);
|
|
String algName = null;
|
|
String attrName = null;
|
|
|
|
if (filterValue.length() == 0) {
|
|
// The filterValue is an empty string. So the filterKey
|
|
// should be in the format of <crypto_service>.<algorithm_or_type>.
|
|
algName = filterKey.substring(algIndex + 1).trim();
|
|
if (algName.length() == 0) {
|
|
// There must be a algorithm or type name.
|
|
throw new InvalidParameterException("Invalid filter");
|
|
}
|
|
} else {
|
|
// The filterValue is a non-empty string. So the filterKey must be
|
|
// in the format of
|
|
// <crypto_service>.<algorithm_or_type> <attribute_name>
|
|
int attrIndex = filterKey.indexOf(' ');
|
|
|
|
if (attrIndex == -1) {
|
|
// There is no attribute name in the filter.
|
|
throw new InvalidParameterException("Invalid filter");
|
|
} else {
|
|
attrName = filterKey.substring(attrIndex + 1).trim();
|
|
if (attrName.length() == 0) {
|
|
// There is no attribute name in the filter.
|
|
throw new InvalidParameterException("Invalid filter");
|
|
}
|
|
}
|
|
|
|
// There must be an algorithm name in the filter.
|
|
if ((attrIndex < algIndex) ||
|
|
(algIndex == attrIndex - 1)) {
|
|
throw new InvalidParameterException("Invalid filter");
|
|
} else {
|
|
algName = filterKey.substring(algIndex + 1, attrIndex);
|
|
}
|
|
}
|
|
|
|
String[] result = new String[3];
|
|
result[0] = serviceName;
|
|
result[1] = algName;
|
|
result[2] = attrName;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns a Set of Strings containing the names of all available
|
|
* algorithms or types for the specified Java cryptographic service
|
|
* (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore). Returns
|
|
* an empty Set if there is no provider that supports the
|
|
* specified service or if serviceName is null. For a complete list
|
|
* of Java cryptographic services, please see the
|
|
* <a href="../../../technotes/guides/security/crypto/CryptoSpec.html">Java
|
|
* Cryptography Architecture API Specification & Reference</a>.
|
|
* Note: the returned set is immutable.
|
|
*
|
|
* @param serviceName the name of the Java cryptographic
|
|
* service (e.g., Signature, MessageDigest, Cipher, Mac, KeyStore).
|
|
* Note: this parameter is case-insensitive.
|
|
*
|
|
* @return a Set of Strings containing the names of all available
|
|
* algorithms or types for the specified Java cryptographic service
|
|
* or an empty set if no provider supports the specified service.
|
|
*
|
|
* @since 1.4
|
|
**/
|
|
public static Set<String> getAlgorithms(String serviceName) {
|
|
|
|
if ((serviceName == null) || (serviceName.length() == 0) ||
|
|
(serviceName.endsWith("."))) {
|
|
return Collections.emptySet();
|
|
}
|
|
|
|
HashSet<String> result = new HashSet<>();
|
|
Provider[] providers = Security.getProviders();
|
|
|
|
for (int i = 0; i < providers.length; i++) {
|
|
// Check the keys for each provider.
|
|
for (Enumeration<Object> e = providers[i].keys();
|
|
e.hasMoreElements(); ) {
|
|
String currentKey =
|
|
((String)e.nextElement()).toUpperCase(Locale.ENGLISH);
|
|
if (currentKey.startsWith(
|
|
serviceName.toUpperCase(Locale.ENGLISH))) {
|
|
// We should skip the currentKey if it contains a
|
|
// whitespace. The reason is: such an entry in the
|
|
// provider property contains attributes for the
|
|
// implementation of an algorithm. We are only interested
|
|
// in entries which lead to the implementation
|
|
// classes.
|
|
if (currentKey.indexOf(" ") < 0) {
|
|
result.add(currentKey.substring(
|
|
serviceName.length() + 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Collections.unmodifiableSet(result);
|
|
}
|
|
|
|
// BEGIN Android-added: Methods for version handling.
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static void increaseVersion() {
|
|
version.incrementAndGet();
|
|
}
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static int getVersion() {
|
|
return version.get();
|
|
}
|
|
// END Android-added: Methods for version handling.
|
|
}
|