security properties) and dynamically
+# installed ones (java.security.Security::addProvider API). No distinctions are
+# made between OpenJDK and third-party security providers.
+#
+# If the system property jdk.security.providers.filter is set, it supersedes
+# the security one defined here. If any of these properties is set at run time,
+# the filter could be initialized already and the new value will not take effect
+# until the JVM is relaunched. When a filter is not set or is set to the empty
+# string, filtering is disabled: all services are allowed.
+#
+# A service is typically evaluated against the filter at provider registration
+# time (java.security.Provider ::put or ::putService APIs). In special cases, when
+# a provider overrides java.security.Provider ::getService or ::getServices APIs to
+# return unregistered services, filter evaluation is deferred until its first use.
+# Services are identifiable as a combination of a security provider, a service type
+# and an algorithm name. Optionally, an algorithm alias can be used to replace the
+# algorithm name. A filter is made of a sequence of patterns that identify a service
+# according to a matching criteria (as we shall see later) and indicate an action:
+# allow or deny the service under evaluation.
+#
+# The filter syntax is as follows:
+#
+# pattern-1; pattern-2; ...; pattern-n
+#
+# Each pattern in the sequence can be optionally prefixed by a '!' character (e.g.
+# " ! pattern-1 "). White spaces between patterns, pattern prefixes ('!') and pattern
+# separators (';') are not significant. A service is evaluated against the filter
+# from left to right. If a service matches one of the patterns in the sequence, an
+# authorization decision is made: if the pattern is prefixed by a '!' character, the
+# decision is to deny it; otherwise, the decision is to allow it. If none of the
+# patterns match, the default decision is to deny the service. Once a decision is
+# made, remaining patterns are not considered.
+#
+# Each pattern's syntax has one of the following forms:
+#
+# 1) security-provider
+# 2) security-provider.service-type
+# 3.a) security-provider.service-type.algorithm-name
+# 3.b) security-provider.service-type.algorithm-alias
+# 3.c) security-provider.Cipher.transformation
+# 3.d) security-provider.Cipher.transformation-alias
+#
+# In form #1, a security provider name equal to security-provider is enough for a
+# match to be successful. In form #2, the service type must also be equal to
+# service-type. In form #3.a, the service algorithm must also be equal to
+# algorithm-name. In form #3.b, it is enough that one of the service aliases matches
+# algorithm-alias, in addition to the requirements for form #2. Form #3.c is similar
+# to form #3.a but applies to cipher transformations with multiple components
+# (algorithm/mode/padding). Form #3.d is equivalent to #3.c but looks for a
+# transformation alias match (algorithm-alias/mode/padding). In all cases, pattern and
+# service names must have valid characters and cannot be empty. Pattern matching is
+# always case insensitive.
+#
+# Characters '\n' and '\0' are not valid in a pattern. The character '.' is used as a
+# separator between different levels: security provider, service type, algorithm name or
+# algorithm alias. The following characters, when part of one of the listed levels, must
+# be escaped by prepending a '\' character: '!', '*', ' ' (white space), '.', ';', '\',
+# ':' and ','. Escaping any other character has no effect other than silently discarding
+# the '\' character.
+#
+# The aforementioned escaping rules apply to the filter value as read in the
+# java.security.Security::getProperty and java.lang.System::getProperty APIs:
+# additional escaping might be needed depending on how the filter value is passed. For
+# example, security properties require '\' characters to be escaped. Thus, to match a
+# provider whose name is abc\123, a pattern must be escaped as abc\\\\123 if passed as a
+# security property.
+#
+# In addition to character escape sequences, pattern names can contain '*' wildcards to
+# imply zero or more repetitions of any character. Wildcards behave in greedy mode, trying
+# to consume as many characters as possible and backing off if necessary.
+#
+# When a service has aliases, its algorithm name and each of the aliases are independently
+# evaluated against the filter. Notice that the security provider and service type for each
+# of these evaluations are the same. From the set of authorization decisions obtained —which
+# can potentially be contradictory—, the one made by the left-most pattern in the filter has
+# the highest priority and is finally effective. This strategy would be equivalent to
+# modifying the evaluation of a service against each pattern so that each alias is tried
+# (besides the algorithm name) and stopping if a decision is made for one of them.
+#
+# For troubleshooting, it is possible to enable filter debugging logs with the system
+# property java.security.debug=jca and look for messages prefixed by ProvidersFilter. To list
+# services allowed and not allowed by a filter for each installed security provider, run java
+# with the argument -XshowSettings:security:providers. When a filter value is syntactically
+# invalid, the exception message thrown points to the exact location in the pattern that could
+# not be parsed.
+#
+# Example
+#
+# Enable all services except those involving the algorithms MD2 or MD5:
+# jdk.security.providers.filter=!*.*.*MD2*; !*.*.*MD5*; *
+#
+#jdk.security.providers.filter=
diff --git a/src/java.security.sasl/share/classes/javax/security/sasl/Sasl.java b/src/java.security.sasl/share/classes/javax/security/sasl/Sasl.java
index 79f592fc54a1a..2cdf36216a28e 100644
--- a/src/java.security.sasl/share/classes/javax/security/sasl/Sasl.java
+++ b/src/java.security.sasl/share/classes/javax/security/sasl/Sasl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2025, 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
@@ -42,6 +42,8 @@
import java.util.logging.Level;
import java.util.logging.Logger;
+import sun.security.jca.ProvidersFilter;
+
/**
* A static class for creating SASL clients and servers.
*
@@ -337,12 +339,21 @@ private Sasl() {
* providers.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
- * {@link Security#getProperty(String) Security} property to determine
- * the preferred provider order for the specified algorithm. This
- * may be different than the order of providers returned by
- * {@link Security#getProviders() Security.getProviders()}.
+ * The JDK Reference Implementation additionally uses the following
+ * properties to customize the behavior of this method:
+ *
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified SASL mechanism(s).
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its SASL
+ * mechanism(s) implementation available.
+ *
*
* If a mechanism is listed in the {@code jdk.sasl.disabledMechanisms}
* security property, it will be ignored and won't be negotiated.
@@ -419,7 +430,8 @@ public static SaslClient createSaslClient(
if (provs != null) {
for (Provider p : provs) {
service = p.getService(type, mechName);
- if (service == null) {
+ if (service == null ||
+ !ProvidersFilter.isAllowed(service)) {
// no such service exists
continue;
}
@@ -492,12 +504,21 @@ private static Object loadFactory(Service service)
* service providers.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
- * {@link Security#getProperty(String) Security} property to determine
- * the preferred provider order for the specified algorithm. This
- * may be different than the order of providers returned by
- * {@link Security#getProviders() Security.getProviders()}.
+ * The JDK Reference Implementation additionally uses the following
+ * properties to customize the behavior of this method:
+ *
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified SASL mechanism(s).
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its SASL
+ * mechanism(s) implementation available.
+ *
*
* If {@code mechanism} is listed in the {@code jdk.sasl.disabledMechanisms}
* security property, it will be ignored and this method returns {@code null}.
@@ -565,7 +586,7 @@ private static Object loadFactory(Service service)
if (provs != null) {
for (Provider p : provs) {
service = p.getService(type, mechanism);
- if (service == null) {
+ if (service == null || !ProvidersFilter.isAllowed(service)) {
throw new SaslException("Provider does not support " +
mechanism + " " + type);
}
@@ -640,7 +661,8 @@ private static Set getFactories(String serviceName) {
Iterator iter = p.getServices().iterator();
while (iter.hasNext()) {
Service s = iter.next();
- if (s.getType().equals(serviceName)) {
+ if (ProvidersFilter.isAllowed(s) &&
+ s.getType().equals(serviceName)) {
try {
fac = loadFactory(s);
if (fac != null) {
diff --git a/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java b/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java
index a2be0caa7ea57..11cbe77dc2c18 100644
--- a/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java
+++ b/src/java.smartcardio/share/classes/javax/smartcardio/TerminalFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2025, 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
@@ -232,12 +232,21 @@ public static TerminalFactory getDefault() {
* needed may vary between different types of TerminalFactory
s.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
- * {@link Security#getProperty(String) Security} property to determine
- * the preferred provider order for the specified algorithm. This
- * may be different than the order of providers returned by
- * {@link Security#getProviders() Security.getProviders()}.
+ * The JDK Reference Implementation additionally uses the following
+ * properties to customize the behavior of this method:
+ *
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified type.
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its type
+ * implementation available.
+ *
*
* @param type the type of the requested TerminalFactory
* @param params the parameters to pass to the TerminalFactorySpi
@@ -272,6 +281,15 @@ public static TerminalFactory getInstance(String type, Object params)
* specified parameters Object. The type of parameters
* needed may vary between different types of TerminalFactory
s.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its type
+ * implementation available.
+ *
* @param type the type of the requested TerminalFactory
* @param params the parameters to pass to the TerminalFactorySpi
* implementation, or null if no parameters are needed
@@ -308,6 +326,15 @@ public static TerminalFactory getInstance(String type, Object params,
* specified parameters Object. The type of parameters
* needed may vary between different types of TerminalFactory
s.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its type
+ * implementation available.
+ *
* @param type the type of the requested TerminalFactory
* @param params the parameters to pass to the TerminalFactorySpi
* implementation, or null if no parameters are needed
diff --git a/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/TransformService.java b/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/TransformService.java
index 9040552beb762..f921dd16d21a2 100644
--- a/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/TransformService.java
+++ b/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/TransformService.java
@@ -39,6 +39,7 @@
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
+import sun.security.jca.ProvidersFilter;
/**
* A Service Provider Interface for transform and canonicalization algorithms.
@@ -135,12 +136,21 @@ protected TransformService() {}
* the {@link Security#getProviders() Security.getProviders()} method.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
- * {@link Security#getProperty(String) Security} property to determine
- * the preferred provider order for the specified algorithm. This
- * may be different than the order of providers returned by
- * {@link Security#getProviders() Security.getProviders()}.
+ * The JDK Reference Implementation additionally uses the following
+ * properties to customize the behavior of this method:
+ *
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified algorithm.
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its algorithm
+ * implementation available.
+ *
*
* @param algorithm the URI of the algorithm. See the
* {@code TransformService} section in the
@@ -177,7 +187,7 @@ protected TransformService() {}
Provider[] provs = Security.getProviders();
for (Provider p : provs) {
Service s = p.getService("TransformService", algorithm);
- if (s != null) {
+ if (s != null && ProvidersFilter.isAllowed(s)) {
String value = s.getAttribute("MechanismType");
if ((value == null && dom) ||
(value != null && value.equals(mechanismType))) {
@@ -204,6 +214,15 @@ protected TransformService() {}
* Provider
object does not have to be registered in the
* provider list.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its algorithm
+ * implementation available.
+ *
* @param algorithm the URI of the algorithm. See the
* {@code TransformService} section in the
* Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its algorithm
+ * implementation available.
+ *
* @param algorithm the URI of the algorithm. See the
* {@code TransformService} section in the
*
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified mechanism type.
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
*
* @param mechanismType the type of the XML processing mechanism and
* representation. See the {@code XMLSignatureFactory} section in the
@@ -203,7 +213,7 @@ public static XMLSignatureFactory getInstance(String mechanismType) {
Provider[] provs = Security.getProviders();
for (Provider p : provs) {
Service s = p.getService("XMLSignatureFactory", mechanismType);
- if (s != null) {
+ if (s != null && ProvidersFilter.isAllowed(s)) {
Object obj = null;
try {
obj = s.newInstance(null);
@@ -229,6 +239,15 @@ public static XMLSignatureFactory getInstance(String mechanismType) {
* Provider
object does not have to be registered in the
* provider list.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
* @param mechanismType the type of the XML processing mechanism and
* representation. See the {@code XMLSignatureFactory} section in the
* Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
* @param mechanismType the type of the XML processing mechanism and
* representation. See the {@code XMLSignatureFactory} section in the
* Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
* @return a new XMLSignatureFactory
* @throws NoSuchMechanismException if no Provider
supports an
* XMLSignatureFactory
implementation for the DOM
diff --git a/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/keyinfo/KeyInfoFactory.java b/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/keyinfo/KeyInfoFactory.java
index 09f8a12e486bc..a2c76ffb32048 100644
--- a/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/keyinfo/KeyInfoFactory.java
+++ b/src/java.xml.crypto/share/classes/javax/xml/crypto/dsig/keyinfo/KeyInfoFactory.java
@@ -44,6 +44,7 @@
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.*;
+import sun.security.jca.ProvidersFilter;
/**
* A factory for creating {@link KeyInfo} objects from scratch or for
@@ -130,12 +131,21 @@ protected KeyInfoFactory() {}
* the {@link Security#getProviders() Security.getProviders()} method.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
- * {@link Security#getProperty(String) Security} property to determine
- * the preferred provider order for the specified algorithm. This
- * may be different than the order of providers returned by
- * {@link Security#getProviders() Security.getProviders()}.
+ * The JDK Reference Implementation additionally uses the following
+ * properties to customize the behavior of this method:
+ *
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified mechanism type.
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
*
* @param mechanismType the type of the XML processing mechanism and
* representation. See the {@code KeyInfoFactory} section in the
@@ -158,7 +168,7 @@ public static KeyInfoFactory getInstance(String mechanismType) {
Provider[] provs = Security.getProviders();
for (Provider p : provs) {
Service s = p.getService("KeyInfoFactory", mechanismType);
- if (s != null) {
+ if (s != null && ProvidersFilter.isAllowed(s)) {
Object obj = null;
try {
obj = s.newInstance(null);
@@ -184,6 +194,15 @@ public static KeyInfoFactory getInstance(String mechanismType) {
* Provider
object does not have to be registered in the
* provider list.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
* @param mechanismType the type of the XML processing mechanism and
* representation. See the {@code KeyInfoFactory} section in the
* Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses the
+ * {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties to determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
* @param mechanismType the type of the XML processing mechanism and
* representation. See the {@code KeyInfoFactory} section in the
*
+ * The {@code jdk.security.provider.preferred}
+ * {@link Security#getProperty(String) Security} property determines
+ * the preferred provider order for the specified mechanism type.
+ * This may be different from the order of providers returned by
+ * {@link Security#getProviders() Security.getProviders()}.
+ * The {@code jdk.security.providers.filter}
+ * {@link System#getProperty(String) System} and
+ * {@link Security#getProperty(String) Security} properties determine
+ * which {@linkplain java.security.Provider.Service services} are enabled.
+ * A service that is not enabled by the filter will not make its mechanism
+ * type implementation available.
+ *
*
* @return a new KeyInfoFactory
* @throws NoSuchMechanismException if no Provider
supports a
diff --git a/test/jdk/sun/security/provider/ProvidersFilterTest.java b/test/jdk/sun/security/provider/ProvidersFilterTest.java
new file mode 100644
index 0000000000000..f816f50761978
--- /dev/null
+++ b/test/jdk/sun/security/provider/ProvidersFilterTest.java
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (c) 2025, Red Hat, Inc.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+import java.io.*;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.*;
+import java.security.cert.*;
+import java.util.*;
+import javax.crypto.*;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.Configuration;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslServer;
+import javax.smartcardio.TerminalFactory;
+import javax.xml.crypto.dsig.Transform;
+import javax.xml.crypto.dsig.TransformService;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
+
+import sun.security.jca.GetInstance;
+import sun.security.util.KnownOIDs;
+
+import jdk.test.lib.process.Proc;
+import jdk.test.lib.util.FileUtils;
+
+/*
+ * @test
+ * @bug 8315487
+ * @summary
+ * Tests the sun.security.jca.ProvidersFilter.
+ * @modules java.base/sun.security.jca
+ * java.base/sun.security.util
+ * @library /test/lib
+ * @run main/othervm/timeout=600 -enablesystemassertions ProvidersFilterTest
+ */
+
+public final class ProvidersFilterTest {
+ private static final boolean DEBUG = false;
+
+ private static final String SEC_FILTER_PROP =
+ "jdk.security.providers.filter";
+
+ private static final String FILTER_EXCEPTION_HDR = " * Filter string: ";
+
+ private static final String FILTER_EXCEPTION_MORE = "(...)";
+
+ private static final int FILTER_EXCEPTION_MAX_LINE = 80;
+
+ private static Path workspace;
+
+ private static final String TEST_SERVICE_TYPE = "TestServiceType";
+
+ /*
+ * Class used as a service SPI for services added by security providers
+ * installed dynamically.
+ */
+ public static final class TestServiceSpi {
+ }
+
+ @FunctionalInterface
+ private interface ServiceChecker {
+ boolean check(ServiceData svcData);
+ }
+
+ @FunctionalInterface
+ private interface ServiceOp {
+ void doOp() throws Throwable;
+ }
+
+ private static boolean serviceCheck(ServiceOp serviceOp) {
+ try {
+ serviceOp.doOp();
+ return true;
+ } catch (Throwable t) {
+ if (DEBUG) {
+ t.printStackTrace();
+ }
+ return false;
+ }
+ }
+
+ private static final Map serviceCheckers =
+ new HashMap<>();
+
+ static {
+ serviceCheckers.put("AlgorithmParameterGenerator", (ServiceData d) ->
+ serviceCheck(() -> AlgorithmParameterGenerator
+ .getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("AlgorithmParameters",
+ (ServiceData d) -> serviceCheck(() -> AlgorithmParameters
+ .getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("CertificateFactory", (ServiceData d) ->
+ serviceCheck(() ->
+ CertificateFactory.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("CertPathBuilder", (ServiceData d) -> serviceCheck(
+ () -> CertPathBuilder.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("CertPathValidator", (ServiceData d) ->
+ serviceCheck(() ->
+ CertPathValidator.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("CertStore", (ServiceData d) -> serviceCheck(
+ () -> {
+ if (d.svcAlgo.equals("Collection")) {
+ CertStore.getInstance(d.svcAlgo,
+ new CollectionCertStoreParameters(),
+ d.provider);
+ } else {
+ try {
+ CertStore.getInstance(d.svcAlgo,
+ new LDAPCertStoreParameters(),
+ d.provider);
+ } catch (InvalidAlgorithmParameterException ignored) {
+ // The InitialDirContext could not be created as
+ // there is not a server in localhost but this is
+ // an indication that the service is available:
+ // NoSuchAlgorithmException would have been thrown
+ // otherwise.
+ }
+ }
+ }));
+ serviceCheckers.put("Cipher", (ServiceData d) -> serviceCheck(
+ () -> Cipher.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("Configuration", (ServiceData d) ->
+ serviceCheck(() -> Configuration
+ .getInstance(d.svcAlgo, null, d.provider)));
+ serviceCheckers.put("KEM", (ServiceData d) -> serviceCheck(
+ () -> KEM.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyAgreement", (ServiceData d) -> serviceCheck(
+ () -> KeyAgreement.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyFactory", (ServiceData d) -> serviceCheck(
+ () -> KeyFactory.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyGenerator", (ServiceData d) -> serviceCheck(
+ () -> KeyGenerator.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyInfoFactory", (ServiceData d) ->
+ serviceCheck(() -> KeyInfoFactory
+ .getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyManagerFactory", (ServiceData d) ->
+ serviceCheck(() ->
+ KeyManagerFactory.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyPairGenerator", (ServiceData d) -> serviceCheck(
+ () -> KeyPairGenerator.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("KeyStore", (ServiceData d) -> serviceCheck(
+ () -> KeyStore.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("Mac", (ServiceData d) -> serviceCheck(
+ () -> Mac.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("MessageDigest", (ServiceData d) -> serviceCheck(
+ () -> MessageDigest.getInstance(d.svcAlgo, d.provider)));
+ final CallbackHandler saslCallbackHandler = callbacks -> {
+ for (Callback cb : callbacks) {
+ if (cb instanceof PasswordCallback) {
+ ((PasswordCallback) cb).setPassword(
+ "password".toCharArray());
+ } else if (cb instanceof NameCallback) {
+ ((NameCallback) cb).setName("username");
+ }
+ }
+ };
+ serviceCheckers.put("SaslClientFactory", (ServiceData d) ->
+ serviceCheck(() -> {
+ SaslClient c = Sasl.createSaslClient(
+ new String[] { d.svcAlgo }, "username",
+ "ldap", "server1", Collections.emptyMap(),
+ saslCallbackHandler);
+ if (c == null) {
+ throw new NoSuchAlgorithmException();
+ }
+ }));
+ serviceCheckers.put("SaslServerFactory", (ServiceData d) ->
+ serviceCheck(() -> {
+ SaslServer s = Sasl.createSaslServer(
+ d.svcAlgo, "ldap", "server1",
+ Collections.emptyMap(), saslCallbackHandler);
+ if (s == null) {
+ throw new NoSuchAlgorithmException();
+ }
+ }));
+ serviceCheckers.put("SecretKeyFactory", (ServiceData d) -> serviceCheck(
+ () -> SecretKeyFactory.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("SecureRandom", (ServiceData d) -> serviceCheck(
+ () -> SecureRandom.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("Signature", (ServiceData d) -> serviceCheck(
+ () -> Signature.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("SSLContext", (ServiceData d) -> serviceCheck(
+ () -> SSLContext.getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("TerminalFactory", (ServiceData d) ->
+ serviceCheck(() -> TerminalFactory
+ .getInstance(d.svcAlgo, null, d.provider)));
+ serviceCheckers.put("TransformService", (ServiceData d) ->
+ serviceCheck(() -> TransformService
+ .getInstance(d.svcAlgo, "DOM", d.provider)));
+ serviceCheckers.put("TrustManagerFactory", (ServiceData d) ->
+ serviceCheck(() -> TrustManagerFactory
+ .getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put("XMLSignatureFactory", (ServiceData d) ->
+ serviceCheck(() -> XMLSignatureFactory
+ .getInstance(d.svcAlgo, d.provider)));
+ serviceCheckers.put(TEST_SERVICE_TYPE,
+ (ServiceData d) -> serviceCheck(() -> GetInstance.getInstance(
+ TEST_SERVICE_TYPE, TestServiceSpi.class, d.svcAlgo,
+ d.provider)));
+ }
+
+ private static sealed class ServiceData implements Serializable
+ permits DynamicServiceData {
+ @Serial
+ private static final long serialVersionUID = -351065619007499507L;
+ protected final String provider;
+ private final String svcType;
+ protected final String svcAlgo;
+
+ private ServiceData(String provider, String svcType, String svcAlgo) {
+ this.provider = provider;
+ this.svcType = svcType;
+ this.svcAlgo = svcAlgo;
+ }
+
+ @Override
+ public String toString() {
+ return provider + " / " + svcType + " / " + svcAlgo;
+ }
+ }
+
+ private static final class DynamicServiceData extends ServiceData {
+ @Serial
+ private static final long serialVersionUID = 6156428473910912042L;
+ final List aliases;
+ final Boolean legacy;
+
+ DynamicServiceData(String provider, String svcType,
+ String svcAlgo, List aliases, Boolean legacy) {
+ super(provider, svcType, svcAlgo);
+ if (aliases != null) {
+ this.aliases = aliases;
+ } else {
+ this.aliases = List.of();
+ }
+ this.legacy = legacy;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + (aliases != null ?
+ " / aliases: " + aliases : "") + " / legacy: " + (legacy ==
+ null ? "unregistered" : legacy);
+ }
+ }
+
+ private record ExpectedExceptionData(String exceptionClass,
+ String filterLine, String underliningLine) implements Serializable {
+ }
+
+ private static final class TestExecutor {
+ enum FilterPropertyType {
+ SYSTEM, SECURITY
+ }
+
+ @FunctionalInterface
+ private interface AssertionDataLoader {
+ void apply(TestExecutor testExecutor, String provider,
+ String svcType, String svcAlgo) throws Throwable;
+ }
+
+ private final List dynamicServices =
+ new ArrayList<>();
+ private final List expected = new ArrayList<>();
+ private final List notExpected = new ArrayList<>();
+ private ExpectedExceptionData expectedException = null;
+ private String filterStr;
+ private FilterPropertyType propertyType;
+
+ void setFilter(String filterStr) {
+ setFilter(filterStr, FilterPropertyType.SECURITY);
+ }
+
+ void setFilter(String filterStr, FilterPropertyType propertyType) {
+ if (propertyType == FilterPropertyType.SECURITY) {
+ StringBuilder sb = new StringBuilder(filterStr.length());
+ CharBuffer cb = CharBuffer.wrap(filterStr);
+ while (cb.hasRemaining()) {
+ char c = cb.get();
+ if (c == '\\') {
+ sb.append('\\');
+ }
+ if (Character.UnicodeBlock.of(c) ==
+ Character.UnicodeBlock.BASIC_LATIN) {
+ sb.append(c);
+ } else {
+ sb.append("\\u%04x".formatted((int) c));
+ }
+ }
+ this.filterStr = sb.toString();
+ } else {
+ this.filterStr = filterStr;
+ }
+ this.propertyType = propertyType;
+ if (DEBUG) {
+ System.out.println("Filter: " + filterStr);
+ }
+ }
+
+ private void addDynamicService(String provider, String svcAlgo,
+ List aliases, Boolean legacy,
+ AssertionDataLoader assertionDataLoader) throws Throwable {
+ DynamicServiceData svcData = new DynamicServiceData(provider,
+ TEST_SERVICE_TYPE, svcAlgo, aliases, legacy);
+ dynamicServices.add(svcData);
+ // Sanity check: install the dynamic security provider without a
+ // filter.
+ DynamicProvider dynamicProvider = DynamicProvider.install(svcData);
+ dynamicProvider.putAlgo(svcData);
+ assertionDataLoader.apply(this, provider, TEST_SERVICE_TYPE,
+ svcAlgo);
+ }
+
+ void addExpectedDynamicService(String provider, String svcAlgo)
+ throws Throwable {
+ addExpectedDynamicService(provider, svcAlgo, null, false);
+ }
+
+ void addExpectedDynamicService(String provider, String svcAlgo,
+ List aliases, Boolean legacy) throws Throwable {
+ addDynamicService(provider, svcAlgo, aliases, legacy,
+ TestExecutor::addExpectedService);
+ }
+
+ void addExpectedService(String provider, String svcType,
+ String svcAlgo) throws Throwable {
+ expected.add(checkSvcAvailable(new ServiceData(provider,
+ svcType, svcAlgo)));
+ }
+
+ void addNotExpectedDynamicService(String provider, String svcAlgo)
+ throws Throwable {
+ addNotExpectedDynamicService(provider, svcAlgo, null, false);
+ }
+
+ void addNotExpectedDynamicService(String provider, String svcAlgo,
+ List aliases, Boolean legacy) throws Throwable {
+ addDynamicService(provider, svcAlgo, aliases, legacy,
+ TestExecutor::addNotExpectedService);
+ }
+
+ void addNotExpectedService(String provider, String svcType,
+ String svcAlgo) throws Throwable {
+ notExpected.add(checkSvcAvailable(new ServiceData(provider,
+ svcType, svcAlgo)));
+ }
+
+ /*
+ * Sanity check: services must be available without a filter.
+ */
+ private ServiceData checkSvcAvailable(ServiceData svcData)
+ throws Throwable {
+ if (!serviceCheckers.get(svcData.svcType).check(svcData)) {
+ throw new Exception("The service " + svcData + " is not" +
+ " available without a filter.");
+ }
+ return svcData;
+ }
+
+ void addExpectedFilterException(String filterLine,
+ int underliningSpaces) {
+ String underliningLine = " ".repeat(underliningSpaces) +
+ "---^---";
+ underliningLine = underliningLine.substring(0, Math.min(
+ underliningLine.length(), FILTER_EXCEPTION_MAX_LINE));
+ expectedException = new ExpectedExceptionData("sun.security.jca" +
+ ".ProvidersFilter$Filter$ParserException",
+ FILTER_EXCEPTION_HDR + filterLine, underliningLine);
+ }
+
+ void execute() throws Throwable {
+ String testClassName = getClass().getEnclosingClass().getName();
+ Path dynamicServicesPath = getSvcDataFile(dynamicServices,
+ "Dynamically installed services");
+ Path expectedPath = getSvcDataFile(expected, "Expected");
+ Path notExpectedPath = getSvcDataFile(notExpected, "Not expected");
+ Path expectedExceptionPath = serializeObject(expectedException);
+ if (DEBUG) {
+ System.out.println("=========================================");
+ }
+ Proc p = Proc.create(testClassName).args(
+ dynamicServicesPath.toString(), expectedPath.toString(),
+ notExpectedPath.toString(), (expectedExceptionPath == null ?
+ "" : expectedExceptionPath.toString()));
+ p.env("JDK_JAVA_OPTIONS", "-enablesystemassertions");
+ if (propertyType == FilterPropertyType.SECURITY) {
+ p.secprop(SEC_FILTER_PROP, filterStr);
+ } else {
+ p.prop(SEC_FILTER_PROP, filterStr);
+ }
+ if (DEBUG) {
+ p.inheritIO();
+ p.prop("java.security.debug", "jca");
+ p.debug(testClassName);
+
+ // Need the launched process to connect to a debugger?
+ //System.setProperty("test.vm.opts", "-Xrunjdwp:transport=" +
+ // "dt_socket,address=localhost:8000,suspend=y");
+ } else {
+ p.nodump();
+ }
+ p.start().waitFor(0);
+ for (ServiceData svcData : dynamicServices) {
+ Security.removeProvider(svcData.provider);
+ }
+ }
+ }
+
+ private static Path getSvcDataFile(Object svcData, String title)
+ throws Throwable {
+ assert svcData != null : "Service data cannot be null.";
+ Path svcDataFilePath = serializeObject(svcData);
+ showFileContent(svcDataFilePath, title);
+ return svcDataFilePath;
+ }
+
+ private static List getSvcData(Path svcDataPath)
+ throws Throwable {
+ return (List) deserializeObject(svcDataPath);
+ }
+
+ private static Path serializeObject(Object obj) throws Throwable {
+ if (obj == null) {
+ return null;
+ }
+ Path objFilePath = Files.createTempFile(workspace, null, null);
+ try (FileOutputStream fos =
+ new FileOutputStream(objFilePath.toFile())) {
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(obj);
+ oos.flush();
+ }
+ return objFilePath;
+ }
+
+ private static Object deserializeObject(Path filePath)
+ throws Throwable {
+ try (FileInputStream fos = new FileInputStream(filePath.toFile())) {
+ ObjectInputStream ois = new ObjectInputStream(fos);
+ return ois.readObject();
+ }
+ }
+
+ private static void showFileContent(Path filePath, String title)
+ throws Throwable {
+ if (DEBUG) {
+ System.out.println("-----------------------------------------");
+ System.out.println(title + " assertion data (" + filePath + "):");
+ for (ServiceData svcData : getSvcData(filePath)) {
+ System.out.println(svcData);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ if (args.length == 4) {
+ // Executed by a child process.
+ mainChild(args[0], args[1], args[2], args[3]);
+ } else if (args.length == 0) {
+ // Executed by the parent process.
+ try {
+ workspace = Files.createTempDirectory(null);
+ mainLauncher();
+ } finally {
+ FileUtils.deleteFileTreeWithRetry(workspace);
+ }
+ System.out.println("TEST PASS - OK");
+ } else {
+ throw new Exception("Unexpected number of arguments.");
+ }
+ }
+
+ private interface SvcDataConsumer {
+ void consume(ServiceData data, boolean available) throws Throwable;
+ }
+
+ private static void mainChild(String dynamicServicesPath,
+ String expectedPropsPath, String notExpectedPropsPath,
+ String expectedExceptionPath) throws Throwable {
+ if (!expectedExceptionPath.isEmpty()) {
+ ExpectedExceptionData expectedException = (ExpectedExceptionData)
+ deserializeObject(Paths.get(expectedExceptionPath));
+ try {
+ // Force the filter to be loaded.
+ Security.getProviders();
+ } catch (Throwable t) {
+ if (DEBUG) {
+ System.out.println("Filter line expected: " +
+ expectedException.filterLine);
+ System.out.println("Filter underlining line expected: " +
+ expectedException.underliningLine);
+ t.printStackTrace();
+ }
+ Throwable ultimateCause = t.getCause();
+ while (ultimateCause.getCause() != null) {
+ ultimateCause = ultimateCause.getCause();
+ }
+ if (ultimateCause.getClass().getName()
+ .equals(expectedException.exceptionClass)) {
+ String[] lines = ultimateCause.getMessage().split("\\R");
+ for (int i = 0; i < lines.length; i++) {
+ if (lines[i].startsWith(FILTER_EXCEPTION_HDR)) {
+ if (lines[i].equals(expectedException.filterLine) &&
+ i < lines.length - 1 && lines[i + 1].equals(
+ expectedException.underliningLine)) {
+ return;
+ }
+ break;
+ }
+ }
+ }
+ }
+ throw new Exception("Expected filter exception could not be " +
+ "verified.");
+ }
+ installDynamicServices(dynamicServicesPath);
+ if (DEBUG) {
+ System.out.println("Security Providers installed:");
+ for (Provider provider : Security.getProviders()) {
+ System.out.println("Provider: " + provider);
+ }
+ }
+ perSvcDataDo(expectedPropsPath,
+ (ServiceData data, boolean available) -> {
+ if (!available) {
+ throw new Exception("The service '" + data + "' is not " +
+ "available when it was expected.");
+ }
+ });
+ perSvcDataDo(notExpectedPropsPath,
+ (ServiceData data, boolean available) -> {
+ if (available) {
+ throw new Exception("The service '" + data + "' is " +
+ "available when it was not expected.");
+ }
+ });
+ }
+
+ private static abstract sealed class DynamicProvider extends Provider
+ permits DynamicProviderCurrent, DynamicProviderLegacy,
+ DynamicProviderUnregistered {
+ @Serial
+ private static final long serialVersionUID = 6088341396620902983L;
+
+ static DynamicProvider install(DynamicServiceData svcData)
+ throws Throwable {
+ DynamicProvider dynamicProvider;
+ if (Security.getProvider(svcData.provider)
+ instanceof DynamicProvider dP) {
+ dynamicProvider = dP;
+ } else {
+ if (svcData.legacy == null) {
+ dynamicProvider = new DynamicProviderUnregistered(svcData);
+ } else if (svcData.legacy) {
+ dynamicProvider = new DynamicProviderLegacy(svcData);
+ } else {
+ dynamicProvider = new DynamicProviderCurrent(svcData);
+ }
+ if (Security.addProvider(dynamicProvider) == -1) {
+ throw new Exception("Could not install dynamic provider.");
+ }
+ }
+ return dynamicProvider;
+ }
+
+ DynamicProvider(ServiceData svcData) {
+ super(svcData.provider, "", svcData.toString());
+ }
+ abstract void putAlgo(DynamicServiceData svcData);
+ }
+
+ private static final class DynamicProviderCurrent extends DynamicProvider {
+ @Serial
+ private static final long serialVersionUID = 7754296009615868997L;
+
+ DynamicProviderCurrent(DynamicServiceData svcData) {
+ super(svcData);
+ }
+
+ @Override
+ void putAlgo(DynamicServiceData svcData) {
+ putService(new Service(this, TEST_SERVICE_TYPE, svcData.svcAlgo,
+ TestServiceSpi.class.getName(), svcData.aliases, null));
+ }
+ }
+
+ private static final class DynamicProviderLegacy extends DynamicProvider {
+ @Serial
+ private static final long serialVersionUID = 1859892951118353404L;
+
+ DynamicProviderLegacy(DynamicServiceData svcData) {
+ super(svcData);
+ }
+
+ @Override
+ void putAlgo(DynamicServiceData svcData) {
+ put(TEST_SERVICE_TYPE + "." + svcData.svcAlgo,
+ TestServiceSpi.class.getName());
+ for (String alias : svcData.aliases) {
+ put("Alg.Alias." + TEST_SERVICE_TYPE + "." + alias,
+ svcData.svcAlgo);
+ }
+ }
+ }
+
+ private static final class DynamicProviderUnregistered
+ extends DynamicProvider {
+ @Serial
+ private static final long serialVersionUID = 4421847184357342760L;
+ private final Map services = new HashMap<>();
+
+ DynamicProviderUnregistered(DynamicServiceData svcData) {
+ super(svcData);
+ }
+
+ @Override
+ void putAlgo(DynamicServiceData svcData) {
+ Provider.Service s = new Service(this, TEST_SERVICE_TYPE,
+ svcData.svcAlgo, TestServiceSpi.class.getName(),
+ svcData.aliases, null);
+ services.put(s.getType() + "." + s.getAlgorithm(), s);
+ for (String alias : svcData.aliases) {
+ services.put(s.getType() + "." + alias, s);
+ }
+ }
+
+ @Override
+ public Provider.Service getService(String type, String algorithm) {
+ return services.get(type + "." + algorithm);
+ }
+
+ @Override
+ public Set getServices() {
+ return new HashSet<>(services.values());
+ }
+ }
+
+ private static void installDynamicServices(String svcDataPath)
+ throws Throwable {
+ for (ServiceData svcDataObj : getSvcData(Paths.get(svcDataPath))) {
+ DynamicServiceData svcData = (DynamicServiceData)svcDataObj;
+ DynamicProvider dynamicProvider = DynamicProvider.install(svcData);
+ dynamicProvider.putAlgo(svcData);
+ }
+ }
+
+ private static Provider getProviderByName(String providerName) {
+ Provider[] providers = Security.getProviders();
+ for (Provider p : providers) {
+ if (p.getName().equals(providerName)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ private static void perSvcDataDo(String svcDataPath,
+ SvcDataConsumer svcDataDo) throws Throwable {
+ for (ServiceData svcData : getSvcData(Paths.get(svcDataPath))) {
+ Provider p = getProviderByName(svcData.provider);
+ ServiceChecker checker = serviceCheckers.get(svcData.svcType);
+ boolean availableInCryptoCheckers = checker.check(svcData);
+ List allAlgos = new ArrayList<>(List.of(svcData.svcAlgo));
+ if (svcData instanceof DynamicServiceData dynamicSvcData) {
+ allAlgos.addAll(dynamicSvcData.aliases);
+ }
+ for (String algo : allAlgos) {
+ String filter = svcData.svcType + "." + algo;
+ if (availableInCryptoCheckers &&
+ svcData.svcType.equalsIgnoreCase("Cipher")) {
+ Provider.Service svc = p.getService(svcData.svcType, algo);
+ if (svc == null) {
+ // The Security::getProviders API does not support
+ // transformations except when the service is explicitly
+ // registered for it.
+ continue;
+ }
+ }
+ if (filter.indexOf(':') != -1) {
+ // Character not supported for algorithms in
+ // Security::getProviders.
+ continue;
+ }
+ boolean availableInFiltered = findSvcInFilteredProviders(
+ svcData.provider, filter);
+ if (availableInCryptoCheckers != availableInFiltered) {
+ throw new Exception("Inconsistent Security.getProviders(" +
+ "\"" + filter + "\") filtering result.");
+ }
+ }
+ svcDataDo.consume(svcData, availableInCryptoCheckers);
+ }
+ }
+
+ private static boolean findSvcInFilteredProviders(String provider,
+ String filter) {
+ Provider[] filteredProviders = Security.getProviders(filter);
+ if (filteredProviders != null) {
+ for (Provider p : filteredProviders) {
+ if (p.getName().equals(provider)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static void mainLauncher() throws Throwable {
+ for (Method m : ProvidersFilterTest.class.getDeclaredMethods()) {
+ if (m.getName().startsWith("test")) {
+ printTestHeader(m.getName());
+ TestExecutor t = new TestExecutor();
+ m.invoke(null, t);
+ t.execute();
+ }
+ }
+ }
+
+ private static void printTestHeader(String testName) {
+ if (DEBUG) {
+ System.out.println("=========================================");
+ System.out.println(testName);
+ System.out.println("-----------------------------------------");
+ }
+ }
+
+ /*
+ * Valid filters
+ */
+
+ private static void testBasicFiltering(TestExecutor t) throws Throwable {
+ t.setFilter(" SunJCE.Mac.HmacSHA512; SUN.MessageDigest.SHA-512 ;" +
+ " ! *.*.*WeaK*;MyProvider.*.myStrongAlgorithm*; " +
+ "!NonExistentProvider ");
+ t.addExpectedService("SunJCE", "Mac", "HmacSHA512");
+ t.addExpectedDynamicService("MyProvider", "MyStrongAlgorithm");
+ t.addExpectedDynamicService("MyProvider", "MyStrongAlgorithm2");
+ t.addNotExpectedService("SunJCE", "KeyGenerator", "HmacSHA3-512");
+ t.addNotExpectedDynamicService("MyProvider", "MyWeakAlgorithm");
+ }
+
+ private static void testBasicFilteringUnregistered(TestExecutor t)
+ throws Throwable {
+ t.setFilter("R1_MyProvider.*.strong; !R1_MyProvider;" +
+ "!R2_MyProvider.*.weak; R2_MyProvider");
+ t.addExpectedDynamicService("R1_MyProvider", "strong", List.of(), null);
+ t.addExpectedDynamicService("R2_MyProvider", "Algo", List.of(), null);
+ t.addNotExpectedDynamicService("R1_MyProvider", "Algo", List.of(),
+ null);
+ t.addNotExpectedDynamicService("R2_MyProvider", "weak", List.of(),
+ null);
+ }
+
+ private static void testCipherFiltering(TestExecutor t) throws Throwable {
+ t.setFilter("!*.Cipher.AES; *.Cipher.AES/CBC/PKCS5Padding; " +
+ "*.Cipher." + KnownOIDs.AES.value().replace(".", "\\.") +
+ "/OFB/NoPadding; *.Cipher.AES_128/CBC/*; " +
+ "*.Cipher.PBEWithHmacSHA512/256AndAES_128/CBC/PKCS5Padding;");
+ t.addExpectedService("SunJCE", "Cipher", "AES/CBC/PKCS5Padding");
+ t.addExpectedService("SunJCE", "Cipher", "AES/OFB/NoPadding");
+ t.addExpectedService("SunJCE", "Cipher", "AES_128/CBC/NoPadding");
+ t.addExpectedService("SunJCE", "Cipher",
+ KnownOIDs.AES.value() + "/CBC/PKCS5Padding");
+ t.addExpectedService("SunJCE", "Cipher",
+ KnownOIDs.AES.value() + "/OFB/NoPadding");
+ t.addExpectedService("SunJCE", "Cipher",
+ KnownOIDs.AES_128$CBC$NoPadding.value());
+ t.addExpectedService("SunJCE", "Cipher",
+ "PBEWithHmacSHA512/256AndAES_128/CBC/PKCS5Padding");
+ t.addNotExpectedService("SunJCE", "Cipher", "AES");
+ t.addNotExpectedService("SunJCE", "Cipher", "AES//");
+ t.addNotExpectedService("SunJCE", "Cipher", KnownOIDs.AES.value() +
+ "//");
+ t.addNotExpectedService("SunJCE", "Cipher", KnownOIDs.AES.value());
+ t.addNotExpectedService("SunJCE", "Cipher", "AES/CBC/NoPadding");
+ t.addNotExpectedService("SunJCE", "Cipher",
+ "PBEWithHmacSHA512/256AndAES_128");
+ }
+
+ private static void testAllServiceTypesFiltering(TestExecutor t)
+ throws Throwable {
+ t.setFilter("*.AlgorithmParameterGenerator.DiffieHellman; " +
+ "*.AlgorithmParameters.PBES2;" +
+ "*.CertStore.Collection; " +
+ "*.KeyAgreement.ECDH; " +
+ "*.KeyFactory.DiffieHellman; " +
+ "*.KeyGenerator.HmacSHA3-512; " +
+ "*.KeyManagerFactory.NewSunX509; " +
+ "*.KeyPairGenerator.DiffieHellman; " +
+ "*.KeyStore.PKCS12; " +
+ "*.Mac.HmacSHA512; " +
+ "*.MessageDigest.SHA-512; " +
+ "*.SaslClientFactory.EXTERNAL; " +
+ "*.SaslServerFactory.CRAM-MD5; " +
+ "*.SecretKeyFactory.PBEWithHmacSHA512/256AndAES_256; " +
+ "*.SecureRandom.SHA1PRNG; *.MessageDigest.SHA-1; " +
+ "*.Signature.EdDSA; " +
+ "*.SSLContext.TLSv1\\.3; " +
+ "*.TransformService." +
+ Transform.XPATH.replace(".", "\\.").replace(":", "\\:") + "; " +
+ "*.TrustManagerFactory.PKIX");
+
+ // Expected services
+ t.addExpectedService("SunJCE", "AlgorithmParameterGenerator",
+ "DiffieHellman");
+ t.addExpectedService("SunJCE", "AlgorithmParameters", "PBES2");
+ t.addExpectedService("SUN", "CertStore", "Collection");
+ t.addExpectedService("SunEC", "KeyAgreement", "ECDH");
+ t.addExpectedService("SunJCE", "KeyFactory", "DiffieHellman");
+ t.addExpectedService("SunJCE", "KeyGenerator", "HmacSHA3-512");
+ t.addExpectedService("SunJSSE", "KeyManagerFactory", "NewSunX509");
+ t.addExpectedService("SunJCE", "KeyPairGenerator", "DiffieHellman");
+ t.addExpectedService("SunJSSE", "KeyStore", "PKCS12");
+ t.addExpectedService("SunJCE", "Mac", "HmacSHA512");
+ t.addExpectedService("SUN", "MessageDigest", "SHA-512");
+ t.addExpectedService("SunSASL", "SaslClientFactory", "EXTERNAL");
+ t.addExpectedService("SunSASL", "SaslServerFactory", "CRAM-MD5");
+ t.addExpectedService("SunJCE", "SecretKeyFactory",
+ "PBEWithHmacSHA512/256AndAES_256");
+ t.addExpectedService("SUN", "SecureRandom", "SHA1PRNG");
+ t.addExpectedService("SunEC", "Signature", "EdDSA");
+ t.addExpectedService("SunJSSE", "SSLContext", "TLSv1.3");
+ t.addExpectedService("XMLDSig", "TransformService",
+ Transform.XPATH);
+ t.addExpectedService("SunJSSE", "TrustManagerFactory", "PKIX");
+
+ // Not expected services
+ t.addNotExpectedService("SUN", "AlgorithmParameterGenerator", "DSA");
+ t.addNotExpectedService("SUN", "AlgorithmParameters", "DSA");
+ t.addNotExpectedService("SUN", "CertificateFactory", "X.509");
+ t.addNotExpectedService("SUN", "CertPathBuilder", "PKIX");
+ t.addNotExpectedService("SUN", "CertPathValidator", "PKIX");
+ t.addNotExpectedService("JdkLDAP", "CertStore", "LDAP");
+ t.addNotExpectedService("SUN", "Configuration", "JavaLoginConfig");
+ t.addNotExpectedService("SunJCE", "KEM", "DHKEM");
+ t.addNotExpectedService("SunEC", "KeyAgreement", "X25519");
+ t.addNotExpectedService("SUN", "KeyFactory", "DSA");
+ t.addNotExpectedService("SunJCE", "KeyGenerator", "Blowfish");
+ t.addNotExpectedService("XMLDSig", "KeyInfoFactory", "DOM");
+ t.addNotExpectedService("SunJSSE", "KeyManagerFactory", "SunX509");
+ t.addNotExpectedService("SUN", "KeyPairGenerator", "DSA");
+ t.addNotExpectedService("SUN", "KeyStore", "JKS");
+ t.addNotExpectedService("SunJCE", "Mac", "HmacSHA1");
+ t.addNotExpectedService("SUN", "MessageDigest", "MD5");
+ t.addNotExpectedService("SunSASL", "SaslClientFactory", "PLAIN");
+ t.addNotExpectedService("SunSASL", "SaslServerFactory", "DIGEST-MD5");
+ t.addNotExpectedService("SunJCE", "SecretKeyFactory", "DES");
+ t.addNotExpectedService("SUN", "SecureRandom", "DRBG");
+ t.addNotExpectedService("SUN", "Signature", "SHA1withDSA");
+ t.addNotExpectedService("SunJSSE", "SSLContext", "TLSv1.2");
+ t.addNotExpectedService("SunPCSC", "TerminalFactory", "PC/SC");
+ t.addNotExpectedService("XMLDSig", "TransformService",
+ Transform.ENVELOPED);
+ t.addNotExpectedService("SunJSSE", "TrustManagerFactory", "SunX509");
+ t.addNotExpectedService("XMLDSig", "XMLSignatureFactory", "DOM");
+ }
+
+ private static void testCharsEscaping(TestExecutor t) throws Throwable {
+ t.setFilter("R1_\\M\\!\\ \\.Pr\\*\\\\/\\;der \t; " +
+ "R2_My\\\\E\\.\\\\QProvider;" +
+ "\\!R3_M\\:Pr\\\tvi\\,de\u2014r.*;");
+ t.addExpectedDynamicService("R1_M! .Pr*\\/;der", "Algo");
+ t.addExpectedDynamicService("R2_My\\E.\\QProvider", "Algo");
+ t.addExpectedDynamicService("!R3_M:Pr\tvi,de\u2014r", "Algo");
+ t.addNotExpectedDynamicService("R1_\\M! .Pr*\\/;der", "Algo");
+ t.addNotExpectedDynamicService("R1_M! .Pro\\/;der", "Algo");
+ t.addNotExpectedDynamicService("R1_M! .Pr*/;der", "Algo");
+ t.addNotExpectedDynamicService("R1_M! .Pr*\\/", "Algo");
+ t.addNotExpectedDynamicService("R1_M! .Pr*\\/\\", "Algo");
+ t.addNotExpectedDynamicService("R2_MyXProvider", "Algo");
+ }
+
+ private static void testWildcardGreediness(TestExecutor t)
+ throws Throwable {
+ t.setFilter("R1_MyProvider*; R2_MyProviderA**B**C; " +
+ "R3_MyProvider*ABC");
+ t.addExpectedDynamicService("R1_MyProvider", "Algo");
+ t.addExpectedDynamicService("R1_MyProviderX", "Algo");
+ t.addExpectedDynamicService("R1_MyProviderXX", "Algo");
+ t.addExpectedDynamicService("R2_MyProviderABC", "Algo");
+ t.addExpectedDynamicService("R2_MyProviderABCDC", "Algo");
+ t.addExpectedDynamicService("R2_MyProviderABCCCC", "Algo");
+ t.addExpectedDynamicService("R3_MyProviderABC", "Algo");
+ t.addExpectedDynamicService("R3_MyProviderABCABC", "Algo");
+ t.addNotExpectedDynamicService("R2_MyProviderA", "Algo");
+ }
+
+ private static void testLeftPrecedence(TestExecutor t) throws Throwable {
+ t.setFilter("R1_MyProvider; !R1_MyProvider; !R2_MyProvider; " +
+ "R2_MyProvider; !R3_*; R3_MyProvider; !R4_*.*.AES; " +
+ "R4_*.*.RSA");
+ t.addExpectedDynamicService("R1_MyProvider", "Algo");
+ t.addExpectedDynamicService("R4_MyProvider", "RSA");
+ t.addNotExpectedDynamicService("R2_MyProvider", "Algo");
+ t.addNotExpectedDynamicService("R3_MyProvider", "Algo");
+ t.addNotExpectedDynamicService("R4_MyProvider", "AES");
+ t.addNotExpectedDynamicService("R4_MyProvider", "*");
+ }
+
+ private static void aliasesCommon(TestExecutor t, Boolean legacy)
+ throws Throwable {
+ t.setFilter("R1_MyProvider.*.Alias; !R1_MyProvider.*.Algo; " +
+ "!R2_MyProvider.*.Alias; R2_MyProvider.*.Algo;" +
+ "R3_MyProvider.*.Algo; !R3_MyProvider.*.Alias;" +
+ "!R4_MyProvider.*.Algo; R4_MyProvider.*.Alias;" +
+ "R5_MyProvider.*.ALIAS1; !R5_MyProvider.*.ALIAS2");
+ t.addExpectedDynamicService("R1_MyProvider", "Algo", List.of("Alias"),
+ legacy);
+ t.addExpectedDynamicService("R3_MyProvider", "Algo", List.of("Alias"),
+ legacy);
+ t.addExpectedDynamicService("R5_MyProvider", "Algo", List.of("Alias1",
+ "Alias2"), legacy);
+ t.addNotExpectedDynamicService("R2_MyProvider", "Algo",
+ List.of("Alias"), legacy);
+ t.addNotExpectedDynamicService("R4_MyProvider", "Algo",
+ List.of("Alias"), legacy);
+ }
+
+ private static void testAliases(TestExecutor t) throws Throwable {
+ aliasesCommon(t, false);
+ }
+
+ private static void testAliasesLegacy(TestExecutor t) throws Throwable {
+ aliasesCommon(t, true);
+ }
+
+ private static void testAliasesUnregistered(TestExecutor t)
+ throws Throwable {
+ aliasesCommon(t, null);
+ }
+
+ /*
+ * Invalid filters (must throw an exception)
+ */
+
+ private static void testWhitespacesOnlyInFilter(TestExecutor t)
+ throws Throwable {
+ t.setFilter("\t\t\t", TestExecutor.FilterPropertyType.SYSTEM);
+ t.addExpectedFilterException("\t\t\t", 17);
+ }
+
+ private static void testWhitespacesOnlyInRule(TestExecutor t) {
+ t.setFilter("*; ;");
+ t.addExpectedFilterException("*; ;", 21);
+ }
+
+ private static void testDenyOnly(TestExecutor t) {
+ t.setFilter("!");
+ t.addExpectedFilterException("!", 15);
+ }
+
+ private static void testTooManyLevels(TestExecutor t) {
+ t.setFilter("*.*.*.*");
+ t.addExpectedFilterException("*.*.*.*", 20);
+ }
+
+ private static void testMissingSecurityProvider(TestExecutor t) {
+ t.setFilter(".*.*");
+ t.addExpectedFilterException(".*.*", 15);
+ }
+
+ private static void testDenyMissingSecurityProvider(TestExecutor t) {
+ t.setFilter("!.*");
+ t.addExpectedFilterException("!.*", 16);
+ }
+
+ private static void testMissingServiceType(TestExecutor t) {
+ t.setFilter("*.");
+ t.addExpectedFilterException("*.", 16);
+ }
+
+ private static void testMissingServiceType2(TestExecutor t) {
+ t.setFilter("*..*");
+ t.addExpectedFilterException("*..*", 17);
+ }
+
+ private static void testMissingAlgorithm(TestExecutor t) {
+ t.setFilter("*.*.");
+ t.addExpectedFilterException("*.*.", 18);
+ }
+
+ private static void testUnescapedSpaceInProvider(TestExecutor t) {
+ t.setFilter("My Provider");
+ t.addExpectedFilterException("My Provider", 18);
+ }
+
+ private static void testUnescapedSpaceInServiceType(TestExecutor t) {
+ t.setFilter("MyProvider. MyService");
+ t.addExpectedFilterException("MyProvider. MyService", 26);
+ }
+
+ private static void testUnescapedExclamationMark(TestExecutor t) {
+ t.setFilter("My!Provider");
+ t.addExpectedFilterException("My!Provider", 17);
+ }
+
+ private static void testUnescapedColonInProvider(TestExecutor t) {
+ t.setFilter("My:Provider");
+ t.addExpectedFilterException("My:Provider", 17);
+ }
+
+ private static void testUnescapedCommaInProvider(TestExecutor t) {
+ t.setFilter("My,Provider");
+ t.addExpectedFilterException("My,Provider", 17);
+ }
+
+ private static void testFilterEndsInEscape(TestExecutor t) {
+ t.setFilter("\\");
+ t.addExpectedFilterException("\\", 15);
+ }
+
+ private static void testProviderEndsInEscape(TestExecutor t) {
+ t.setFilter("MyProvider\\");
+ t.addExpectedFilterException("MyProvider\\", 25);
+ }
+
+ private static void testParserExceptionLineMoreRight(TestExecutor t) {
+ t.setFilter("." + ";".repeat(FILTER_EXCEPTION_MAX_LINE + 10));
+ t.addExpectedFilterException("." + ";".repeat(
+ FILTER_EXCEPTION_MAX_LINE - FILTER_EXCEPTION_HDR.length() - 1
+ - FILTER_EXCEPTION_MORE.length() - 1) + " " +
+ FILTER_EXCEPTION_MORE, 15);
+ }
+
+ private static void testParserExceptionLineMoreLeft(TestExecutor t) {
+ t.setFilter("*".repeat(FILTER_EXCEPTION_MAX_LINE + 10) + "!");
+ t.addExpectedFilterException(FILTER_EXCEPTION_MORE + " " + "*".repeat(
+ FILTER_EXCEPTION_MAX_LINE - FILTER_EXCEPTION_HDR.length() - 1
+ - FILTER_EXCEPTION_MORE.length() - 1) + "!", 76);
+ }
+
+ private static void testParserExceptionLineMoreBoth(TestExecutor t) {
+ t.setFilter("*".repeat(FILTER_EXCEPTION_MAX_LINE + 10) + "!" +
+ "*".repeat(FILTER_EXCEPTION_MAX_LINE + 10));
+ float halfWildcards = (FILTER_EXCEPTION_MAX_LINE -
+ FILTER_EXCEPTION_HDR.length() - (FILTER_EXCEPTION_MORE.length()
+ + 1) * 2 - 1) / 2.0f;
+ int preWildcards = (int) halfWildcards;
+ int postWildcards = (int) (halfWildcards + 0.5f);
+ t.addExpectedFilterException(FILTER_EXCEPTION_MORE + " " + "*".repeat(
+ preWildcards) + "!" + "*".repeat(postWildcards) + " " +
+ FILTER_EXCEPTION_MORE, 45);
+ }
+}
diff --git a/test/jdk/tools/launcher/Settings.java b/test/jdk/tools/launcher/Settings.java
index fe5a9c3b15742..50f273205e977 100644
--- a/test/jdk/tools/launcher/Settings.java
+++ b/test/jdk/tools/launcher/Settings.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025, 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
@@ -22,10 +22,11 @@
*/
import java.io.File;
import java.io.IOException;
+import java.util.Iterator;
/*
* @test
- * @bug 6994753 7123582 8305950 8281658 8310201 8311653 8343804
+ * @bug 6994753 7123582 8305950 8281658 8310201 8311653 8343804 8315487
* @summary tests -XshowSettings options
* @modules jdk.compiler
* jdk.zipfs
@@ -64,6 +65,28 @@ static void checkNotContains(TestResult tr, String str) {
}
}
+ static void checkServicesAllowed(TestResult tr, boolean servicesAllowed) {
+ String noneSvcHdr = "Provider services " + (servicesAllowed ?
+ "NOT " : "") + "allowed: (type : algorithm)";
+ String errorMsg = "Expected header '" + noneSvcHdr + "' not found";
+ Iterator oi = tr.testOutput.iterator();
+ while (oi.hasNext()) {
+ if (oi.next().contains(noneSvcHdr)) {
+ if (oi.next().contains("")) {
+ errorMsg = null;
+ } else {
+ errorMsg = "Unexpected services listed under '" +
+ noneSvcHdr + "'";
+ break;
+ }
+ }
+ }
+ if (errorMsg != null) {
+ System.out.println(tr);
+ throw new RuntimeException(errorMsg);
+ }
+ }
+
private static final String VM_SETTINGS = "VM settings:";
private static final String PROP_SETTINGS = "Property settings:";
private static final String LOCALE_SETTINGS = "Locale settings:";
@@ -215,8 +238,11 @@ static void runTestOptionSecurityProps() throws IOException {
checkContains(tr, "keystore.type=pkcs12");
}
- static void runTestOptionSecurityProv() throws IOException {
- TestResult tr = doExec(javaCmd, "-XshowSettings:security:providers");
+ static void runTestOptionSecurityProv(boolean servicesAllowed)
+ throws IOException {
+ TestResult tr = doExec(javaCmd, "-XshowSettings:security:providers",
+ "-Djdk.security.providers.filter=" + (servicesAllowed ? "" :
+ "!*"));
checkNotContains(tr, SEC_PROPS_SETTINGS);
checkContains(tr, SEC_PROVIDER_SETTINGS);
checkNotContains(tr, SEC_TLS_SETTINGS);
@@ -225,6 +251,13 @@ static void runTestOptionSecurityProv() throws IOException {
// test for a well known alias (SunJCE: AlgorithmParameterGenerator.DiffieHellman)
checkContains(tr, "aliases: [1.2.840.113549.1.3.1, " +
"DH, OID.1.2.840.113549.1.3.1]");
+ // test services filter information
+ checkServicesAllowed(tr, servicesAllowed);
+ }
+
+ static void runTestOptionSecurityProv() throws IOException {
+ runTestOptionSecurityProv(true);
+ runTestOptionSecurityProv(false);
}
static void runTestOptionSecurityTLS() throws IOException {