diff --git a/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java b/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java index 0410179550b2d..8c646383cf630 100644 --- a/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java +++ b/src/java.base/share/classes/java/security/AlgorithmParameterGenerator.java @@ -148,12 +148,21 @@ public final String getAlgorithm() { * 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 from 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: + * * * @param algorithm the name of the algorithm this * parameter generator is associated with. @@ -201,6 +210,15 @@ public static AlgorithmParameterGenerator getInstance(String algorithm) *

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 name of the algorithm this * parameter generator is associated with. * See the AlgorithmParameterGenerator section in the + *

  • 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 name of the algorithm requested. * See the AlgorithmParameters section in the
    @@ -239,6 +257,15 @@ public static AlgorithmParameters getInstance(String algorithm, * {@code init}, using an appropriate parameter specification or * parameter encoding. * + * @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 name of the algorithm requested. * See the AlgorithmParameters section in the diff --git a/src/java.base/share/classes/java/security/KeyFactory.java b/src/java.base/share/classes/java/security/KeyFactory.java index 8eaf72f1b83b5..4e651438666bc 100644 --- a/src/java.base/share/classes/java/security/KeyFactory.java +++ b/src/java.base/share/classes/java/security/KeyFactory.java @@ -157,12 +157,21 @@ private KeyFactory(String algorithm) throws NoSuchAlgorithmException { * 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 from 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: + * * * @param algorithm the name of the requested key algorithm. * See the KeyFactory 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 name of the requested key algorithm. * See the KeyFactory section in the @@ -242,6 +260,15 @@ public static KeyFactory getInstance(String algorithm, String provider) * is returned. Note that the specified provider 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 name of the requested key algorithm. * See the KeyFactory section in the diff --git a/src/java.base/share/classes/java/security/KeyPairGenerator.java b/src/java.base/share/classes/java/security/KeyPairGenerator.java index bc10bc9255076..39bf43243bb49 100644 --- a/src/java.base/share/classes/java/security/KeyPairGenerator.java +++ b/src/java.base/share/classes/java/security/KeyPairGenerator.java @@ -204,12 +204,21 @@ private static KeyPairGenerator getInstance(Instance instance, * 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 from 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: + * * * @param algorithm the standard string name of the algorithm. * See the KeyPairGenerator 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 standard string name of the algorithm. * See the KeyPairGenerator section in the @@ -312,6 +330,15 @@ public static KeyPairGenerator getInstance(String algorithm, * is returned. Note that the specified provider 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 standard string name of the algorithm. * See the KeyPairGenerator section in the diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 9e50a1588e77d..7abd62f03ecce 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -36,6 +36,7 @@ import javax.security.auth.DestroyFailedException; import javax.security.auth.callback.*; +import sun.security.jca.ProvidersFilter; import sun.security.util.Debug; /** @@ -841,12 +842,21 @@ private String getProviderName() { * 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 from 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: + * . * * @param type the type of keystore. * See the KeyStore 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 type + * implementation available. + * * @param type the type of keystore. * See the KeyStore section in the @@ -935,6 +954,15 @@ public static KeyStore getInstance(String type, String provider) * object is returned. Note that the specified 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 type + * implementation available. + * * @param type the type of keystore. * See the KeyStore section in the @@ -1677,6 +1705,15 @@ public final void setEntry(String alias, Entry entry, *

    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 type + * implementation available. + * * @param file the keystore file * @param password the keystore password, which may be {@code null} * @@ -1730,6 +1767,15 @@ public static final KeyStore getInstance(File file, char[] password) *

    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 type + * implementation available. + * * @param file the keystore file * @param param the {@code LoadStoreParameter} that specifies how to load * the keystore, which may be {@code null} @@ -1790,7 +1836,8 @@ private static final KeyStore getInstance(File file, char[] password, // Detect the keystore type for (Provider p : Security.getProviders()) { for (Provider.Service s : p.getServices()) { - if (s.getType().equals("KeyStore")) { + if (ProvidersFilter.isAllowed(s) && + s.getType().equals("KeyStore")) { try { KeyStoreSpi impl = (KeyStoreSpi) s.newInstance(null); if (impl.engineProbe(dataStream)) { diff --git a/src/java.base/share/classes/java/security/MessageDigest.java b/src/java.base/share/classes/java/security/MessageDigest.java index fa8d3dea8fd91..e6b07b631e099 100644 --- a/src/java.base/share/classes/java/security/MessageDigest.java +++ b/src/java.base/share/classes/java/security/MessageDigest.java @@ -155,12 +155,21 @@ private MessageDigest(String algorithm, Provider p) { * 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 from 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: + *

    * * @param algorithm the name of the algorithm requested. * See the MessageDigest 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 name of the algorithm requested. * See the MessageDigest section in the @@ -271,6 +289,15 @@ public static MessageDigest getInstance(String algorithm, String provider) * is returned. Note that the specified provider 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 name of the algorithm requested. * See the MessageDigest section in the diff --git a/src/java.base/share/classes/java/security/Policy.java b/src/java.base/share/classes/java/security/Policy.java index 89876498cf547..85e30af726322 100644 --- a/src/java.base/share/classes/java/security/Policy.java +++ b/src/java.base/share/classes/java/security/Policy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -128,12 +128,21 @@ public static void setPolicy(Policy p) * 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: + * * * @param type the specified Policy type * @@ -183,6 +192,15 @@ public static Policy getInstance(String type, Policy.Parameters params) *

    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 type + * implementation available. + * * @param type the specified Policy type * * @param params parameters for the {@code Policy}, which may be @@ -243,6 +261,15 @@ public static Policy getInstance(String type, * is returned. Note that the specified provider 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 type + * implementation available. + * * @param type the specified Policy type * * @param params parameters for the {@code Policy}, which may be diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index de2845fb550d8..a8cf48cdf4746 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -25,8 +25,6 @@ package java.security; -import jdk.internal.event.SecurityProviderServiceEvent; - import javax.crypto.KDFParameters; import javax.security.auth.login.Configuration; import java.io.*; @@ -40,6 +38,12 @@ import java.util.function.Function; import java.util.concurrent.ConcurrentHashMap; +import jdk.internal.access.JavaSecurityProviderAccess; +import jdk.internal.access.SharedSecrets; +import jdk.internal.event.SecurityProviderServiceEvent; +import sun.security.jca.ProvidersFilter; +import sun.security.util.AlgorithmDecomposer; + /** * This class represents a "provider" for the * Java Security API, where a provider implements some or all parts of @@ -119,6 +123,26 @@ public abstract class Provider extends Properties { private static final sun.security.util.Debug debug = sun.security.util.Debug.getInstance("provider", "Provider"); + static { + SharedSecrets.setJavaSecurityProviderAccess( + new JavaSecurityProviderAccess() { + @Override + public Set getServicesNotAllowed(Provider p) { + return p.getServicesNotAllowed(); + } + + @Override + public List getAliases(Service svc) { + return svc.getAliases(); + } + + @Override + public boolean isAllowed(Service svc) { + return svc.isAllowed(); + } + }); + } + /** * The provider name. * @@ -706,8 +730,12 @@ private void checkInitialized() { private transient Map legacyMap; // Set - // Unmodifiable set of all services. Initialized on demand. - private transient volatile Set serviceSet; + // Unmodifiable set of allowed services. Initialized on demand. + private transient volatile Set allowedSet; + + // Set + // Unmodifiable set of not allowed services. Initialized on demand. + private transient volatile Set notAllowedSet; // register the id attributes for this provider // this is to ensure that equals() and hashCode() do not incorrectly @@ -931,7 +959,8 @@ private void implClear() { serviceMap.clear(); legacyChanged = false; servicesChanged = false; - serviceSet = null; + allowedSet = null; + notAllowedSet = null; prngAlgos.clear(); super.clear(); putId(); @@ -1016,17 +1045,31 @@ private void parseLegacy(String name, String value, OPType opType) { if (prevAliasService != null) { prevAliasService.removeAlias(aliasAlg); } - if (stdService == null) { - // add standard mapping in order to add alias + boolean isNewService = stdService == null; + if (isNewService) { stdService = new Service(this, type, value); - legacyMap.put(stdKey, stdService); } stdService.addAlias(aliasAlg); + // The new alias can modify the Providers filter decision. + stdService.computeSvcAllowed(); + if (stdService.cipherTransformsAllowed != null) { + stdService.cipherTransformsAllowed.clear(); + } + if (isNewService) { + // add standard mapping in order to add alias + legacyMap.put(stdKey, stdService); + } legacyMap.put(aliasKey, stdService); break; case REMOVE: if (stdService != null) { stdService.removeAlias(aliasAlg); + // The removed alias can modify the Providers filter + // decision. + stdService.computeSvcAllowed(); + if (stdService.cipherTransformsAllowed != null) { + stdService.cipherTransformsAllowed.clear(); + } } legacyMap.remove(aliasKey); break; @@ -1053,6 +1096,11 @@ private void parseLegacy(String name, String value, OPType opType) { "className can't be null"); if (stdService == null) { stdService = new Service(this, type, stdAlg); + // Note: if the service exists already, recomputing + // Service::isAllowed is not necessary because a + // change in the class name does not affect the + // previous filter decision. + stdService.computeSvcAllowed(); legacyMap.put(stdKey, stdService); } stdService.className = value; @@ -1121,6 +1169,15 @@ private void parseLegacy(String name, String value, OPType opType) { * {@link #putService putService()} and one added via {@link #put put()}, * the service added via {@link #putService putService()} is returned. * + * @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 described + * implementation available. + * * @param type the type of {@link Service service} requested * (for example, {@code MessageDigest}) * @param algorithm the case-insensitive algorithm name (or alternate @@ -1146,11 +1203,14 @@ public Service getService(String type, String algorithm) { if (s == null) { s = legacyMap.get(key); if (s != null && !s.isValid()) { - legacyMap.remove(key, s); return null; } } + if (s != null && !s.isAllowed()) { + return null; + } + if (s != null && SecurityProviderServiceEvent.isTurnedOn()) { var e = new SecurityProviderServiceEvent(); e.provider = getName(); @@ -1175,6 +1235,15 @@ public Service getService(String type, String algorithm) { * Get an unmodifiable Set of all services supported by * this {@code Provider}. * + * @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 be included in the + * Set. + * * @return an unmodifiable Set of all services supported by * this {@code Provider} * @@ -1182,25 +1251,47 @@ public Service getService(String type, String algorithm) { */ public Set getServices() { checkInitialized(); - if (serviceSet == null || legacyChanged || servicesChanged) { - Set set = new LinkedHashSet<>(); - if (!serviceMap.isEmpty()) { - set.addAll(serviceMap.values()); - } - if (!legacyMap.isEmpty()) { - legacyMap.entrySet().forEach(entry -> { - if (!entry.getValue().isValid()) { - legacyMap.remove(entry.getKey(), entry.getValue()); - } else { - set.add(entry.getValue()); - } - }); - } - serviceSet = Collections.unmodifiableSet(set); + computeServiceSets(); + return allowedSet; + } + + /* + * This method returns an unmodifiable set of services that are supported + * by this provider but not allowed by the Providers filter (see + * sun.security.jca.ProvidersFilter). These services must not be used for + * anything other than informational purposes (see + * sun.launcher.SecuritySettings and the -XshowSettings:security:providers + * JVM argument). + */ + private Set getServicesNotAllowed() { + checkInitialized(); + computeServiceSets(); + return notAllowedSet; + } + + private void computeServiceSets() { + if (allowedSet == null || notAllowedSet == null || + legacyChanged || servicesChanged) { + Set newAllowedSet = new LinkedHashSet<>(); + Set newNotAllowedSet = new LinkedHashSet<>(); + classifyServices(serviceMap, newAllowedSet, newNotAllowedSet); + classifyServices(legacyMap, newAllowedSet, newNotAllowedSet); + allowedSet = Collections.unmodifiableSet(newAllowedSet); + notAllowedSet = Collections.unmodifiableSet(newNotAllowedSet); servicesChanged = false; legacyChanged = false; } - return serviceSet; + } + + private static void classifyServices(Map map, + Set allowedSvcs, Set notAllowedSvcs) { + if (!map.isEmpty()) { + for (Service svc : map.values()) { + if (svc.isValid()) { + (svc.isAllowed() ? allowedSvcs : notAllowedSvcs).add(svc); + } + } + } } /** @@ -1212,6 +1303,14 @@ public Set getServices() { * {@extLink security_guide_jca * Java Cryptography Architecture (JCA) Reference Guide}. * + * @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 be added. + * * @param s the Service to add * * @throws NullPointerException if s is {@code null} @@ -1230,6 +1329,9 @@ protected void putService(Service s) { throw new IllegalArgumentException ("service.getProvider() must match this Provider object"); } + if (s.isAllowed == null) { + s.computeSvcAllowed(); + } String type = s.getType(); String algorithm = s.getAlgorithm(); ServiceKey key = new ServiceKey(type, algorithm, true); @@ -1264,15 +1366,15 @@ private void checkAndUpdateSecureRandom(String type, String algo, // service for this provider Service getDefaultSecureRandomService() { checkInitialized(); - - if (!prngAlgos.isEmpty()) { - String algo = prngAlgos.iterator().next(); + for (String algo : prngAlgos) { // IMPORTANT: use the Service obj returned by getService(...) call // as providers may override putService(...)/getService(...) and // return their own Service objects - return getService("SecureRandom", algo); + Service s = getService("SecureRandom", algo); + if (s != null && s.isAllowed()) { + return s; + } } - return null; } @@ -1506,6 +1608,16 @@ public static class Service { private Map attributes; private final EngineDescription engineDescription; + // Cache with the Providers filter decision for this service. Value is + // null when not decided. + private Boolean isAllowed; + + // Cache with transformation - filter decision entries. Transformations + // in this cache are based on this service algorithm or aliases, but are + // not necessarily supported (further evaluation is needed). For Cipher + // service types only (lazily initialized), null otherwise. + private Map cipherTransformsAllowed; + // Reference to the cached implementation Class object. // Will be a Class if this service is loaded from the built-in // classloader (unloading not possible), otherwise a WeakReference to a @@ -1624,6 +1736,129 @@ public Service(Provider provider, String type, String algorithm, } } + /* + * Returns whether the service is allowed or not according to the + * Providers filter. This decision is usually made when a service + * instance is added to a services map, and then cached. However, some + * Providers may override Provider::getService or Provider::getServices + * and return Service instances that did not go through the filter + * before. In any case, if the service did not go through the filter, + * evaluate it now and save the result. + */ + private boolean isAllowed() { + ProvidersFilter.CipherContext cipherContext = + ProvidersFilter.CipherTransformation.getContext(); + if (cipherContext != null) { + // The Cipher class is trying to create a CipherSpi instance + // from a service. E.g. Cipher.getInstance("transformation"). + // The service algorithm and aliases do not match the + // transformation exactly. However, there could still be support + // for it. Evaluate the transformation according to the filter + // and see if the service remains on track for further + // assessment (e.g. Cipher.Transform::supports). + if ((cipherTransformsAllowed != null || + type.equals("Cipher")) && + isTransformationForSvc(cipherContext.svcSearchKey())) { + return isTransformationAllowed( + cipherContext.transformation()); + } else { + // Unlikely. May happen if a provider overrides + // Provider::getService or Provider.Service::newInstance + // and, during a Cipher service lookup, triggers a + // Provider.Service::isAllowed call for a service not + // related to the Cipher transformation. + if (debug != null) { + debug.println("Filter evaluation of a service not " + + "related to a Cipher transformation (" + + cipherContext.transformation() + "). Service " + + "search key: " + cipherContext.svcSearchKey() + + ". Service: " + this); + } + } + } + if (isAllowed == null) { + computeSvcAllowed(); + } + return isAllowed; + } + + /* + * Returns whether a key matches any of the algorithm or aliases + * (case insensitive). + */ + private boolean isTransformationForSvc(String svcSearchKey) { + if (svcSearchKey.equalsIgnoreCase(algorithm)) { + return true; + } + for (String alias : getAliases()) { + if (svcSearchKey.equalsIgnoreCase(alias)) { + return true; + } + } + return false; + } + + /* + * Returns whether a transformation potentially supported by this + * service is allowed by the Providers filter. Service algorithm and + * aliases are used to build transformation aliases. + */ + private boolean isTransformationAllowed(String transformation) { + Boolean isTransformAllowed; + if (cipherTransformsAllowed == null) { + cipherTransformsAllowed = new ConcurrentHashMap<>(); + isTransformAllowed = null; + } else { + isTransformAllowed = + cipherTransformsAllowed.get(transformation); + } + if (isTransformAllowed == null) { + String[] transformParts = AlgorithmDecomposer + .getTransformationTokens(transformation); + // transformParts has three non-empty components because + // transformation 1) was analyzed by + // Cipher::tokenizeTransformation before and 2) if it + // had have a single component, it would have been + // equal to the service algorithm or alias and not set by + // ProvidersFilter.CipherTransformation to reach this point. + assert transformParts.length == 3 : + "Unexpected transformation."; + List allAlgos = + new ArrayList<>(getAliases().size() + 1); + allAlgos.add(algorithm); + allAlgos.addAll(getAliases()); + List tAliases = new ArrayList<>(allAlgos.size() - 1); + for (String algo : allAlgos) { + // If a service algorithm or alias has multiple components, + // use the first one for the transformation alias. The + // second and third one (if any) are assumed to be the mode + // and padding respectively, and taken from the + // transformation. + algo = AlgorithmDecomposer.getTransformationTokens(algo)[0]; + String transformAlgo = algo + "/" + transformParts[1] + + "/" + transformParts[2]; + if (!transformAlgo.equalsIgnoreCase(transformation)) { + tAliases.add(transformAlgo); + } + } + isTransformAllowed = ProvidersFilter.computeSvcAllowed( + provider.getName(), type, transformation, tAliases); + cipherTransformsAllowed.put(transformation, isTransformAllowed); + } + return isTransformAllowed; + } + + /* + * Pass the service through the Providers filter and save the result. + * Called before adding a Service to the map, when adding or removing + * a service alias with the legacy API, and from Service::isAllowed to + * handle uncommon cases. + */ + private void computeSvcAllowed() { + isAllowed = ProvidersFilter.computeSvcAllowed( + provider.getName(), type, algorithm, getAliases()); + } + /** * Get the type of this service. For example, {@code MessageDigest}. * @@ -1661,9 +1896,12 @@ public final String getClassName() { return className; } - // internal only + /* + * Method accessed from sun.security.jca.ProvidersFilter and + * sun.launcher.SecuritySettings. + */ private List getAliases() { - return aliases; + return Collections.unmodifiableList(aliases); } /** @@ -1720,6 +1958,10 @@ public Object newInstance(Object constructorParameter) } registered = true; } + if (!isAllowed()) { + throw new NoSuchAlgorithmException( + "Service not allowed: " + this); + } Class ctrParamClz; try { EngineDescription cap = engineDescription; diff --git a/src/java.base/share/classes/java/security/SecureRandom.java b/src/java.base/share/classes/java/security/SecureRandom.java index 53fdd3070d8ce..898085cbe8267 100644 --- a/src/java.base/share/classes/java/security/SecureRandom.java +++ b/src/java.base/share/classes/java/security/SecureRandom.java @@ -29,6 +29,7 @@ import sun.security.jca.GetInstance; import sun.security.jca.GetInstance.Instance; import sun.security.jca.Providers; +import sun.security.jca.ProvidersFilter; import sun.security.provider.SunEntries; import sun.security.util.Debug; @@ -280,7 +281,13 @@ private void getDefaultPRNG(boolean setSeed, byte[] seed) { if (p.getName().equals("SUN")) { prngAlgorithm = SunEntries.DEF_SECURE_RANDOM_ALGO; prngService = p.getService("SecureRandom", prngAlgorithm); - break; + if (prngService != null) { + if (ProvidersFilter.isAllowed(prngService)) { + break; + } else { + prngService = null; + } + } } else { prngService = p.getDefaultSecureRandomService(); if (prngService != null) { @@ -293,8 +300,13 @@ private void getDefaultPRNG(boolean setSeed, byte[] seed) { // then an implementation-specific default is returned. if (prngService == null) { prngAlgorithm = "SHA1PRNG"; - this.secureRandomSpi = new sun.security.provider.SecureRandom(); this.provider = Providers.getSunProvider(); + try { + this.secureRandomSpi = SecureRandom.getInstance(prngAlgorithm, + this.provider).secureRandomSpi; + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException("Default PRNG not found", nsae); + } } else { try { this.secureRandomSpi = (SecureRandomSpi) @@ -361,12 +373,21 @@ private String getProviderName() { * 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 from 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: + *

    * * @param algorithm the name of the RNG algorithm. * See the {@code SecureRandom} 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 name of the RNG algorithm. * See the {@code SecureRandom} section in the @@ -453,6 +483,15 @@ public static SecureRandom getInstance(String algorithm, String provider) * is returned. Note that the specified provider 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 name of the RNG algorithm. * See the {@code SecureRandom} section in the @@ -502,11 +541,21 @@ public static SecureRandom getInstance(String algorithm, * the {@link Security#getProviders() Security.getProviders()} method. * * @implNote - * The JDK Reference Implementation additionally uses the - * {@code jdk.security.provider.preferred} property to determine - * 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 JDK Reference Implementation additionally uses the following + * properties to customize the behavior of this method: + * * * @param algorithm the name of the RNG algorithm. * See the {@code SecureRandom} 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 name of the RNG algorithm. * See the {@code SecureRandom} section in the @@ -612,6 +670,15 @@ public static SecureRandom getInstance(String algorithm, * provider is returned. Note that the specified provider * 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 name of the RNG algorithm. * See the {@code SecureRandom} section in the @@ -949,6 +1016,15 @@ private static final class StrongPatternHolder { * Every implementation of the Java platform is required to * support at least one strong {@code SecureRandom} implementation. * + * @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. + * * @return a strong {@code SecureRandom} implementation as indicated * by the {@code securerandom.strongAlgorithms} Security property * diff --git a/src/java.base/share/classes/java/security/Security.java b/src/java.base/share/classes/java/security/Security.java index 6969fe8a8e142..dc3b821fe0a1e 100644 --- a/src/java.base/share/classes/java/security/Security.java +++ b/src/java.base/share/classes/java/security/Security.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -55,6 +55,7 @@ import sun.security.jca.GetInstance; import sun.security.jca.ProviderList; import sun.security.jca.Providers; +import sun.security.jca.ProvidersFilter; import sun.security.util.Debug; import sun.security.util.PropertyExpander; @@ -380,23 +381,26 @@ private static ProviderProperty getProviderProperty(String key) { } /** - * Returns the property (if any) mapping the key for the given provider. + * Returns a service allowed by the Providers filter given a service type, + * algorithm and provider. Search is case-insensitive. */ - 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 e = provider.keys(); - e.hasMoreElements(); ) { - String matchKey = (String)e.nextElement(); - if (key.equalsIgnoreCase(matchKey)) { - prop = provider.getProperty(matchKey); + private static Provider.Service findService(String type, String algo, + Provider provider) { + // Try the fast path (when "type" has the exact case). + Provider.Service foundSvc = provider.getService(type, algo); + if (foundSvc == null) { + // Try the slow path (when "type" does not have the exact case). + for (Provider.Service svc : provider.getServices()) { + if (svc.getType().equalsIgnoreCase(type)) { + foundSvc = provider.getService(svc.getType(), algo); break; } } } - return prop; + if (foundSvc != null && ProvidersFilter.isAllowed(foundSvc)) { + return foundSvc; + } + return null; } /** @@ -596,6 +600,14 @@ public static Provider getProvider(String name) { * for information about standard cryptographic service names, standard * algorithm names and standard attribute names. * + * @implNote + * 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 providers filter + * will not match the selection criteria for its provider. + * * @param filter the criterion for selecting * providers. The filter is case-insensitive. * @@ -674,6 +686,14 @@ public static Provider[] getProviders(String filter) { * for information about standard cryptographic service names, standard * algorithm names and standard attribute names. * + * @implNote + * 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 providers filter + * will not match the selection criteria for its provider. + * * @param filter the criteria for selecting * providers. The filter is case-insensitive. * @@ -912,30 +932,9 @@ private boolean isCompositeValue() { * 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) { - // 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 (propValue == null) { - // The provider doesn't have the given - // key in its property list. - return false; - } + Provider.Service svc = findService(serviceName, algName, prov); + if (svc == null) { + return false; } // If the key is in the format of: @@ -945,6 +944,11 @@ private boolean isCriterionSatisfied(Provider prov) { return true; } + String foundAttrValue = svc.getAttribute(attrName); + if (foundAttrValue == null) { + return false; + } + // If we get here, the key must be in the // format of . . @@ -955,24 +959,24 @@ private boolean isCriterionSatisfied(Provider prov) { // for a specific .. if (attrName.equalsIgnoreCase("KeySize")) { int requestedSize = Integer.parseInt(attrValue); - int maxSize = Integer.parseInt(propValue); + int maxSize = Integer.parseInt(foundAttrValue); return requestedSize <= maxSize; } // Handle attributes with composite values if (isCompositeValue()) { String attrValue2 = attrValue.toUpperCase(Locale.ENGLISH); - propValue = propValue.toUpperCase(Locale.ENGLISH); + foundAttrValue = foundAttrValue.toUpperCase(Locale.ENGLISH); // match value to the property components - String[] propComponents = propValue.split("\\|"); + String[] propComponents = foundAttrValue.split("\\|"); for (String pc : propComponents) { if (attrValue2.equals(pc)) return true; } return false; } else { // direct string compare (ignore case) - return attrValue.equalsIgnoreCase(propValue); + return attrValue.equalsIgnoreCase(foundAttrValue); } } } @@ -989,6 +993,14 @@ private boolean isCriterionSatisfied(Provider prov) { * Java Cryptography Architecture (JCA) Reference Guide}. * Note: the returned set is immutable. * + * @implNote + * 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. The algorithm of a service that is not enabled by the providers + * filter will be in the returned set but may not be available for use. + * * @param serviceName the name of the Java cryptographic * service (e.g., {@code Signature}, {@code MessageDigest}, {@code Cipher}, * {@code Mac}, {@code KeyStore}). diff --git a/src/java.base/share/classes/java/security/Signature.java b/src/java.base/share/classes/java/security/Signature.java index 52aa4328b2cfb..42ac3906579f5 100644 --- a/src/java.base/share/classes/java/security/Signature.java +++ b/src/java.base/share/classes/java/security/Signature.java @@ -241,12 +241,21 @@ protected Signature(String algorithm) { * 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 from 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 standard name of the algorithm requested. * See the Signature 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 name of the algorithm requested. * See the Signature section in the @@ -424,6 +442,15 @@ public static Signature getInstance(String algorithm, String provider) * is returned. Note that the specified provider 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 name of the algorithm requested. * See the Signature section in the @@ -468,7 +495,7 @@ private static Signature getInstanceRSA(Provider p) throws NoSuchAlgorithmException { // try Signature first Service s = p.getService("Signature", RSA_SIGNATURE); - if (s != null) { + if (s != null && ProvidersFilter.isAllowed(s)) { Instance instance = GetInstance.getInstance(s, SignatureSpi.class); return getInstance(instance, RSA_SIGNATURE); } diff --git a/src/java.base/share/classes/java/security/cert/CertPathBuilder.java b/src/java.base/share/classes/java/security/cert/CertPathBuilder.java index 692bfa0fb8e79..76b828f5ef3bc 100644 --- a/src/java.base/share/classes/java/security/cert/CertPathBuilder.java +++ b/src/java.base/share/classes/java/security/cert/CertPathBuilder.java @@ -142,12 +142,21 @@ protected CertPathBuilder(CertPathBuilderSpi builderSpi, Provider provider, * 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 from 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 name of the requested {@code CertPathBuilder} * algorithm. See the CertPathBuilder 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 name of the requested {@code CertPathBuilder} * algorithm. See the CertPathBuilder section in the @@ -232,6 +250,15 @@ public static CertPathBuilder getInstance(String algorithm, String provider) * object is returned. Note that the specified 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 name of the requested {@code CertPathBuilder} * algorithm. See the CertPathBuilder section in the diff --git a/src/java.base/share/classes/java/security/cert/CertPathValidator.java b/src/java.base/share/classes/java/security/cert/CertPathValidator.java index 539bee3419247..af297da2d8453 100644 --- a/src/java.base/share/classes/java/security/cert/CertPathValidator.java +++ b/src/java.base/share/classes/java/security/cert/CertPathValidator.java @@ -143,12 +143,21 @@ protected CertPathValidator(CertPathValidatorSpi validatorSpi, * 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 from 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 name of the requested {@code CertPathValidator} * algorithm. See the CertPathValidator 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 name of the requested {@code CertPathValidator} * algorithm. See the CertPathValidator section in the @@ -234,6 +252,15 @@ public static CertPathValidator getInstance(String algorithm, * object is returned. Note that the specified 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 name of the requested {@code CertPathValidator} * algorithm. See the CertPathValidator section in the diff --git a/src/java.base/share/classes/java/security/cert/CertStore.java b/src/java.base/share/classes/java/security/cert/CertStore.java index ce213a7c2e8f3..46a8312f23754 100644 --- a/src/java.base/share/classes/java/security/cert/CertStore.java +++ b/src/java.base/share/classes/java/security/cert/CertStore.java @@ -202,12 +202,21 @@ public final Collection getCRLs(CRLSelector selector) * cloned. * * @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 from 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 {@code CertStore} 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 + * {@code CertStore} type implementation available.
    • + *
    * * @param type the name of the requested {@code CertStore} type. * See the CertStore section in the
    @@ -336,6 +354,15 @@ public static CertStore getInstance(String type, * Note that the specified {@code CertStoreParameters} object is * cloned. * + * @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 + * {@code CertStore} type implementation available. + * * @param type the requested {@code CertStore} type. * See the CertStore section in the diff --git a/src/java.base/share/classes/java/security/cert/CertificateFactory.java b/src/java.base/share/classes/java/security/cert/CertificateFactory.java index 3ee375c163c08..e46764951a9b8 100644 --- a/src/java.base/share/classes/java/security/cert/CertificateFactory.java +++ b/src/java.base/share/classes/java/security/cert/CertificateFactory.java @@ -164,12 +164,21 @@ protected CertificateFactory(CertificateFactorySpi certFacSpi, * 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 from 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 certificate 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 certificate + * type implementation available.
    • + *
    * * @param type the name of the requested certificate type. * See the CertificateFactory 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 certificate + * type implementation available. + * * @param type the certificate type. * See the CertificateFactory section in the @@ -261,6 +279,15 @@ public static final CertificateFactory getInstance(String type, * object is returned. Note that the specified 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 certificate + * type implementation available. + * * @param type the certificate type. * See the CertificateFactory section in the diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java index 22dc66127e2d6..5fba2d24e923f 100644 --- a/src/java.base/share/classes/javax/crypto/Cipher.java +++ b/src/java.base/share/classes/javax/crypto/Cipher.java @@ -45,6 +45,7 @@ import sun.security.util.Debug; import sun.security.jca.*; +import sun.security.util.AlgorithmDecomposer; import sun.security.util.KnownOIDs; /** @@ -314,61 +315,29 @@ private Cipher(CipherSpi firstSpi, Service firstService, this.lock = new Object(); } - private static final String SHA512TRUNCATED = "SHA512/2"; - // Parse the specified cipher transformation for algorithm and the // optional mode and padding. If the transformation contains only // algorithm, then only algorithm is returned. Otherwise, the // transformation must contain all 3 and they must be non-empty. private static String[] tokenizeTransformation(String transformation) throws NoSuchAlgorithmException { - if (transformation == null) { - throw new NoSuchAlgorithmException("No transformation given"); - } - /* - * Components of a cipher transformation: - * - * 1) algorithm component (e.g., AES) - * 2) feedback component (e.g., CFB) - optional - * 3) padding component (e.g., PKCS5Padding) - optional - */ - - // check if the transformation contains algorithms with "/" in their - // name which can cause the parsing logic to go wrong - int sha512Idx = transformation.toUpperCase(Locale.ENGLISH) - .indexOf(SHA512TRUNCATED); - int startIdx = (sha512Idx == -1 ? 0 : - sha512Idx + SHA512TRUNCATED.length()); - int endIdx = transformation.indexOf('/', startIdx); - - boolean algorithmOnly = (endIdx == -1); - String algo = (algorithmOnly ? transformation.trim() : - transformation.substring(0, endIdx).trim()); - if (algo.isEmpty()) { - throw new NoSuchAlgorithmException("Invalid transformation: " + - "algorithm not specified-" - + transformation); - } - if (algorithmOnly) { // done - return new String[] { algo }; - } else { - // continue parsing mode and padding - startIdx = endIdx+1; - endIdx = transformation.indexOf('/', startIdx); - if (endIdx == -1) { - throw new NoSuchAlgorithmException("Invalid transformation" - + " format:" + transformation); - } - String mode = transformation.substring(startIdx, endIdx).trim(); - String padding = transformation.substring(endIdx+1).trim(); - // ensure mode and padding are specified - if (mode.isEmpty() || padding.isEmpty()) { - throw new NoSuchAlgorithmException("Invalid transformation: " + - "missing mode and/or padding-" - + transformation); - } - return new String[] { algo, mode, padding }; - } + String[] transformationTokens = + AlgorithmDecomposer.getTransformationTokens(transformation); + if (transformationTokens[0].isEmpty()) { + throw new NoSuchAlgorithmException("Invalid transformation (" + + "algorithm not specified): " + transformation); + } + if (transformationTokens.length == 2) { + throw new NoSuchAlgorithmException( + "Invalid transformation format: " + transformation); + } + if (transformationTokens.length == 3 && + (transformationTokens[1].isEmpty() || + transformationTokens[2].isEmpty())) { + throw new NoSuchAlgorithmException("Invalid transformation (" + + "missing mode and/or padding): " + transformation); + } + return transformationTokens; } // Provider attribute name for supported chaining mode @@ -490,6 +459,30 @@ private static Transform getTransform(Service s, return null; } + private static Service tryGetService(Provider p, String canonicalTransform, + String svcSearchKey) { + ProvidersFilter.CipherTransformation ct = + new ProvidersFilter.CipherTransformation( + canonicalTransform, svcSearchKey); + try (ct) { + Service s = p.getService("Cipher", svcSearchKey); + if (s == null || !ProvidersFilter.isAllowed(s)) { + return null; + } + return s; + } + } + + private static Object newInstance(Service s, String canonicalTransform, + String svcSearchKey) throws NoSuchAlgorithmException { + ProvidersFilter.CipherTransformation ct = + new ProvidersFilter.CipherTransformation( + canonicalTransform, svcSearchKey); + try (ct) { + return s.newInstance(null); + } + } + /** * Returns a {@code Cipher} object that implements the specified * transformation. @@ -510,12 +503,22 @@ private static Transform getTransform(Service s, * requirements of your application. * * @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 transformation. + * 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 + * transformation implementation available.
    • + *
    + *

    * See also the Cipher Transformations section of the {@extLink * security_guide_jdk_providers JDK Providers} document for information * on the transformation defaults used by JDK providers. @@ -548,17 +551,18 @@ public static final Cipher getInstance(String transformation) throw new NoSuchAlgorithmException("Null or empty transformation"); } List transforms = getTransforms(transformation); + String canonicalTransform = transforms.getFirst().transform; List cipherServices = new ArrayList<>(transforms.size()); for (Transform transform : transforms) { cipherServices.add(new ServiceId("Cipher", transform.transform)); } // make sure there is at least one service from a signed provider // and that it can use the specified mode and padding - Iterator t = GetInstance.getServices(cipherServices); + Iterator t = GetInstance.getCipherServices(cipherServices); Exception failure = null; while (t.hasNext()) { Service s = t.next(); - if (JceSecurity.canUseProvider(s.getProvider()) == false) { + if (!JceSecurity.canUseProvider(s.getProvider())) { continue; } Transform tr = getTransform(s, transforms); @@ -575,7 +579,8 @@ public static final Cipher getInstance(String transformation) // even when mode and padding are both supported, they // may not be used together, try out and see if it works try { - CipherSpi spi = (CipherSpi)s.newInstance(null); + CipherSpi spi = (CipherSpi)newInstance(s, canonicalTransform, + tr.transform); tr.setModePadding(spi); // specify null instead of spi for delayed provider selection return new Cipher(null, s, t, transformation, transforms); @@ -606,6 +611,14 @@ public static final Cipher getInstance(String transformation) * requirements of your application. * * @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 + * transformation implementation available. + *

    * See the Cipher Transformations section of the {@extLink * security_guide_jdk_providers JDK Providers} document for information * on the transformation defaults used by JDK providers. @@ -679,6 +692,14 @@ private String getProviderName() { * requirements of your application. * * @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 + * transformation implementation available. + *

    * See the Cipher Transformations section of the {@extLink * security_guide_jdk_providers JDK Providers} document for information * on the transformation defaults used by JDK providers. @@ -722,22 +743,24 @@ public static final Cipher getInstance(String transformation, } Exception failure = null; List transforms = getTransforms(transformation); + String canonicalTransform = transforms.getFirst().transform; boolean providerChecked = false; String paddingError = null; for (Transform tr : transforms) { - Service s = provider.getService("Cipher", tr.transform); + Service s = tryGetService(provider, canonicalTransform, + tr.transform); if (s == null) { continue; } - if (providerChecked == false) { + if (!providerChecked) { // for compatibility, first do the lookup and then verify // the provider. this makes the difference between a NSAE - // and a SecurityException if the - // provider does not support the algorithm. + // and a SecurityException if the provider does not support + // the algorithm. Exception ve = JceSecurity.getVerificationResult(provider); if (ve != null) { String msg = "JCE cannot authenticate the provider " - + provider.getName(); + + provider.getName(); throw new SecurityException(msg, ve); } providerChecked = true; @@ -750,7 +773,8 @@ public static final Cipher getInstance(String transformation, continue; } try { - CipherSpi spi = (CipherSpi)s.newInstance(null); + CipherSpi spi = (CipherSpi)newInstance(s, canonicalTransform, + tr.transform); tr.setModePadding(spi); Cipher cipher = new Cipher(spi, transformation); cipher.provider = s.getProvider(); @@ -760,7 +784,6 @@ public static final Cipher getInstance(String transformation, failure = e; } } - // throw NoSuchPaddingException if the problem is with padding if (failure instanceof NoSuchPaddingException) { throw (NoSuchPaddingException)failure; @@ -817,6 +840,7 @@ void chooseFirstProvider() { new Exception("Call trace").printStackTrace(); } } + String canonicalTransform = transforms.getFirst().transform; Exception lastException = null; while ((firstService != null) || serviceIterator.hasNext()) { Service s; @@ -830,7 +854,7 @@ void chooseFirstProvider() { s = serviceIterator.next(); thisSpi = null; } - if (JceSecurity.canUseProvider(s.getProvider()) == false) { + if (!JceSecurity.canUseProvider(s.getProvider())) { continue; } Transform tr = getTransform(s, transforms); @@ -843,7 +867,8 @@ void chooseFirstProvider() { } try { if (thisSpi == null) { - Object obj = s.newInstance(null); + Object obj = newInstance(s, canonicalTransform, + tr.transform); if (obj instanceof CipherSpi == false) { continue; } @@ -911,6 +936,7 @@ private void chooseProvider(int initType, int opmode, Key key, implInit(spi, initType, opmode, key, paramSpec, params, random); return; } + String canonicalTransform = transforms.getFirst().transform; Exception lastException = null; while ((firstService != null) || serviceIterator.hasNext()) { Service s; @@ -925,10 +951,10 @@ private void chooseProvider(int initType, int opmode, Key key, thisSpi = null; } // if provider says it does not support this key, ignore it - if (s.supportsParameter(key) == false) { + if (!s.supportsParameter(key)) { continue; } - if (JceSecurity.canUseProvider(s.getProvider()) == false) { + if (!JceSecurity.canUseProvider(s.getProvider())) { continue; } Transform tr = getTransform(s, transforms); @@ -941,7 +967,8 @@ private void chooseProvider(int initType, int opmode, Key key, } try { if (thisSpi == null) { - thisSpi = (CipherSpi)s.newInstance(null); + thisSpi = (CipherSpi)newInstance(s, canonicalTransform, + tr.transform); } tr.setModePadding(thisSpi); initCryptoPermission(); diff --git a/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java b/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java index 63686b8433f38..6c8c070a21691 100644 --- a/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java +++ b/src/java.base/share/classes/javax/crypto/ExemptionMechanism.java @@ -114,12 +114,21 @@ public final String getName() { * 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 from 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 standard name of the requested exemption * mechanism. @@ -162,6 +171,15 @@ public static final ExemptionMechanism getInstance(String algorithm) *

    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 standard name of the requested exemption mechanism. * See the ExemptionMechanism section in the * + *

  • 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 key derivation algorithm to use. See the {@code KDF} section @@ -247,6 +256,15 @@ public static KDF getInstance(String algorithm) * the specified security provider. The specified provider must be * registered in the security 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 key derivation algorithm to use. See the {@code KDF} section * in the
    @@ -283,6 +301,15 @@ public static KDF getInstance(String algorithm, String provider) * Returns a {@code KDF} object that implements the specified algorithm from * the specified security provider. * + * @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 key derivation algorithm to use. See the {@code KDF} section * in the @@ -317,12 +344,21 @@ public static KDF getInstance(String algorithm, Provider provider) * is initialized with the specified parameters. * * @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()}. + * 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 key derivation algorithm to use. See the {@code KDF} section @@ -366,6 +402,15 @@ public static KDF getInstance(String algorithm, * the specified provider and is initialized with the specified parameters. * The specified provider must be registered in the security 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 key derivation algorithm to use. See the {@code KDF} section * in the
    @@ -420,6 +465,15 @@ public static KDF getInstance(String algorithm, * Returns a {@code KDF} object that implements the specified algorithm from * the specified provider and is initialized with the specified parameters. * + * @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 key derivation algorithm to use. See the {@code KDF} section * in the diff --git a/src/java.base/share/classes/javax/crypto/KEM.java b/src/java.base/share/classes/javax/crypto/KEM.java index 58089b386661d..73918068f584a 100644 --- a/src/java.base/share/classes/javax/crypto/KEM.java +++ b/src/java.base/share/classes/javax/crypto/KEM.java @@ -556,6 +556,23 @@ private KEM(String algorithm, DelayedKEM delayed) { /** * Returns a {@code KEM} object that implements the specified algorithm. * + * @implNote + * 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 name of the KEM algorithm. * See the {@code KEM} section in the
    @@ -592,6 +609,15 @@ public static KEM getInstance(String algorithm) * Returns a {@code KEM} object that implements the specified algorithm * from the specified security provider. * + * @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 name of the KEM algorithm. * See the {@code KEM} section in the @@ -624,6 +650,15 @@ public static KEM getInstance(String algorithm, Provider provider) * Returns a {@code KEM} object that implements the specified algorithm * from the specified security provider. * + * @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 name of the KEM algorithm. * See the {@code KEM} section in the diff --git a/src/java.base/share/classes/javax/crypto/KeyAgreement.java b/src/java.base/share/classes/javax/crypto/KeyAgreement.java index 4b3a3fd1cf7b8..ee966b28ef185 100644 --- a/src/java.base/share/classes/javax/crypto/KeyAgreement.java +++ b/src/java.base/share/classes/javax/crypto/KeyAgreement.java @@ -157,12 +157,21 @@ public final String getAlgorithm() { * 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 from 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 standard name of the requested key agreement * algorithm. @@ -210,6 +219,15 @@ public static final KeyAgreement getInstance(String algorithm) *

    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 standard name of the requested key agreement * algorithm. * See the KeyAgreement section in the + *

  • 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 standard name of the requested key algorithm. * See the KeyGenerator 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 standard name of the requested key algorithm. * See the KeyGenerator section in the @@ -300,6 +318,15 @@ public static final KeyGenerator getInstance(String algorithm, * object is returned. Note that the specified 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 standard name of the requested key algorithm. * See the KeyGenerator section in the diff --git a/src/java.base/share/classes/javax/crypto/Mac.java b/src/java.base/share/classes/javax/crypto/Mac.java index fb1eb2c310a92..1b42c85e16421 100644 --- a/src/java.base/share/classes/javax/crypto/Mac.java +++ b/src/java.base/share/classes/javax/crypto/Mac.java @@ -153,12 +153,21 @@ public final String getAlgorithm() { * 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 from 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 standard name of the requested MAC algorithm. * See the Mac 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 standard name of the requested MAC algorithm. * See the Mac section in the @@ -246,6 +264,15 @@ public static final Mac getInstance(String algorithm, String provider) * is returned. Note that the specified provider * 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 standard name of the requested MAC algorithm. * See the Mac section in the diff --git a/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java b/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java index d7163e4d24074..891fd4603e177 100644 --- a/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java +++ b/src/java.base/share/classes/javax/crypto/SecretKeyFactory.java @@ -133,12 +133,21 @@ private SecretKeyFactory(String algorithm) throws NoSuchAlgorithmException { * 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 from 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 standard name of the requested secret key * algorithm. @@ -176,6 +185,15 @@ public static final SecretKeyFactory getInstance(String algorithm) *

    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 standard name of the requested secret key * algorithm. * See the SecretKeyFactory section in the + *

  • 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 standard name of the requested algorithm. * See 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 standard name of the requested algorithm. * See the @@ -203,6 +221,15 @@ public static final KeyManagerFactory getInstance(String algorithm, * object is returned. Note that the specified 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 standard name of the requested algorithm. * See the diff --git a/src/java.base/share/classes/javax/net/ssl/SSLContext.java b/src/java.base/share/classes/javax/net/ssl/SSLContext.java index b8759d0414de1..bf0c754b57470 100644 --- a/src/java.base/share/classes/javax/net/ssl/SSLContext.java +++ b/src/java.base/share/classes/javax/net/ssl/SSLContext.java @@ -149,12 +149,21 @@ public static void setDefault(SSLContext context) { * 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 from 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 protocol. + * 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 protocol + * implementation available.
    • + *
    * * @param protocol the standard name of the requested protocol. * See the SSLContext 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 protocol + * implementation available. + * * @param protocol the standard name of the requested protocol. * See the SSLContext section in the @@ -237,6 +255,15 @@ public static SSLContext getInstance(String protocol, String provider) * object is returned. Note that the specified 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 protocol + * implementation available. + * * @param protocol the standard name of the requested protocol. * See the SSLContext section in the diff --git a/src/java.base/share/classes/javax/net/ssl/TrustManagerFactory.java b/src/java.base/share/classes/javax/net/ssl/TrustManagerFactory.java index 76c7680640389..150548517f2b4 100644 --- a/src/java.base/share/classes/javax/net/ssl/TrustManagerFactory.java +++ b/src/java.base/share/classes/javax/net/ssl/TrustManagerFactory.java @@ -125,12 +125,21 @@ public final String getAlgorithm() { * 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 from 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 standard name of the requested trust management * algorithm. See 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 standard name of the requested trust management * algorithm. See the @@ -218,6 +236,15 @@ public static final TrustManagerFactory getInstance(String algorithm, * object is returned. Note that the specified 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 standard name of the requested trust management * algorithm. See the diff --git a/src/java.base/share/classes/javax/security/auth/login/Configuration.java b/src/java.base/share/classes/javax/security/auth/login/Configuration.java index 6e69d0746ff23..4df5d7405be65 100644 --- a/src/java.base/share/classes/javax/security/auth/login/Configuration.java +++ b/src/java.base/share/classes/javax/security/auth/login/Configuration.java @@ -254,12 +254,21 @@ public static void setConfiguration(Configuration configuration) { * 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 from 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 specified Configuration type. See the Configuration * 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 type + * implementation available. + * * @param type the specified Configuration type. See the Configuration * section in the @@ -380,6 +398,15 @@ public static Configuration getInstance(String type, * object is returned. Note that the specified 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 type + * implementation available. + * * @param type the specified Configuration type. See the Configuration * section in the diff --git a/src/java.base/share/classes/jdk/internal/access/JavaSecurityProviderAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaSecurityProviderAccess.java new file mode 100644 index 0000000000000..9b76e93903176 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/access/JavaSecurityProviderAccess.java @@ -0,0 +1,37 @@ +/* + * 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. 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 jdk.internal.access; + +import java.security.Provider; +import java.util.List; +import java.util.Set; + +public interface JavaSecurityProviderAccess { + Set getServicesNotAllowed(Provider p); + List getAliases(Provider.Service svc); + boolean isAllowed(Provider.Service svc); +} diff --git a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java index 5c6212d0bf6e1..1313104fb9873 100644 --- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java +++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java @@ -40,6 +40,7 @@ import java.io.FilePermission; import java.io.ObjectInputStream; import java.io.RandomAccessFile; +import java.security.Provider; import java.security.Signature; import javax.security.auth.x500.X500Principal; @@ -85,6 +86,7 @@ public class SharedSecrets { private static JavaUtilZipFileAccess javaUtilZipFileAccess; private static JavaUtilResourceBundleAccess javaUtilResourceBundleAccess; private static JavaSecurityPropertiesAccess javaSecurityPropertiesAccess; + private static JavaSecurityProviderAccess javaSecurityProviderAccess; private static JavaSecuritySignatureAccess javaSecuritySignatureAccess; private static JavaSecuritySpecAccess javaSecuritySpecAccess; private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; @@ -307,6 +309,20 @@ public static JavaSecurityPropertiesAccess getJavaSecurityPropertiesAccess() { return access; } + public static void setJavaSecurityProviderAccess( + JavaSecurityProviderAccess jspa) { + javaSecurityProviderAccess = jspa; + } + + public static JavaSecurityProviderAccess getJavaSecurityProviderAccess() { + var access = javaSecurityProviderAccess; + if (access == null) { + ensureClassInitialized(Provider.class); + access = javaSecurityProviderAccess; + } + return access; + } + public static JavaUtilZipFileAccess getJavaUtilZipFileAccess() { var access = javaUtilZipFileAccess; if (access == null) { diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 43d148a342886..a1551d620d68a 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -320,6 +320,7 @@ exports sun.security.jca to java.security.sasl, java.smartcardio, + java.xml.crypto, jdk.crypto.cryptoki, jdk.naming.dns; exports sun.security.pkcs to diff --git a/src/java.base/share/classes/sun/launcher/SecuritySettings.java b/src/java.base/share/classes/sun/launcher/SecuritySettings.java index afd5ead4914a0..73adf1352fd8e 100644 --- a/src/java.base/share/classes/sun/launcher/SecuritySettings.java +++ b/src/java.base/share/classes/sun/launcher/SecuritySettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -32,7 +32,6 @@ import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.Security; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Properties; @@ -140,6 +139,30 @@ private static void printSecurityTLSConfig(boolean verbose) { ostream.println(); } + private static void printSecurityProviderServices( + Set services) { + if (!services.isEmpty()) { + services.stream().sorted( + Comparator.comparing(Provider.Service::getType) + .thenComparing(Provider.Service::getAlgorithm)) + .forEach(ps -> { + ostream.println(THREEINDENT + + ps.getType() + "." + ps.getAlgorithm()); + List aliases = SharedSecrets + .getJavaSecurityProviderAccess().getAliases(ps); + + if (!aliases.isEmpty()) { + ostream.println(wrappedString( + aliases.stream().sorted() + .collect(Collectors.joining(", ", INDENT + " aliases: [", "]")), + 80, " " + TWOINDENT, INDENT + THREEINDENT)); + } + }); + } else { + ostream.println(THREEINDENT + ""); + } + } + private static void printSecurityProviderConfig(boolean verbose) { ostream.println(INDENT + "Security provider static configuration: (in order of preference)"); for (Provider p : Security.getProviders()) { @@ -149,39 +172,14 @@ private static void printSecurityProviderConfig(boolean verbose) { } ostream.println(TWOINDENT + "Provider name: " + p.getName()); if (verbose) { - ostream.println(wrappedString(PROV_INFO_STRING + p.getInfo(), 80, - TWOINDENT, THREEINDENT)); - ostream.println(TWOINDENT + "Provider services: (type : algorithm)"); - Set services = p.getServices(); - Set keys = Collections.list(p.keys()) - .stream() - .map(String.class::cast) - .filter(s -> s.startsWith("Alg.Alias.")) - .collect(Collectors.toSet()); - if (!services.isEmpty()) { - services.stream() - .sorted(Comparator.comparing(Provider.Service::getType) - .thenComparing(Provider.Service::getAlgorithm)) - .forEach(ps -> { - ostream.println(THREEINDENT + - ps.getType() + "." + ps.getAlgorithm()); - List aliases = keys - .stream() - .filter(s -> s.startsWith("Alg.Alias." + ps.getType())) - .filter(s -> p.getProperty(s).equals(ps.getAlgorithm())) - .map(s -> s.substring(("Alg.Alias." + ps.getType() + ".").length())) - .toList(); - - if (!aliases.isEmpty()) { - ostream.println(wrappedString( - aliases.stream() - .collect(Collectors.joining(", ", INDENT + " aliases: [", "]")), - 80, " " + TWOINDENT, INDENT + THREEINDENT)); - } - }); - } else { - ostream.println(THREEINDENT + ""); - } + ostream.println(wrappedString(PROV_INFO_STRING + p.getInfo(), + 80, TWOINDENT, THREEINDENT)); + ostream.println(TWOINDENT + "Provider services allowed: (type : algorithm)"); + printSecurityProviderServices(p.getServices()); + ostream.println(TWOINDENT + "Provider services NOT allowed: (type : algorithm)"); + printSecurityProviderServices( + SharedSecrets.getJavaSecurityProviderAccess() + .getServicesNotAllowed(p)); } } if (verbose) { diff --git a/src/java.base/share/classes/sun/security/jca/GetInstance.java b/src/java.base/share/classes/sun/security/jca/GetInstance.java index c99bd8851fa5c..c6538f1da4bac 100644 --- a/src/java.base/share/classes/sun/security/jca/GetInstance.java +++ b/src/java.base/share/classes/sun/security/jca/GetInstance.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -83,7 +83,7 @@ public static Service getService(String type, String algorithm, throw new NoSuchProviderException("no such provider: " + provider); } Service s = p.getService(type, algorithm); - if (s == null) { + if (s == null || !ProvidersFilter.isAllowed(s)) { throw new NoSuchAlgorithmException("no such algorithm: " + algorithm + " for provider " + provider); } @@ -96,7 +96,7 @@ public static Service getService(String type, String algorithm, throw new IllegalArgumentException("missing provider"); } Service s = provider.getService(type, algorithm); - if (s == null) { + if (s == null || !ProvidersFilter.isAllowed(s)) { throw new NoSuchAlgorithmException("no such algorithm: " + algorithm + " for provider " + provider.getName()); } @@ -123,6 +123,11 @@ public static Iterator getServices(List ids) { return list.getServices(ids); } + public static Iterator getCipherServices(List ids) { + ProviderList list = Providers.getProviderList(); + return list.getCipherServices(ids); + } + /* * For all the getInstance() methods below: * @param type the type of engine (e.g. MessageDigest) diff --git a/src/java.base/share/classes/sun/security/jca/ProviderList.java b/src/java.base/share/classes/sun/security/jca/ProviderList.java index b83571405434e..ae308206240e5 100644 --- a/src/java.base/share/classes/sun/security/jca/ProviderList.java +++ b/src/java.base/share/classes/sun/security/jca/ProviderList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -368,7 +368,7 @@ public Service getService(String type, String name) { continue; } Service s = p.getService(type, name); - if (s != null) { + if (s != null && ProvidersFilter.isAllowed(s)) { return s; } } @@ -376,7 +376,7 @@ public Service getService(String type, String name) { for (i = 0; i < configs.length; i++) { Provider p = getProvider(i); Service s = p.getService(type, name); - if (s != null) { + if (s != null && ProvidersFilter.isAllowed(s)) { return s; } } @@ -400,12 +400,17 @@ public Iterator getServices(List ids) { return new ServiceIterator(ids); } + public Iterator getCipherServices(List ids) { + return new CipherServiceIterator(ids); + } + /** * Inner class for an iterator over Services. Customized implementation in * order to delay Provider initialization and lookup. * Not thread safe. */ - private final class ServiceIterator implements Iterator { + private sealed class ServiceIterator implements Iterator + permits CipherServiceIterator { // type and algorithm for simple lookup // avoid allocating/traversing the ServiceId list for these lookups @@ -497,14 +502,14 @@ private Service tryGet(int index) { if (type != null) { // simple lookup - Service s = p.getService(type, algorithm); + Service s = tryGetService(p, type, algorithm); if (s != null) { addService(s); } } else { // parallel lookup for (ServiceId id : ids) { - Service s = p.getService(id.type, id.algorithm); + Service s = tryGetService(p, id.type, id.algorithm); if (s != null) { addService(s); } @@ -513,6 +518,14 @@ private Service tryGet(int index) { } } + Service tryGetService(Provider p, String type, String algorithm) { + Service s = p.getService(type, algorithm); + if (s == null || !ProvidersFilter.isAllowed(s)) { + return null; + } + return s; + } + int index; @Override @@ -536,6 +549,25 @@ public void remove() { } } + private final class CipherServiceIterator extends ServiceIterator { + private final String canonicalTransform; + + CipherServiceIterator(List ids) { + super(ids); + canonicalTransform = ids.getFirst().algorithm; + } + + @Override + Service tryGetService(Provider p, String type, String algorithm) { + ProvidersFilter.CipherTransformation ct = + new ProvidersFilter.CipherTransformation( + canonicalTransform, algorithm); + try (ct) { + return super.tryGetService(p, type, algorithm); + } + } + } + // Provider list defined by jdk.security.provider.preferred entry static final class PreferredList { ArrayList list = new ArrayList<>(); diff --git a/src/java.base/share/classes/sun/security/jca/ProvidersFilter.java b/src/java.base/share/classes/sun/security/jca/ProvidersFilter.java new file mode 100644 index 0000000000000..a9c6a05abaabe --- /dev/null +++ b/src/java.base/share/classes/sun/security/jca/ProvidersFilter.java @@ -0,0 +1,807 @@ +/* + * 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. 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 sun.security.jca; + +import java.io.Closeable; +import java.nio.CharBuffer; +import java.security.Provider; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +import jdk.internal.access.JavaSecurityProviderAccess; +import jdk.internal.access.SharedSecrets; +import sun.security.util.Debug; +import sun.security.util.SecurityProperties; + +public final class ProvidersFilter { + + private static final String FILTER_PROP = "jdk.security.providers.filter"; + + private static final Debug debug = Debug.getInstance("jca", + "ProvidersFilter"); + + private static final JavaSecurityProviderAccess jspa = SharedSecrets + .getJavaSecurityProviderAccess(); + + private static final class FilterDecision { + private enum Result { + DENY, + ALLOW, + UNDECIDED + } + private static final int UNDEFINED_PRIORITY = -1; + private static final FilterDecision UNDECIDED = new FilterDecision(); + private final Result result; + private final int priority; + + private FilterDecision() { + this.result = Result.UNDECIDED; + this.priority = UNDEFINED_PRIORITY; + } + + FilterDecision(Result result, int priority) { + assert result != Result.UNDECIDED : "Invalid result."; + assert priority >= 0 : "Invalid priority"; + this.result = result; + this.priority = priority; + } + + boolean isAllow() { + return result == ProvidersFilter.FilterDecision.Result.ALLOW; + } + + @Override + public String toString() { + return result + (priority != UNDEFINED_PRIORITY ? " - priority: " + + priority : ""); + } + + void debugDisplay() { + if (debug == null) { + return; + } + debug.println(" * Decision: " + this); + } + } + + private record FilterQuery(String provider, String svcType, + String svcAlgo) { + private FilterQuery { + assert provider != null && svcType != null && svcAlgo != null : + "Invalid FilterQuery."; + } + + @Override + public String toString() { + return "Service filter query (Provider: " + provider + + ", Service type: " + svcType + ", Algorithm: " + + svcAlgo + ")"; + } + } + + private static final class Filter { + private sealed interface Rule permits PatternRule, DefaultRule { + FilterDecision apply(FilterQuery q); + } + + private record PatternRuleComponent(Type type, String value, + Pattern regexp) { + enum Type { + PROVIDER("Provider"), + SVC_TYPE("Service type"), + SVC_ALGO("Algorithm"); + + private final String type; + + Type(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + } + + private static final Pattern ALL_PATTERN = Pattern.compile(".*"); + + static final PatternRuleComponent ANY_SVC_TYPE = + new PatternRuleComponent(Type.SVC_TYPE, "*", ALL_PATTERN); + + static final PatternRuleComponent ANY_SVC_ALGO = + new PatternRuleComponent(Type.SVC_ALGO, "*", ALL_PATTERN); + + PatternRuleComponent { + assert value != null && !value.isEmpty() && regexp != null : + "Invalid PatternRuleComponent instance."; + } + + @Override + public String toString() { + return value; + } + + void debugDisplay() { + if (debug == null) { + return; + } + debug.println(" * " + type + ": " + value + " (regexp: " + + regexp + ")"); + } + } + + private static final class PatternRule implements Rule { + private FilterDecision decision; + private PatternRuleComponent provider; + private PatternRuleComponent svcType; + private PatternRuleComponent svcAlgo; + + @Override + public FilterDecision apply(FilterQuery q) { + assert assertIsValid(); + if (provider.regexp.matcher(q.provider).matches() && + svcType.regexp.matcher(q.svcType).matches() && + svcAlgo.regexp.matcher(q.svcAlgo).matches()) { + return decision; + } + return FilterDecision.UNDECIDED; + } + + private boolean assertIsValid() { + assert decision.result != FilterDecision.Result.UNDECIDED : + "Invalid decision result."; + assert decision.priority != FilterDecision.UNDEFINED_PRIORITY : + "Invalid decision priority."; + assert provider != null : "Invalid provider."; + assert svcType != null : "Invalid service type."; + assert svcAlgo != null : "Invalid algorithm."; + return true; + } + + @Override + public String toString() { + return (decision.result == FilterDecision.Result.DENY ? "!" : + "") + provider + "." + svcType + "." + svcAlgo; + } + + void debugDisplay() { + if (debug == null) { + return; + } + provider.debugDisplay(); + svcType.debugDisplay(); + svcAlgo.debugDisplay(); + decision.debugDisplay(); + } + } + + private static final class DefaultRule implements Rule { + private final FilterDecision d; + + DefaultRule(int priority) { + d = new FilterDecision(FilterDecision.Result.DENY, priority); + } + + @Override + public FilterDecision apply(FilterQuery q) { + return d; + } + + @Override + public String toString() { + return "!* (DEFAULT)"; + } + } + + private static final class ParserException extends Exception { + @java.io.Serial + private static final long serialVersionUID = -6981287318167654426L; + + private static final String LN = System.lineSeparator(); + + private static final String HEADER_STR = " * Filter string: "; + + private static final String MORE_STR = "(...)"; + + private static final int MORE_TOTAL = MORE_STR.length() + 1; + + private static final int MAX_MARK = 7; + + private static final int MAX_LINE = 80; + + static { + assert MAX_LINE >= HEADER_STR.length() + (MORE_TOTAL * 2) + 1 + : "Not enough line space."; + } + + private static String addStateInfo(String message, Parser parser) { + StringBuilder sb = new StringBuilder(); + sb.append(message); + sb.append(LN); + sb.append(" * State: "); + sb.append(parser.state); + sb.append(LN); + renderFilterStr(parser.filterBuff.asReadOnlyBuffer(), sb); + return sb.toString(); + } + + private static void renderFilterStr(CharBuffer filterBuff, + StringBuilder sb) { + int filterBuffLen = filterBuff.limit(); + int cursor = filterBuff.position() - 1; + int preCutMark, postCutMark; + int lineAvailable = MAX_LINE - HEADER_STR.length() - 1; + int preAvailable = lineAvailable / 2; + int postAvailable = (lineAvailable + 1) / 2; + boolean preMore = false, postMore = false; + int preCursor, preSpaceCount, preDashCount, postDashCount; + + // Calculate the filter line + if (preAvailable < cursor) { + preMore = true; + preAvailable -= MORE_TOTAL; + } + if (postAvailable + cursor + 1 < filterBuffLen) { + postMore = true; + postAvailable -= MORE_TOTAL; + } + preCutMark = Math.max(0, cursor - preAvailable); + preAvailable -= cursor - preCutMark; + postCutMark = Math.min(filterBuffLen, cursor + 1 + + postAvailable); + postAvailable -= postCutMark - (cursor + 1); + if (postAvailable > 0 && preMore) { + if (preCutMark - (postAvailable + MORE_TOTAL) <= 0) { + postAvailable += MORE_TOTAL; + preMore = false; + } + preCutMark = Math.max(0, preCutMark - postAvailable); + } + if (preAvailable > 0 && postMore) { + if (postCutMark + preAvailable + MORE_TOTAL >= + filterBuffLen) { + preAvailable += MORE_TOTAL; + postMore = false; + } + postCutMark = Math.min(filterBuffLen, postCutMark + + preAvailable); + } + + // Calculate the underlining line + preCursor = HEADER_STR.length() + (preMore ? MORE_TOTAL : 0) + + cursor - preCutMark; + preSpaceCount = Math.max(0, preCursor - MAX_MARK/2); + preDashCount = Math.min(preCursor, MAX_MARK/2); + postDashCount = Math.min(MAX_LINE - 1 - preSpaceCount - + preDashCount, MAX_MARK/2); + + // Render the filter line + sb.append(HEADER_STR); + if (preMore) { + sb.append(MORE_STR); + sb.append(' '); + } + filterBuff.position(0); + sb.append(filterBuff, preCutMark, postCutMark); + if (postMore) { + sb.append(' '); + sb.append(MORE_STR); + } + sb.append(LN); + + // Render the underlining line + sb.append(" ".repeat(preSpaceCount)); + sb.append("-".repeat(preDashCount)); + sb.append("^"); + sb.append("-".repeat(postDashCount)); + sb.append(LN); + } + + ParserException(String message, Parser parser) { + super(addStateInfo(message, parser)); + } + } + + private static final class Parser { + private enum ParsingState { + PRE_PATTERN, + PRE_PATTERN_DENY, + PATTERN, + POST_PATTERN + } + + private enum Transition { + WHITESPACE_CHAR, + DENY_CHAR, + REGULAR_CHAR, + PATTERN_LEVEL_CHAR, + PATTERN_END_CHAR + } + + static List parse(String filterStr) throws ParserException { + return new Parser(filterStr).getRules(); + } + + private final CharBuffer filterBuff; + private final List rules; + private PatternRule rule; + private ParsingState state; + private final StringBuffer buff; + private final StringBuffer buffR; + private boolean escape; + private boolean quote; + + private Parser(String filterStr) throws ParserException { + filterBuff = CharBuffer.wrap(filterStr); + rules = new ArrayList<>(); + rule = new PatternRule(); + state = ParsingState.PRE_PATTERN; + buff = new StringBuffer(); + buffR = new StringBuffer(); + escape = false; + quote = false; + parse(); + } + + private List getRules() { + return rules; + } + + private PatternRuleComponent getComponent( + PatternRuleComponent.Type type) throws ParserException { + if (buff.isEmpty()) { + throw new ParserException("Missing " + + type.toString().toLowerCase() + " in " + + "pattern rule.", this); + } + if (quote) { + buffR.append("\\E"); + quote = false; + } + return new PatternRuleComponent(type, buff.toString(), + Pattern.compile(buffR.toString(), + Pattern.CASE_INSENSITIVE)); + } + + private void flushBuffers() throws ParserException { + if (rule.provider == null) { + rule.provider = getComponent( + PatternRuleComponent.Type.PROVIDER); + } else if (rule.svcType == null) { + rule.svcType = getComponent( + PatternRuleComponent.Type.SVC_TYPE); + } else if (rule.svcAlgo == null) { + rule.svcAlgo = getComponent( + PatternRuleComponent.Type.SVC_ALGO); + } else { + assert false : "Should not reach."; + } + buff.setLength(0); + buffR.setLength(0); + } + + private void endPattern() throws ParserException { + if (escape) { + throw new ParserException("Invalid escaping.", this); + } + flushBuffers(); + if (rule.svcType == null) { + rule.svcType = PatternRuleComponent.ANY_SVC_TYPE; + } + if (rule.svcAlgo == null) { + rule.svcAlgo = PatternRuleComponent.ANY_SVC_ALGO; + } + if (debug != null) { + debug.println("--------------------"); + debug.println("Rule parsed: " + rule); + rule.debugDisplay(); + } + rules.add(rule); + rule = new PatternRule(); + } + + /* + * Transition to the next state if there is a valid reason. If the + * reason is not valid, throw an exception. If there are no reasons + * to transition, stay in the same state. + */ + private void nextState(Transition transition) + throws ParserException { + if (state == ParsingState.PRE_PATTERN) { + if (transition == Transition.WHITESPACE_CHAR) { + // Stay in PRE_PATTERN state and ignore whitespaces + // at the beginning of a pattern: + // + // " Provider.ServiceType.Algorithm;" + // ^^^^ + // + // or + // + // " ! Provider.ServiceType.Algorithm;" + // ^^^^ + } else if (transition == Transition.REGULAR_CHAR) { + // Transition to PATTERN state: + // + // " Provider.ServiceType.Algorithm;" + // ^^^^ + state = ParsingState.PATTERN; + rule.decision = new FilterDecision( + FilterDecision.Result.ALLOW, rules.size()); + } else if (transition == Transition.DENY_CHAR) { + // Transition to PRE_PATTERN_DENY state: + // + // " ! Provider.ServiceType.Algorithm;" + // ^^^^ + state = ParsingState.PRE_PATTERN_DENY; + rule.decision = new FilterDecision( + FilterDecision.Result.DENY, rules.size()); + } else { + throw new ParserException("A pattern must start with " + + "a '!' or a security provider name.", this); + } + } else if (state == ParsingState.PRE_PATTERN_DENY) { + if (transition == Transition.WHITESPACE_CHAR) { + // Stay in PRE_PATTERN_DENY state and ignore whitespaces + // before the provider: + // + // " ! Provider.ServiceType.Algorithm;" + // ^^^^ + } else if (transition == Transition.REGULAR_CHAR) { + // Transition to PATTERN state: + // + // " ! Provider.ServiceType.Algorithm;" + // ^^^^ + state = ParsingState.PATTERN; + } else { + throw new ParserException("A pattern must have a " + + "security provider name after '!'.", this); + } + } else if (state == ParsingState.PATTERN) { + if (transition == Transition.REGULAR_CHAR) { + // Stay in PATTERN while the provider, service type + // and algorithm names fill up: + // + // " Provider.ServiceType.Algorithm;" + // ^^^^ + } else if (transition == Transition.WHITESPACE_CHAR) { + // Transition to POST_PATTERN state, after recording + // the parsed rule: + // + // " Provider.ServiceType.Algorithm ;" + // ^^^^ + endPattern(); + state = ParsingState.POST_PATTERN; + } else if (transition == Transition.PATTERN_END_CHAR) { + // Transition to PRE_PATTERN state, after recording + // the parsed rule: + // + // " Provider.ServiceType.Algorithm; Provider..." + // ^^^ + endPattern(); + state = ParsingState.PRE_PATTERN; + } else if (transition == Transition.PATTERN_LEVEL_CHAR) { + // Stay in PATTERN state while recording characters + // for the next level (service type or algorithm): + // + // " Provider.ServiceType.Algorithm;" + // ^^^^ + if (rule.svcType != null) { + throw new ParserException("Too many levels. Dots " + + "that are part of a provider name, " + + "service type or algorithm must be " + + "escaped.", this); + } + flushBuffers(); + } else { + throw new ParserException("Invalid name in pattern.", + this); + } + } else if (state == ParsingState.POST_PATTERN) { + if (transition == Transition.WHITESPACE_CHAR) { + // Stay in POST_PATTERN state and ignore whitespaces + // until the end of the pattern: + // + // " Provider.ServiceType.Algorithm ; Provider" + // ^^^^ + } else if (transition == Transition.PATTERN_END_CHAR) { + // Transition to PRE_PATTERN state: + // + // " Provider.ServiceType.Algorithm ; Provider" + // ^^^ + state = ParsingState.PRE_PATTERN; + } else { + throw new ParserException("Unescaped whitespaces are " + + "only valid at the end of a pattern. " + + "Whitespace characters internal to a " + + "provider name, service type or algorithm " + + "must be escaped.", this); + } + } else { + // Should not reach. + throw new RuntimeException("Unexpected Providers filter " + + "parser state."); + } + } + + private void appendChar(char c) { + if (c == '*' && !escape) { + // Character is a wildcard. + if (quote) { + buffR.append("\\E"); + quote = false; + } + buffR.append(".*"); + } else { + // Character is not a wildcard. + if (escape) { + buff.append("\\"); + } + if (!quote) { + buffR.append("\\Q"); + quote = true; + } + buffR.append(c); + if (c == '\\') { + // A '\' could be problematic because if an 'E' comes + // next the sequence "\E" would interfere with regexp + // quoting. Split these sequences into separated + // quoting units. I.e: "...\\E\QE...". + buffR.append("\\E\\Q"); + } + } + buff.append(c); + } + + private void parse() throws ParserException { + if (debug != null) { + debug.println("Parsing: " + filterBuff); + } + assert filterBuff.hasRemaining() : "Cannot parse an empty " + + "filter."; + while (filterBuff.hasRemaining()) { + char c = filterBuff.get(); + if (c == '\n' || c == '\0') { + throw new ParserException("Invalid filter character: " + + "'" + c + "'", this); + } else if (escape) { + appendChar(c); + escape = false; + } else if (c == '\\') { + nextState(Transition.REGULAR_CHAR); + escape = true; + } else if (c == '.') { + nextState(Transition.PATTERN_LEVEL_CHAR); + } else if (c == ';') { + nextState(Transition.PATTERN_END_CHAR); + } else if (Character.isWhitespace(c)) { + nextState(Transition.WHITESPACE_CHAR); + } else if (c == '!') { + nextState(Transition.DENY_CHAR); + } else if (c == ':' || c == ',') { + throw new ParserException("Reserved character '" + c + + "' must be escaped.", this); + } else { + nextState(Transition.REGULAR_CHAR); + appendChar(c); + } + } + if (state != ParsingState.PRE_PATTERN || rules.size() == 0) { + nextState(Transition.PATTERN_END_CHAR); + } + assert state == ParsingState.PRE_PATTERN : "Parser state " + + "must finish in PRE_PATTERN."; + } + } + + private final List rules; + + Filter(String filterStr) throws IllegalArgumentException { + try { + rules = Parser.parse(filterStr); + } catch (ParserException e) { + throw new IllegalArgumentException("Invalid Providers filter:" + + " " + filterStr, e); + } + rules.add(new DefaultRule(rules.size())); + } + + FilterDecision apply(FilterQuery q) { + for (Rule r : rules) { + FilterDecision d = r.apply(q); + if (d != FilterDecision.UNDECIDED) { + if (debug != null) { + debug.println("--------------------"); + debug.println(q.toString()); + debug.println(" * Decision: " + d); + debug.println(" * Made by: " + r); + } + return d; + } + } + // Should never reach this point: there is always a DefaultRule + // capable of deciding. + throw new RuntimeException("Unexpected Providers filter failure: " + + "decision not made."); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Filter: "); + Iterator ri = rules.iterator(); + while (ri.hasNext()) { + sb.append(ri.next()); + if (ri.hasNext()) { + sb.append("; "); + } + } + return sb.toString(); + } + } + + private static final Filter filter; + + static { + Filter tmpFilter = null; + String fStr = SecurityProperties.getOverridableProperty(FILTER_PROP); + if (debug != null) { + debug.println("Filter property value read at this point:"); + for (StackTraceElement ste : new Exception().getStackTrace()) { + debug.println(" ".repeat(4) + ste); + } + } + if (fStr != null && !fStr.isEmpty()) { + tmpFilter = new Filter(fStr); + } + filter = tmpFilter; + if (debug != null) { + debug.println(filter != null ? filter.toString() : "No filter"); + } + } + + /* + * This method has to be called every time that a Provider.Service instance + * is obtained with Provider::getService or Provider::getServices. + */ + public static boolean isAllowed(Provider.Service svc) { + if (filter == null) { + return true; + } + // For services added to the Provider's services map (most cases), this + // call is expected to be fast: only a Provider.Service field read. It + // might take longer on the first time for uncommon services (see + // Provider.Service::isAllowed). + return jspa.isAllowed(svc); + } + + /* + * This method is called from Provider.Service::computeSvcAllowed and + * Provider.Service::isTransformationAllowed. + */ + public static boolean computeSvcAllowed(String providerName, + String svcType, String algo, List aliases) { + if (filter == null) { + return true; + } + FilterDecision d = isAllowed(providerName, svcType, algo); + if (debug != null && aliases.size() > 0) { + debug.println("--------------------"); + debug.println("The queried service has aliases. Checking them " + + "for a final decision..."); + } + for (String algAlias : aliases) { + FilterDecision da = isAllowed(providerName, svcType, algAlias); + if (da.priority < d.priority) { + d = da; + if (debug != null) { + algo = algAlias; + } + } + } + if (debug != null && aliases.size() > 0) { + debug.println("--------------------"); + debug.println("Final decision based on " + algo + " algorithm" + + ": " + d); + } + return d.isAllow(); + } + + private static FilterDecision isAllowed(String provider, String svcType, + String svcAlgo) { + return filter.apply(new FilterQuery(provider, svcType, svcAlgo)); + } + + /* + * CipherContext is an auxiliary class to bundle information required by + * CipherTransformation. The field "transformation" is the ongoing Cipher + * transformation for which a service is being looked up. The field + * "svcSearchKey" is the key (algorithm or alias) used to look up a + * service that might support the transformation. + */ + public record CipherContext(String transformation, String svcSearchKey) {} + + /* + * CipherTransformation is used from the Cipher::tryGetService, + * Cipher::newInstance and ProviderList.CipherServiceIterator::tryGetService + * methods for a thread to indicate that a service will be looked up for a + * Cipher transformation. In these cases, the service evaluation against + * the Providers Filter is based on the transformation and not the service + * algorithm or aliases. Thus, a Filter value such as + * "*.Cipher.AES/ECB/PKCS5Padding; !*" would allow + * Cipher.getInstance("AES/ECB/PKCS5Padding") but block + * Cipher.getInstance("AES") even when the supporting service is the same. + */ + public static final class CipherTransformation implements Closeable { + private static final ThreadLocal cipherTransformContext = + new ThreadLocal<>(); + private CipherContext prevContext; + + public CipherTransformation(String transformation, + String svcSearchKey) { + if (filter == null) { + return; + } + prevContext = cipherTransformContext.get(); + if (!transformation.equalsIgnoreCase(svcSearchKey)) { + cipherTransformContext.set(new CipherContext( + transformation.toUpperCase(Locale.ENGLISH), + svcSearchKey)); + } else { + // The transformation matches the service algorithm or alias. + // Set the context to null to indicate that a regular service + // evaluation (not based on the transformation) should be done. + cipherTransformContext.set(null); + } + } + + /* + * This method is called from Provider.Service::isAllowed for a thread + * to get the CipherContext related to a service lookup. Returns + * null if 1) there is not an ongoing service lookup based on a Cipher + * transformation or 2) the transformation matches the service + * algorithm or any of its aliases. A regular service evaluation (not + * based on the transformation) should be done if null is returned. + */ + public static CipherContext getContext() { + if (filter == null) { + return null; + } + return cipherTransformContext.get(); + } + + @Override + public void close() { + if (filter == null) { + return; + } + cipherTransformContext.set(prevContext); + } + } +} diff --git a/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java b/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java index 2b38bc09733a1..ce8fb80d8fe4a 100644 --- a/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java +++ b/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -171,4 +171,47 @@ static Set decomposeName(String algorithm) { static String decomposeDigestName(String algorithm) { return DECOMPOSED_DIGEST_NAMES.getOrDefault(algorithm, algorithm); } + + private static final String SHA512TRUNCATED = "SHA512/2"; + + /** + * Split a Cipher transformation of the form algorithm/mode/padding, + * algorithm/mode or algorithm into its components. + * + * Components of a cipher transformation: + * + * 1) algorithm component (e.g., AES) + * 2) feedback component (e.g., CFB) - optional + * 3) padding component (e.g., PKCS5Padding) - optional + */ + public static String[] getTransformationTokens(String transformation) { + // check if the transformation contains algorithms with "/" in their + // name which can cause the parsing logic to go wrong + int sha512Idx = transformation.toUpperCase(Locale.ENGLISH) + .indexOf(SHA512TRUNCATED); + int startIdx = (sha512Idx == -1 ? 0 : + sha512Idx + SHA512TRUNCATED.length()); + int endIdx = transformation.indexOf('/', startIdx); + if (endIdx == -1) { + // algorithm + return new String[] { transformation.trim() }; + } else { + String algorithm; + String mode; + String padding; + algorithm = transformation.substring(0, endIdx).trim(); + startIdx = endIdx + 1; + endIdx = transformation.indexOf('/', startIdx); + if (endIdx == -1) { + // algorithm/mode + mode = transformation.substring(startIdx).trim(); + return new String[] { algorithm, mode }; + } else { + // algorithm/mode/padding + mode = transformation.substring(startIdx, endIdx).trim(); + padding = transformation.substring(endIdx + 1).trim(); + return new String[] { algorithm, mode, padding }; + } + } + } } diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 5d96d74539e1c..e0bce040ed043 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1575,3 +1575,106 @@ jdk.tls.alpnCharset=ISO_8859_1 # withEncryption method. # jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 + +# +# Security Providers Filter (JEP TBD) +# +# This filter can be used to constrain which services, implemented by installed +# security providers, are available for use in the getInstance JCA APIs (Cipher, +# Signature, Mac, KeyFactory, etc). Services filtering is independent of other +# mechanisms such as jdk.tls.disabledAlgorithms, jdk.jar.disabledAlgorithms and +# jdk.certpath.disabledAlgorithms. +# +# The scope of this filter includes services implemented by statically installed +# security providers (security.provider. 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 TerminalFactorys. * * @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 TerminalFactorys. * + * @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 TerminalFactorys. * + * @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 {