mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
6447816: Provider filtering (getProviders) is not working with OR'd conditions
Reviewed-by: weijun
This commit is contained in:
parent
43e191d64b
commit
812d805a48
2 changed files with 295 additions and 204 deletions
|
@ -519,14 +519,20 @@ public final class Security {
|
|||
public static Provider[] getProviders(String filter) {
|
||||
String key;
|
||||
String value;
|
||||
|
||||
int index = filter.indexOf(':');
|
||||
|
||||
if (index == -1) {
|
||||
key = filter;
|
||||
if (index == -1) { // <crypto_service>.<algo_or_type> only
|
||||
key = filter.trim();
|
||||
value = "";
|
||||
} else {
|
||||
key = filter.substring(0, index);
|
||||
value = filter.substring(index + 1);
|
||||
// <crypto_service>.<algo_or_type> <attr_name>:<attr_value>
|
||||
key = filter.substring(0, index).trim();
|
||||
value = filter.substring(index + 1).trim();
|
||||
// ensure value is not empty here; rest will be checked in Criteria
|
||||
if (value.isEmpty()) {
|
||||
throw new InvalidParameterException("Invalid filter");
|
||||
}
|
||||
}
|
||||
|
||||
Hashtable<String, String> hashtableFilter = new Hashtable<>(1);
|
||||
|
@ -591,42 +597,31 @@ public final class Security {
|
|||
// 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);
|
||||
Set<Map.Entry<String, String>> entries = filter.entrySet();
|
||||
|
||||
// Returns all installed providers
|
||||
// if the selection criteria is null.
|
||||
if ((keySet == null) || (allProviders == null)) {
|
||||
if (allProviders == null || allProviders.length == 0) {
|
||||
return null;
|
||||
} else if (entries == null) {
|
||||
// return all installed providers if the selection criteria is null
|
||||
return allProviders;
|
||||
} else if (entries.isEmpty()) {
|
||||
// return null if the selection criteria is empty; this is to match
|
||||
// earlier behavior
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean firstSearch = true;
|
||||
LinkedList<Provider> candidates =
|
||||
new LinkedList<>(Arrays.asList(allProviders));
|
||||
|
||||
// For each selection criterion, remove providers
|
||||
// which don't satisfy the criterion from the candidate set.
|
||||
for (String key : keySet) {
|
||||
String value = filter.get(key);
|
||||
|
||||
LinkedHashSet<Provider> newCandidates = getAllQualifyingCandidates(key, value,
|
||||
allProviders);
|
||||
if (firstSearch) {
|
||||
candidates = newCandidates;
|
||||
firstSearch = false;
|
||||
for (var e : entries) {
|
||||
Criteria cr = new Criteria(e.getKey(), e.getValue());
|
||||
candidates.removeIf(p -> !cr.isCriterionSatisfied(p));
|
||||
if (candidates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!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.
|
||||
candidates.removeIf(prov -> !newCandidates.contains(prov));
|
||||
} else {
|
||||
candidates = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates == null || candidates.isEmpty())
|
||||
return null;
|
||||
};
|
||||
|
||||
return candidates.toArray(new Provider[0]);
|
||||
}
|
||||
|
@ -822,194 +817,134 @@ public final class Security {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all providers who satisfy the specified
|
||||
* criterion.
|
||||
*/
|
||||
private static LinkedHashSet<Provider> getAllQualifyingCandidates(
|
||||
String filterKey,
|
||||
String filterValue,
|
||||
Provider[] allProviders) {
|
||||
String[] filterComponents = getFilterComponents(filterKey,
|
||||
filterValue);
|
||||
private static class Criteria {
|
||||
private final String serviceName;
|
||||
private final String algName;
|
||||
private final String attrName;
|
||||
private final String attrValue;
|
||||
|
||||
// The first component is the service name.
|
||||
// The second is the algorithm name.
|
||||
// If the third isn't null, that is the attribute name.
|
||||
String serviceName = filterComponents[0];
|
||||
String algName = filterComponents[1];
|
||||
String attrName = filterComponents[2];
|
||||
Criteria(String key, String value) throws InvalidParameterException {
|
||||
|
||||
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]);
|
||||
int snEndIndex = key.indexOf('.');
|
||||
if (snEndIndex <= 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");
|
||||
}
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns {@code 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;
|
||||
serviceName = key.substring(0, snEndIndex);
|
||||
attrValue = value;
|
||||
|
||||
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;
|
||||
if (value.isEmpty()) {
|
||||
// value is empty. So the key should be in the format of
|
||||
// <crypto_service>.<algorithm_or_type>.
|
||||
algName = key.substring(snEndIndex + 1);
|
||||
attrName = null;
|
||||
} else {
|
||||
// value is non-empty. So the key must be in the format
|
||||
// of <crypto_service>.<algorithm_or_type>(one or more
|
||||
// spaces)<attribute_name>
|
||||
int algEndIndex = key.indexOf(' ', snEndIndex);
|
||||
if (algEndIndex == -1) {
|
||||
throw new InvalidParameterException
|
||||
("Invalid filter - need algorithm name");
|
||||
}
|
||||
algName = key.substring(snEndIndex + 1, algEndIndex);
|
||||
attrName = key.substring(algEndIndex + 1).trim();
|
||||
if (attrName.isEmpty()) {
|
||||
throw new InvalidParameterException
|
||||
("Invalid filter - need attribute name");
|
||||
} else if (isCompositeValue() && attrValue.indexOf('|') != -1) {
|
||||
throw new InvalidParameterException
|
||||
("Invalid filter - composite values unsupported");
|
||||
}
|
||||
|
||||
propValue = getProviderProperty(key, prov);
|
||||
}
|
||||
|
||||
// check required values
|
||||
if (serviceName.isEmpty() || algName.isEmpty()) {
|
||||
throw new InvalidParameterException
|
||||
("Invalid filter - need service and algorithm");
|
||||
}
|
||||
}
|
||||
|
||||
// returns true when this criteria contains a standard attribute
|
||||
// whose value may be composite, i.e. multiple values separated by "|"
|
||||
private boolean isCompositeValue() {
|
||||
return (attrName != null &&
|
||||
(attrName.equalsIgnoreCase("SupportedKeyClasses") ||
|
||||
attrName.equalsIgnoreCase("SupportedPaddings") ||
|
||||
attrName.equalsIgnoreCase("SupportedModes") ||
|
||||
attrName.equalsIgnoreCase("SupportedKeyFormats")));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns {@code true} if the given provider satisfies
|
||||
* the selection criterion key:value.
|
||||
*/
|
||||
private boolean isCriterionSatisfied(Provider prov) {
|
||||
// Constructed key have ONLY 1 space between algName and attrName
|
||||
String key = serviceName + '.' + algName +
|
||||
(attrName != null ? (' ' + 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) {
|
||||
// The provider doesn't have the given
|
||||
// key in its property list.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 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 +
|
||||
(attrName != null ? ' ' + attrName : "");
|
||||
propValue = getProviderProperty(key, prov);
|
||||
}
|
||||
|
||||
// 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 {@code true} if the attribute is a standard attribute;
|
||||
* otherwise, returns {@code false}.
|
||||
*/
|
||||
private static boolean isStandardAttr(String attribute) {
|
||||
// For now, we just have two standard attributes:
|
||||
// KeySize and ImplementedIn.
|
||||
if (attribute.equalsIgnoreCase("KeySize"))
|
||||
return true;
|
||||
|
||||
return attribute.equalsIgnoreCase("ImplementedIn");
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns {@code true} if the requested attribute value is supported;
|
||||
* otherwise, returns {@code 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);
|
||||
return requestedSize <= maxSize;
|
||||
}
|
||||
|
||||
// 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;
|
||||
String attrName = null;
|
||||
|
||||
if (filterValue.isEmpty()) {
|
||||
// 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.isEmpty()) {
|
||||
// There must be an 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.isEmpty()) {
|
||||
// There is no attribute name in the filter.
|
||||
throw new InvalidParameterException("Invalid filter");
|
||||
if (propValue == null) {
|
||||
// The provider doesn't have the given
|
||||
// key in its property list.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// There must be an algorithm name in the filter.
|
||||
if ((attrIndex < algIndex) ||
|
||||
(algIndex == attrIndex - 1)) {
|
||||
throw new InvalidParameterException("Invalid filter");
|
||||
// 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_type> <attribute_name>.
|
||||
|
||||
// Check the "Java Security Standard Algorithm Names" guide for the
|
||||
// list of supported Service Attributes
|
||||
|
||||
// For KeySize, prop is the max key size the provider supports
|
||||
// for a specific <crypto_service>.<algorithm>.
|
||||
if (attrName.equalsIgnoreCase("KeySize")) {
|
||||
int requestedSize = Integer.parseInt(attrValue);
|
||||
int maxSize = Integer.parseInt(propValue);
|
||||
return requestedSize <= maxSize;
|
||||
}
|
||||
|
||||
// Handle attributes with composite values
|
||||
if (isCompositeValue()) {
|
||||
String attrValue2 = attrValue.toUpperCase(Locale.ENGLISH);
|
||||
propValue = propValue.toUpperCase(Locale.ENGLISH);
|
||||
|
||||
// match value to the property components
|
||||
String[] propComponents = propValue.split("\\|");
|
||||
for (String pc : propComponents) {
|
||||
if (attrValue2.equals(pc)) return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
algName = filterKey.substring(algIndex + 1, attrIndex);
|
||||
// direct string compare (ignore case)
|
||||
return attrValue.equalsIgnoreCase(propValue);
|
||||
}
|
||||
}
|
||||
|
||||
String[] result = new String[3];
|
||||
result[0] = serviceName;
|
||||
result[1] = algName;
|
||||
result[2] = attrName;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue