This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/WSS-660 in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit dc2b070ce91525bf801c608efb2f0af9192d4943 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Tue Nov 3 06:42:24 2026 +0000 WSS-660 - Only remove security providers on cleanUp that WSS4J registered --- .../wss4j/common/crypto/WSProviderConfig.java | 68 +++++++++++++++------- .../org/apache/wss4j/dom/engine/WSSConfig.java | 52 +++++++++++------ .../wss4j/dom/components/crypto/WSSConfigTest.java | 38 +++++++++++- 3 files changed, 119 insertions(+), 39 deletions(-) diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java index acdae3796..20067bd20 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/WSProviderConfig.java @@ -25,6 +25,8 @@ import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.Provider; import java.security.Security; +import java.util.HashMap; +import java.util.Map; import org.apache.wss4j.common.util.FIPSUtils; import org.apache.wss4j.common.util.Loader; @@ -56,9 +58,12 @@ public final class WSProviderConfig { */ private static boolean staticallyInitialized; - private static boolean santuarioProviderAdded; - private static boolean bcProviderAdded; - private static boolean tlProviderAdded; + /** + * The provider instances registered by this class, keyed by name. On cleanUp only these + * instances are removed, so that a provider of the same name registered by another copy + * of WSS4J (e.g. in another web application) is left alone. + */ + private static final Map<String, Provider> ADDED_PROVIDERS = new HashMap<>(); private WSProviderConfig() { // complete @@ -75,10 +80,6 @@ public final class WSProviderConfig { return true; } }); - - santuarioProviderAdded = true; - bcProviderAdded = false; - tlProviderAdded = false; } if (FIPSUtils.isFIPSEnabled()) { //So far the in-JDK security provider in FIPS mode @@ -99,7 +100,6 @@ public final class WSProviderConfig { if (!staticallyInitialized) { initializeResourceBundles(); setXmlSecIgnoreLineBreak(); - santuarioProviderAdded = addXMLDSigRIInternalProv; if (addXMLDSigRIInternalProv) { AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { @@ -109,11 +109,12 @@ public final class WSProviderConfig { }); } - bcProviderAdded = addBCProv; if (addBCProv) { AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { + Provider existing = Security.getProvider("BC"); addJceProvider("BC", "org.bouncycastle.jce.provider.BouncyCastleProvider"); + recordAddedProvider("BC", existing); return true; } }); @@ -133,11 +134,12 @@ public final class WSProviderConfig { } - tlProviderAdded = addTLProv; if (addTLProv) { AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { + Provider existing = Security.getProvider("TLSP"); ThreadLocalSecurityProvider.install(); + recordAddedProvider("TLSP", existing); return true; } }); @@ -148,18 +150,10 @@ public final class WSProviderConfig { public static synchronized void cleanUp() { if (staticallyInitialized) { - if (santuarioProviderAdded) { - Security.removeProvider("ApacheXMLDSig"); - santuarioProviderAdded = false; - } - if (bcProviderAdded) { - Security.removeProvider("BC"); - bcProviderAdded = false; - } - if (tlProviderAdded) { - Security.removeProvider("TLSP"); - tlProviderAdded = false; + for (Map.Entry<String, Provider> entry : ADDED_PROVIDERS.entrySet()) { + removeJceProvider(entry.getKey(), entry.getValue()); } + ADDED_PROVIDERS.clear(); staticallyInitialized = false; } @@ -212,8 +206,40 @@ public final class WSProviderConfig { } private static void addXMLDSigRIInternal() { + Provider existing = Security.getProvider("ApacheXMLDSig"); Security.removeProvider("ApacheXMLDSig"); addJceProvider("ApacheXMLDSig", SantuarioUtil.getSantuarioProvider()); + recordAddedProvider("ApacheXMLDSig", existing); + } + + private static void recordAddedProvider(String name, Provider previous) { + Provider current = Security.getProvider(name); + if (current != null && current != previous) { + ADDED_PROVIDERS.put(name, current); + } + } + + /** + * Remove the JCE security provider registered under the given name, but only if it is the + * given provider instance. A provider of the same name registered by someone else, e.g. by + * a copy of WSS4J loaded by another web application's class loader, is left in place. + * + * @param name + * The name of the provider + * @param provider + * The provider instance that was registered by the caller + * @return true if the provider was removed + */ + public static boolean removeJceProvider(String name, Provider provider) { + Provider current = Security.getProvider(name); + if (current != null && current == provider) { + Security.removeProvider(name); + return true; + } + if (current != null) { + LOG.debug("Not removing provider {} as it was not registered by this instance of WSS4J", name); + } + return false; } private static void initializeResourceBundles() { diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/engine/WSSConfig.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/engine/WSSConfig.java index 733c99eff..c13201bad 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/engine/WSSConfig.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/engine/WSSConfig.java @@ -21,6 +21,7 @@ package org.apache.wss4j.dom.engine; import java.security.AccessController; import java.security.PrivilegedAction; +import java.security.Provider; import java.security.Security; import java.util.HashMap; import java.util.Map; @@ -253,6 +254,13 @@ public final class WSSConfig { */ private static boolean staticallyInitialized = false; + /** + * The provider instances registered by this class, keyed by name. On cleanUp only these + * instances are removed, so that a provider of the same name registered by another copy + * of WSS4J (e.g. in another web application) is left alone. + */ + private static final Map<String, Provider> ADDED_PROVIDERS = new HashMap<>(); + /** * This allows the user to specify a different time than that of the current System time. */ @@ -318,24 +326,18 @@ public final class WSSConfig { if (addJceProviders) { AccessController.doPrivileged(new PrivilegedAction<Boolean>() { public Boolean run() { - Security.removeProvider("STRTransform"); - WSProviderConfig.appendJceProvider( + replaceJceProvider( "STRTransform", new org.apache.wss4j.dom.transform.STRTransformProvider() ); - - Security.removeProvider("AttachmentContentSignatureTransform"); - WSProviderConfig.appendJceProvider( - "AttachmentContentSignatureTransform", - new AttachmentContentSignatureTransformProvider() + replaceJceProvider( + "AttachmentContentSignatureTransform", + new AttachmentContentSignatureTransformProvider() ); - - Security.removeProvider("AttachmentCompleteSignatureTransform"); - WSProviderConfig.appendJceProvider( - "AttachmentCompleteSignatureTransform", - new AttachmentCompleteSignatureTransformProvider() + replaceJceProvider( + "AttachmentCompleteSignatureTransform", + new AttachmentCompleteSignatureTransformProvider() ); - return true; } }); @@ -347,17 +349,33 @@ public final class WSSConfig { public static synchronized void cleanUp() { if (staticallyInitialized) { - if (addJceProviders) { - Security.removeProvider("STRTransform"); - Security.removeProvider("AttachmentContentSignatureTransform"); - Security.removeProvider("AttachmentCompleteSignatureTransform"); + for (Map.Entry<String, Provider> entry : ADDED_PROVIDERS.entrySet()) { + WSProviderConfig.removeJceProvider(entry.getKey(), entry.getValue()); } + ADDED_PROVIDERS.clear(); WSProviderConfig.cleanUp(); staticallyInitialized = false; } } + private static void replaceJceProvider(String name, Provider provider) { + Provider existing = Security.getProvider(name); + if (existing != null + && existing.getClass().getClassLoader() != provider.getClass().getClassLoader()) { + LOG.warn("The security provider {} has already been registered by a different class loader " + + "and will be replaced. If WSS4J is deployed in more than one web application in the same " + + "JVM, the other application may fail to use it. Consider deploying WSS4J in a class loader " + + "shared by all applications, e.g. the container's lib directory.", name); + } + Security.removeProvider(name); + WSProviderConfig.appendJceProvider(name, provider); + Provider current = Security.getProvider(name); + if (current != null && current != existing) { + ADDED_PROVIDERS.put(name, current); + } + } + /** * @return a new WSSConfig instance configured with the default values */ diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/components/crypto/WSSConfigTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/components/crypto/WSSConfigTest.java index d46c5f739..fb897a831 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/components/crypto/WSSConfigTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/components/crypto/WSSConfigTest.java @@ -19,14 +19,18 @@ package org.apache.wss4j.dom.components.crypto; +import java.security.Provider; import java.security.Security; +import org.apache.wss4j.common.crypto.SantuarioUtil; import org.apache.wss4j.common.crypto.WSProviderConfig; import org.apache.wss4j.dom.engine.WSSConfig; +import org.apache.wss4j.dom.transform.AttachmentContentSignatureTransformProvider; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; /** * Test loading and removing security providers via WSSConfig @@ -78,4 +82,36 @@ public class WSSConfigTest { } -} \ No newline at end of file + /** + * WSS-660: cleanUp must not remove providers that were registered by someone else, e.g. + * by a copy of WSS4J deployed in another web application, after we registered ours. + */ + @Test + public void testCleanUpLeavesForeignProviders() { + WSSConfig.cleanUp(); + WSSConfig.init(); + + // Simulate another copy of WSS4J replacing the providers + Provider otherTransformProvider = new AttachmentContentSignatureTransformProvider(); + Security.removeProvider("AttachmentContentSignatureTransform"); + Security.addProvider(otherTransformProvider); + + Provider otherSantuarioProvider = SantuarioUtil.getSantuarioProvider(); + Security.removeProvider("ApacheXMLDSig"); + Security.addProvider(otherSantuarioProvider); + + try { + WSSConfig.cleanUp(); + + assertSame(otherTransformProvider, Security.getProvider("AttachmentContentSignatureTransform")); + assertSame(otherSantuarioProvider, Security.getProvider("ApacheXMLDSig")); + // The providers that were still ours are removed + assertNull(Security.getProvider("STRTransform")); + assertNull(Security.getProvider("AttachmentCompleteSignatureTransform")); + } finally { + Security.removeProvider("AttachmentContentSignatureTransform"); + Security.removeProvider("ApacheXMLDSig"); + } + } + +}
