This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/WSS-729 in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit 470124c4f892027a696a7391f4ecac641f64380c Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Wed Sep 9 15:59:24 2026 +0100 WSS-729 Switch default JasyptPasswordEncryptor algorithm to PBEWithHmacSHA512AndAES_256 --- .../common/crypto/JasyptPasswordEncryptor.java | 61 ++++++++++++++++++---- .../wss4j/common/crypto/PasswordEncryptorTest.java | 37 +++++++++++++ .../wss4j/dom/message/PasswordEncryptorTest.java | 14 +++++ .../wss4j/stax/test/PasswordEncryptorTest.java | 14 +++++ 4 files changed, 115 insertions(+), 11 deletions(-) diff --git a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/JasyptPasswordEncryptor.java b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/JasyptPasswordEncryptor.java index 68ed78aa4..557276ee0 100644 --- a/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/JasyptPasswordEncryptor.java +++ b/ws-security-common/src/main/java/org/apache/wss4j/common/crypto/JasyptPasswordEncryptor.java @@ -34,13 +34,32 @@ import org.jasypt.salt.RandomSaltGenerator; /** * An implementation of PasswordEncryptor that relies on Jasypt's StandardPBEStringEncryptor to - * encrypt and decrypt passwords. The default algorithm that is used is "PBEWithMD5AndTripleDES". + * encrypt and decrypt passwords. The default algorithm that is used is + * "PBEWithHmacSHA512AndAES_256". Values encrypted under the previous non-FIPS default + * ("PBEWithMD5AndTripleDES") can still be decrypted by setting the + * "org.apache.wss4j.crypto.jasypt.useLegacyDefaultAlgorithm" system property to "true", or by + * passing that algorithm to the constructor explicitly - but the legacy algorithm (an MD5-based + * PKCS#5 v1.5 KDF with 3DES) is weak against offline dictionary attack and values should be + * re-encrypted under the current default. */ public class JasyptPasswordEncryptor implements PasswordEncryptor { - public static final String DEFAULT_ALGORITHM = - FIPSUtils.isFIPSEnabled() - ? "PBEWithHmacSHA512AndAES_256" : "PBEWithMD5AndTripleDES"; + /** + * The default algorithm prior to WSS4J adopting "PBEWithHmacSHA512AndAES_256" + * universally (it was previously only the default when FIPS mode was enabled). + */ + public static final String LEGACY_DEFAULT_ALGORITHM = "PBEWithMD5AndTripleDES"; + + /** + * System property to restore {@link #LEGACY_DEFAULT_ALGORITHM} as the default + * algorithm (ignored in FIPS mode), for compatibility with values encrypted under + * previous releases. The property is read each time an instance is constructed + * without an explicit algorithm, not once at class-loading time. + */ + public static final String USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY = + "org.apache.wss4j.crypto.jasypt.useLegacyDefaultAlgorithm"; + + public static final String DEFAULT_ALGORITHM = "PBEWithHmacSHA512AndAES_256"; private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(JasyptPasswordEncryptor.class); @@ -49,31 +68,51 @@ public class JasyptPasswordEncryptor implements PasswordEncryptor { private CallbackHandler callbackHandler; public JasyptPasswordEncryptor(String password) { - this(password, DEFAULT_ALGORITHM); + this(password, defaultAlgorithm()); } public JasyptPasswordEncryptor(String password, String algorithm) { passwordEncryptor = new StandardPBEStringEncryptor(); passwordEncryptor.setPassword(password); passwordEncryptor.setAlgorithm(algorithm); - if (FIPSUtils.isFIPSEnabled()) { - passwordEncryptor.setSaltGenerator(new RandomSaltGenerator("PKCS11")); - passwordEncryptor.setIvGenerator(new RandomIvGenerator("PKCS11")); - } + configureGenerators(algorithm); } public JasyptPasswordEncryptor(CallbackHandler callbackHandler) { - this(callbackHandler, DEFAULT_ALGORITHM); + this(callbackHandler, defaultAlgorithm()); } public JasyptPasswordEncryptor(CallbackHandler callbackHandler, String algorithm) { passwordEncryptor = new StandardPBEStringEncryptor(); passwordEncryptor.setAlgorithm(algorithm); + configureGenerators(algorithm); + this.callbackHandler = callbackHandler; + } + + /** + * Resolve the algorithm to use when none is given explicitly, honoring + * {@link #USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY} at construction time (outside FIPS mode). + */ + private static String defaultAlgorithm() { + if (!FIPSUtils.isFIPSEnabled() + && Boolean.parseBoolean(System.getProperty(USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY, "false"))) { + return LEGACY_DEFAULT_ALGORITHM; + } + return DEFAULT_ALGORITHM; + } + + private void configureGenerators(String algorithm) { if (FIPSUtils.isFIPSEnabled()) { passwordEncryptor.setSaltGenerator(new RandomSaltGenerator("PKCS11")); passwordEncryptor.setIvGenerator(new RandomIvGenerator("PKCS11")); + } else if (requiresIv(algorithm)) { + // AES-based PBE algorithms need an explicit IV generator with Jasypt + passwordEncryptor.setIvGenerator(new RandomIvGenerator()); } - this.callbackHandler = callbackHandler; + } + + private static boolean requiresIv(String algorithm) { + return algorithm != null && algorithm.toUpperCase(java.util.Locale.ROOT).contains("AES"); } /** diff --git a/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/PasswordEncryptorTest.java b/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/PasswordEncryptorTest.java index ead1899ac..310c737af 100644 --- a/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/PasswordEncryptorTest.java +++ b/ws-security-common/src/test/java/org/apache/wss4j/common/crypto/PasswordEncryptorTest.java @@ -40,4 +40,41 @@ public class PasswordEncryptorTest { assertEquals(decryptedPassword, "password"); } + @Test + public void testJasyptDefaultAlgorithm() throws Exception { + // The strong algorithm is the default on both the FIPS and non-FIPS paths, and + // round-trips without needing any pre-encrypted fixture + assertEquals(JasyptPasswordEncryptor.DEFAULT_ALGORITHM, "PBEWithHmacSHA512AndAES_256"); + + PasswordEncryptor passwordEncryptor = + new JasyptPasswordEncryptor("master-password"); + String encryptedPassword = passwordEncryptor.encrypt("password"); + assertNotEquals(encryptedPassword, "password"); + String decryptedPassword = passwordEncryptor.decrypt(encryptedPassword); + assertEquals(decryptedPassword, "password"); + } + + @Test + public void testJasyptLegacyDefaultAlgorithmOptIn() throws Exception { + // A value encrypted under the previous default algorithm... + PasswordEncryptor legacyEncryptor = + new JasyptPasswordEncryptor("master-password", + JasyptPasswordEncryptor.LEGACY_DEFAULT_ALGORITHM); + String legacyEncryptedPassword = legacyEncryptor.encrypt("password"); + assertNotEquals(legacyEncryptedPassword, "password"); + + // ...can still be decrypted with the default constructor by opting in via the system + // property. The class is already loaded at this point, so this also checks that the + // property is honored at construction time rather than at class-loading time. + try { + System.setProperty(JasyptPasswordEncryptor.USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY, "true"); + PasswordEncryptor passwordEncryptor = + new JasyptPasswordEncryptor("master-password"); + String decryptedPassword = passwordEncryptor.decrypt(legacyEncryptedPassword); + assertEquals(decryptedPassword, "password"); + } finally { + System.clearProperty(JasyptPasswordEncryptor.USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY); + } + } + } \ No newline at end of file diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/PasswordEncryptorTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/PasswordEncryptorTest.java index cf068c397..b4b6b5fcc 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/PasswordEncryptorTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/PasswordEncryptorTest.java @@ -45,6 +45,8 @@ import org.apache.wss4j.dom.handler.RequestData; import org.apache.wss4j.dom.handler.WSHandlerConstants; import org.apache.wss4j.dom.handler.WSHandlerResult; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; @@ -65,6 +67,18 @@ public class PasswordEncryptorTest { new JasyptPasswordEncryptor("this-is-a-secret"); private Crypto crypto; + // The ENC() value in crypto_enc.properties was encrypted under the previous default + // algorithm (PBEWithMD5AndTripleDES), so opt in to the legacy default for this class + @BeforeAll + public static void setUpLegacyDefaultAlgorithm() { + System.setProperty(JasyptPasswordEncryptor.USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY, "true"); + } + + @AfterAll + public static void tearDownLegacyDefaultAlgorithm() { + System.clearProperty(JasyptPasswordEncryptor.USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY); + } + public PasswordEncryptorTest() throws Exception { WSSConfig.init(); Properties properties = diff --git a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/PasswordEncryptorTest.java b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/PasswordEncryptorTest.java index c55bce821..5820dcd82 100644 --- a/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/PasswordEncryptorTest.java +++ b/ws-security-stax/src/test/java/org/apache/wss4j/stax/test/PasswordEncryptorTest.java @@ -41,6 +41,8 @@ import org.apache.wss4j.stax.setup.OutboundWSSec; import org.apache.wss4j.stax.setup.WSSec; import org.apache.wss4j.stax.test.utils.XmlReaderToWriter; import org.apache.xml.security.stax.securityEvent.SecurityEvent; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -58,6 +60,18 @@ import static org.junit.jupiter.api.Assertions.fail; */ public class PasswordEncryptorTest extends AbstractTestBase { + // The ENC() value in transmitter-crypto-enc.properties was encrypted under the previous + // default algorithm (PBEWithMD5AndTripleDES), so opt in to the legacy default for this class + @BeforeAll + public static void setUpLegacyDefaultAlgorithm() { + System.setProperty(JasyptPasswordEncryptor.USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY, "true"); + } + + @AfterAll + public static void tearDownLegacyDefaultAlgorithm() { + System.clearProperty(JasyptPasswordEncryptor.USE_LEGACY_DEFAULT_ALGORITHM_PROPERTY); + } + @Test public void testSignatureCryptoPropertiesOutbound() throws Exception {
