This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new f4a8ae5cad  [SYNCOPE-1932] Improve AES management  (#1241)
f4a8ae5cad is described below

commit f4a8ae5cad9d574e7d333cd8d4354ded91f47381
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Wed Nov 19 13:13:33 2025 +0100

     [SYNCOPE-1932] Improve AES management  (#1241)
---
 .../syncope/core/logic/AccessTokenLogic.java       |   5 +-
 .../persistence/jpa/PersistenceTestContext.java    |   5 +-
 .../src/test/resources/core-test.properties        |   2 +-
 .../persistence/neo4j/PersistenceTestContext.java  |   5 +-
 .../src/test/resources/core-test.properties        |   2 +-
 .../java/data/AnyTypeDataBinderImpl.java           |  14 ++-
 .../provisioning/java/utils/ConnObjectUtils.java   |   4 +-
 .../src/test/resources/core-test.properties        |   2 +-
 .../src/test/resources/core-debug.properties       |   2 +-
 .../core/spring/security/DefaultEncryptor.java     | 103 ++++++++++++---------
 .../spring/security/DefaultEncryptorManager.java   |  12 ++-
 .../core/spring/security/SecurityContext.java      |  11 +--
 .../core/spring/security/SecurityProperties.java   |  10 +-
 .../core/spring/SpringTestConfiguration.java       |   7 +-
 .../core/spring/security/DefaultEncryptorTest.java |  12 ++-
 core/starter/src/main/resources/core.properties    |   9 +-
 .../workflow/java/AbstractUserWorkflowAdapter.java |   4 +-
 .../src/main/resources/core-embedded.properties    |   2 +-
 .../org/apache/syncope/fit/AbstractITCase.java     |   8 +-
 .../apache/syncope/fit/core/KeymasterITCase.java   |   4 +-
 .../configuration/configurationparameters.adoc     |  15 +--
 21 files changed, 146 insertions(+), 92 deletions(-)

diff --git 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
index 3e4b17f7fe..f2861a0e65 100644
--- 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
+++ 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AccessTokenLogic.java
@@ -63,9 +63,8 @@ public class AccessTokenLogic extends 
AbstractTransactionalLogic<AccessTokenTO>
     protected byte[] getAuthorities() {
         byte[] authorities = null;
         try {
-            authorities = 
encryptorManager.getInstance().encode(POJOHelper.serialize(
-                    AuthContextUtils.getAuthorities()), CipherAlgorithm.AES).
-                    getBytes();
+            authorities = encryptorManager.getInstance().
+                    
encode(POJOHelper.serialize(AuthContextUtils.getAuthorities()), 
CipherAlgorithm.AES).getBytes();
         } catch (Exception e) {
             LOG.error("Could not fetch authorities", e);
         }
diff --git 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
index 204076a950..3c8d31286d 100644
--- 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
+++ 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa;
 
 import jakarta.persistence.EntityManagerFactory;
 import javax.sql.DataSource;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.JPADomain;
@@ -115,7 +116,9 @@ public class PersistenceTestContext {
 
     @Bean
     public EncryptorManager encryptorManager() {
-        return new DefaultEncryptorManager();
+        SecurityProperties securityProperties = new SecurityProperties();
+        securityProperties.setAesSecretKey(StringUtils.EMPTY);
+        return new DefaultEncryptorManager(securityProperties);
     }
 
     @Bean
diff --git a/core/persistence-jpa/src/test/resources/core-test.properties 
b/core/persistence-jpa/src/test/resources/core-test.properties
index 1b3f95feeb..d904f8db5c 100644
--- a/core/persistence-jpa/src/test/resources/core-test.properties
+++ b/core/persistence-jpa/src/test/resources/core-test.properties
@@ -18,7 +18,7 @@
 security.adminUser=${adminUser}
 security.anonymousUser=${anonymousUser}
 security.jwsKey=${jwsKey}
-security.secretKey=${secretKey}
+security.aesSecretKey=${secretKey}
 
 persistence.db-type=${DB_TYPE}
 
diff --git 
a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java
 
b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java
index e20638c87c..29cb7fe845 100644
--- 
a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java
+++ 
b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.neo4j;
 
 import javax.cache.CacheManager;
 import javax.cache.Caching;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain;
@@ -107,7 +108,9 @@ public class PersistenceTestContext {
 
     @Bean
     public EncryptorManager encryptorManager() {
-        return new DefaultEncryptorManager();
+        SecurityProperties securityProperties = new SecurityProperties();
+        securityProperties.setAesSecretKey(StringUtils.EMPTY);
+        return new DefaultEncryptorManager(securityProperties);
     }
 
     @Bean
diff --git a/core/persistence-neo4j/src/test/resources/core-test.properties 
b/core/persistence-neo4j/src/test/resources/core-test.properties
index 4df24339aa..ba76db08db 100644
--- a/core/persistence-neo4j/src/test/resources/core-test.properties
+++ b/core/persistence-neo4j/src/test/resources/core-test.properties
@@ -18,7 +18,7 @@
 security.adminUser=${adminUser}
 security.anonymousUser=${anonymousUser}
 security.jwsKey=${jwsKey}
-security.secretKey=${secretKey}
+security.aesSecretKey=${secretKey}
 
 persistence.domain[0].key=Master
 persistence.domain[0].uri=bolt://${NEO4J_CONTAINER_IP}:7687/
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
index 1b2f9a840d..111bbda798 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
@@ -96,9 +96,8 @@ public class AnyTypeDataBinderImpl implements 
AnyTypeDataBinder {
 
                 added.forEach(e -> authorities.add(new 
SyncopeGrantedAuthority(e, SyncopeConstants.ROOT_REALM)));
 
-                
accessToken.setAuthorities(encryptorManager.getInstance().encode(
-                        POJOHelper.serialize(authorities), 
CipherAlgorithm.AES).
-                        getBytes());
+                accessToken.setAuthorities(encryptorManager.getInstance().
+                        encode(POJOHelper.serialize(authorities), 
CipherAlgorithm.AES).getBytes());
 
                 accessTokenDAO.save(accessToken);
             } catch (Exception e) {
@@ -142,17 +141,16 @@ public class AnyTypeDataBinderImpl implements 
AnyTypeDataBinder {
                     orElseThrow(() -> new NotFoundException("AccessToken for " 
+ AuthContextUtils.getUsername()));
             try {
                 Set<SyncopeGrantedAuthority> authorities = new 
HashSet<>(POJOHelper.deserialize(
-                        encryptorManager.getInstance().decode(
-                                new String(accessToken.getAuthorities()), 
CipherAlgorithm.AES),
+                        encryptorManager.getInstance().
+                                decode(new 
String(accessToken.getAuthorities()), CipherAlgorithm.AES),
                         new TypeReference<Set<SyncopeGrantedAuthority>>() {
                 }));
 
                 authorities.removeAll(authorities.stream().
                         filter(authority -> 
removed.contains(authority.getAuthority())).toList());
 
-                
accessToken.setAuthorities(encryptorManager.getInstance().encode(
-                        POJOHelper.serialize(authorities), 
CipherAlgorithm.AES).
-                        getBytes());
+                accessToken.setAuthorities(encryptorManager.getInstance().
+                        encode(POJOHelper.serialize(authorities), 
CipherAlgorithm.AES).getBytes());
 
                 accessTokenDAO.save(accessToken);
             } catch (Exception e) {
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
index bde38d1b7f..6f91109ab3 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
@@ -269,8 +269,8 @@ public class ConnObjectUtils {
                 // update password if and only if password is really changed
                 User user = userDAO.authFind(key);
                 if (StringUtils.isBlank(updatedUser.getPassword())
-                        || 
encryptorManager.getInstance().verify(updatedUser.getPassword(),
-                                user.getCipherAlgorithm(), 
user.getPassword())) {
+                        || encryptorManager.getInstance().
+                                verify(updatedUser.getPassword(), 
user.getCipherAlgorithm(), user.getPassword())) {
 
                     updatedUser.setPassword(null);
                 }
diff --git a/core/provisioning-java/src/test/resources/core-test.properties 
b/core/provisioning-java/src/test/resources/core-test.properties
index 6458168bbc..10da9c173c 100644
--- a/core/provisioning-java/src/test/resources/core-test.properties
+++ b/core/provisioning-java/src/test/resources/core-test.properties
@@ -18,7 +18,7 @@
 security.adminUser=${adminUser}
 security.anonymousUser=${anonymousUser}
 security.jwsKey=${jwsKey}
-security.secretKey=${secretKey}
+security.aesSecretKey=${secretKey}
 
 persistence.domain[0].key=Master
 persistence.domain[0].jdbcDriver=org.postgresql.Driver
diff --git 
a/core/self-keymaster-starter/src/test/resources/core-debug.properties 
b/core/self-keymaster-starter/src/test/resources/core-debug.properties
index 05e963d862..4faa0d847c 100644
--- a/core/self-keymaster-starter/src/test/resources/core-debug.properties
+++ b/core/self-keymaster-starter/src/test/resources/core-debug.properties
@@ -29,7 +29,7 @@ keymaster.password=${anonymousKey}
 security.adminUser=${adminUser}
 security.anonymousUser=${anonymousUser}
 security.jwsKey=${jwsKey}
-security.secretKey=${secretKey}
+security.aesSecretKey=${secretKey}
 
 persistence.domain[0].key=Master
 persistence.domain[0].jdbcDriver=org.postgresql.Driver
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptor.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptor.java
index 4da2b719f7..7b0824ba87 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptor.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptor.java
@@ -23,15 +23,15 @@ import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.Base64;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
 import javax.crypto.spec.SecretKeySpec;
-import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
-import org.apache.syncope.core.persistence.api.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.Encryptor;
 import org.jasypt.commons.CommonUtils;
 import org.jasypt.digest.StandardStringDigester;
@@ -43,33 +43,58 @@ public class DefaultEncryptor implements Encryptor {
 
     protected static final Logger LOG = 
LoggerFactory.getLogger(DefaultEncryptor.class);
 
-    protected static final String DEFAULT_SECRET_KEY = 
"1abcdefghilmnopqrstuvz2!";
+    protected final SecurityProperties.DigesterProperties digesterProperties;
 
     protected final Map<CipherAlgorithm, StandardStringDigester> digesters = 
new ConcurrentHashMap<>();
 
-    protected SecretKeySpec keySpec;
+    protected final Optional<SecretKeySpec> aesKeySpec;
 
-    protected DefaultEncryptor(final String secretKey) {
-        String actualKey = secretKey;
-        if (actualKey.length() < 16) {
-            StringBuilder actualKeyPadding = new StringBuilder(actualKey);
-            int length = 16 - actualKey.length();
-            String randomChars = 
SecureRandomUtils.generateRandomPassword(length);
+    protected DefaultEncryptor(
+            final String aesSecretKey,
+            final SecurityProperties.DigesterProperties digesterProperties) {
 
-            actualKeyPadding.append(randomChars);
-            actualKey = actualKeyPadding.toString();
-            LOG.warn("The secret key is too short (< 16), adding some random 
characters. "
-                    + "Passwords encrypted with AES and this key will not be 
recoverable "
-                    + "as a result if the container is restarted.");
-        }
+        this.digesterProperties = digesterProperties;
 
-        try {
-            keySpec = new SecretKeySpec(ArrayUtils.subarray(
-                    actualKey.getBytes(StandardCharsets.UTF_8), 0, 16),
-                    CipherAlgorithm.AES.getAlgorithm());
-        } catch (Exception e) {
-            LOG.error("Error during key specification", e);
+        SecretKeySpec sks = null;
+
+        if (StringUtils.isNotBlank(aesSecretKey)) {
+            String actualKey = aesSecretKey;
+
+            Integer pad = null;
+            boolean truncate = false;
+            if (actualKey.length() < 16) {
+                pad = 16 - actualKey.length();
+            } else if (actualKey.length() > 16 && actualKey.length() < 24) {
+                pad = 24 - actualKey.length();
+            } else if (actualKey.length() > 24 && actualKey.length() < 32) {
+                pad = 32 - actualKey.length();
+            } else if (actualKey.length() > 32) {
+                truncate = true;
+            }
+
+            if (pad != null) {
+                StringBuilder actualKeyPadding = new StringBuilder(actualKey);
+                String randomChars = 
SecureRandomUtils.generateRandomPassword(pad);
+
+                actualKeyPadding.append(randomChars);
+                actualKey = actualKeyPadding.toString();
+                LOG.warn("The configured AES secret key is too short (< {}), 
padding with random chars: {}",
+                        actualKey.length(), actualKey);
+            }
+            if (truncate) {
+                actualKey = actualKey.substring(0, 32);
+                LOG.warn("The configured AES secret key is too long (> 32), 
truncating: {}", actualKey);
+            }
+
+            try {
+                sks = new 
SecretKeySpec(actualKey.getBytes(StandardCharsets.UTF_8), 
CipherAlgorithm.AES.getAlgorithm());
+                LOG.debug("AES-{} successfully configured", actualKey.length() 
* 8);
+            } catch (Exception e) {
+                LOG.error("Error during key specification", e);
+            }
         }
+
+        aesKeySpec = Optional.ofNullable(sks);
     }
 
     @Override
@@ -82,7 +107,8 @@ public class DefaultEncryptor implements Encryptor {
         if (value != null) {
             if (cipherAlgorithm == null || cipherAlgorithm == 
CipherAlgorithm.AES) {
                 Cipher cipher = 
Cipher.getInstance(CipherAlgorithm.AES.getAlgorithm());
-                cipher.init(Cipher.ENCRYPT_MODE, keySpec);
+                cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec.
+                        orElseThrow(() -> new IllegalArgumentException("AES 
not configured")));
 
                 encoded = 
Base64.getEncoder().encodeToString(cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)));
             } else if (cipherAlgorithm == CipherAlgorithm.BCRYPT) {
@@ -125,7 +151,8 @@ public class DefaultEncryptor implements Encryptor {
 
         if (encoded != null && cipherAlgorithm == CipherAlgorithm.AES) {
             Cipher cipher = 
Cipher.getInstance(CipherAlgorithm.AES.getAlgorithm());
-            cipher.init(Cipher.DECRYPT_MODE, keySpec);
+            cipher.init(Cipher.DECRYPT_MODE, aesKeySpec.
+                    orElseThrow(() -> new IllegalArgumentException("AES not 
configured")));
 
             decoded = new 
String(cipher.doFinal(Base64.getDecoder().decode(encoded)), 
StandardCharsets.UTF_8);
         }
@@ -133,25 +160,20 @@ public class DefaultEncryptor implements Encryptor {
         return decoded;
     }
 
-    private StandardStringDigester getDigester(final CipherAlgorithm 
cipherAlgorithm) {
-        StandardStringDigester digester = digesters.get(cipherAlgorithm);
-        if (digester == null) {
-            digester = new StandardStringDigester();
+    protected StandardStringDigester getDigester(final CipherAlgorithm 
cipherAlgorithm) {
+        return digesters.computeIfAbsent(cipherAlgorithm, k -> {
+            StandardStringDigester digester = new StandardStringDigester();
 
             if (cipherAlgorithm.getAlgorithm().startsWith("S-")) {
-                SecurityProperties securityProperties =
-                        
ApplicationContextProvider.getApplicationContext().getBean(SecurityProperties.class);
-
                 // Salted ...
                 
digester.setAlgorithm(cipherAlgorithm.getAlgorithm().replaceFirst("S\\-", ""));
-                
digester.setIterations(securityProperties.getDigester().getSaltIterations());
-                
digester.setSaltSizeBytes(securityProperties.getDigester().getSaltSizeBytes());
+                digester.setIterations(digesterProperties.getSaltIterations());
+                
digester.setSaltSizeBytes(digesterProperties.getSaltSizeBytes());
                 digester.setInvertPositionOfPlainSaltInEncryptionResults(
-                        
securityProperties.getDigester().isInvertPositionOfPlainSaltInEncryptionResults());
+                        
digesterProperties.isInvertPositionOfPlainSaltInEncryptionResults());
                 digester.setInvertPositionOfSaltInMessageBeforeDigesting(
-                        
securityProperties.getDigester().isInvertPositionOfSaltInMessageBeforeDigesting());
-                digester.setUseLenientSaltSizeCheck(
-                        
securityProperties.getDigester().isUseLenientSaltSizeCheck());
+                        
digesterProperties.isInvertPositionOfSaltInMessageBeforeDigesting());
+                
digester.setUseLenientSaltSizeCheck(digesterProperties.isUseLenientSaltSizeCheck());
             } else {
                 // Not salted ...
                 digester.setAlgorithm(cipherAlgorithm.getAlgorithm());
@@ -160,10 +182,7 @@ public class DefaultEncryptor implements Encryptor {
             }
 
             
digester.setStringOutputType(CommonUtils.STRING_OUTPUT_TYPE_HEXADECIMAL);
-
-            digesters.put(cipherAlgorithm, digester);
-        }
-
-        return digester;
+            return digester;
+        });
     }
 }
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptorManager.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptorManager.java
index 903d5c3841..f0d5776f78 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptorManager.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultEncryptorManager.java
@@ -26,16 +26,22 @@ import 
org.apache.syncope.core.persistence.api.EncryptorManager;
 
 public class DefaultEncryptorManager implements EncryptorManager {
 
+    protected final SecurityProperties securityProperties;
+
     protected final Map<String, DefaultEncryptor> instances = new 
ConcurrentHashMap<>();
 
+    public DefaultEncryptorManager(final SecurityProperties 
securityProperties) {
+        this.securityProperties = securityProperties;
+    }
+
     @Override
     public Encryptor getInstance() {
         return getInstance(null);
     }
 
     @Override
-    public Encryptor getInstance(final String secretKey) {
-        String actualKey = StringUtils.isBlank(secretKey) ? 
DefaultEncryptor.DEFAULT_SECRET_KEY : secretKey;
-        return instances.computeIfAbsent(actualKey, DefaultEncryptor::new);
+    public Encryptor getInstance(final String aesSecretKey) {
+        String actualKey = StringUtils.isBlank(aesSecretKey) ? 
securityProperties.getAesSecretKey() : aesSecretKey;
+        return instances.computeIfAbsent(actualKey, k -> new 
DefaultEncryptor(k, securityProperties.getDigester()));
     }
 }
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
index d01dd14288..fdcc2a14f6 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
@@ -27,6 +27,7 @@ import java.io.InputStreamReader;
 import java.io.Reader;
 import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
+import java.util.Optional;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.EncryptorManager;
@@ -64,10 +65,8 @@ public class SecurityContext {
     }
 
     protected static String jwsKey(final JWSAlgorithm jwsAlgorithm, final 
SecurityProperties props) {
-        String jwsKey = props.getJwsKey();
-        if (jwsKey == null) {
-            throw new IllegalArgumentException("No JWS key provided");
-        }
+        String jwsKey = Optional.ofNullable(props.getJwsKey()).
+                orElseThrow(() -> new IllegalArgumentException("No JWS key 
provided"));
 
         if (JWSAlgorithm.Family.HMAC_SHA.contains(jwsAlgorithm)) {
             int minLength = jwsAlgorithm.equals(JWSAlgorithm.HS256)
@@ -158,8 +157,8 @@ public class SecurityContext {
     }
 
     @Bean
-    public EncryptorManager encryptorManager() {
-        return new DefaultEncryptorManager();
+    public EncryptorManager encryptorManager(final SecurityProperties 
securityProperties) {
+        return new DefaultEncryptorManager(securityProperties);
     }
 
     @ConditionalOnMissingBean
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java
index c5d9690117..cb9ffeb6bb 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java
@@ -98,7 +98,7 @@ public class SecurityProperties {
 
     private String jwsAlgorithm = JWSAlgorithm.HS512.getName();
 
-    private String secretKey;
+    private String aesSecretKey;
 
     private String groovyBlacklist = "classpath:META-INF/groovy.blacklist";
 
@@ -168,12 +168,12 @@ public class SecurityProperties {
         this.jwsAlgorithm = jwsAlgorithm;
     }
 
-    public String getSecretKey() {
-        return secretKey;
+    public String getAesSecretKey() {
+        return aesSecretKey;
     }
 
-    public void setSecretKey(final String secretKey) {
-        this.secretKey = secretKey;
+    public void setAesSecretKey(final String secretKey) {
+        this.aesSecretKey = secretKey;
     }
 
     public String getGroovyBlacklist() {
diff --git 
a/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java
 
b/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java
index 5547d28dff..82313849c4 100644
--- 
a/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java
+++ 
b/core/spring/src/test/java/org/apache/syncope/core/spring/SpringTestConfiguration.java
@@ -27,6 +27,7 @@ import 
org.apache.syncope.core.persistence.api.EncryptorManager;
 import org.apache.syncope.core.provisioning.api.ImplementationLookup;
 import org.apache.syncope.core.spring.security.DefaultEncryptorManager;
 import org.apache.syncope.core.spring.security.DummyImplementationLookup;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.jenkinsci.plugins.scriptsecurity.sandbox.blacklists.Blacklist;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -37,6 +38,8 @@ import org.springframework.context.annotation.Primary;
 @Configuration(proxyBeanMethods = false)
 public class SpringTestConfiguration {
 
+    public static final String AES_SECRET_KEY = "1abcdefghilmnopq";
+
     @Bean
     public ApplicationContextProvider applicationContextProvider() {
         return new ApplicationContextProvider();
@@ -44,7 +47,9 @@ public class SpringTestConfiguration {
 
     @Bean
     public EncryptorManager encryptorManager() {
-        return new DefaultEncryptorManager();
+        SecurityProperties securityProperties = new SecurityProperties();
+        securityProperties.setAesSecretKey(AES_SECRET_KEY);
+        return new DefaultEncryptorManager(securityProperties);
     }
 
     @Primary
diff --git 
a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java
 
b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java
index 3b55d76559..bba0d18cf9 100644
--- 
a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java
+++ 
b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.Encryptor;
+import org.apache.syncope.core.spring.SpringTestConfiguration;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -37,8 +38,13 @@ public class DefaultEncryptorTest {
 
     @BeforeAll
     public static void setUp() {
-        
ApplicationContextProvider.getBeanFactory().registerSingleton("securityProperties",
 new SecurityProperties());
-        ENCRYPTOR = new DefaultEncryptorManager().getInstance();
+        SecurityProperties props = new SecurityProperties();
+        props.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY);
+        
ApplicationContextProvider.getBeanFactory().registerSingleton("securityProperties",
 props);
+
+        SecurityProperties securityProperties = new SecurityProperties();
+        
securityProperties.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY);
+        ENCRYPTOR = new 
DefaultEncryptorManager(securityProperties).getInstance();
     }
 
     @Test
@@ -68,7 +74,7 @@ public class DefaultEncryptorTest {
 
     @Test
     public void smallKey() throws Exception {
-        DefaultEncryptor smallKeyEncryptor = new DefaultEncryptor("123");
+        DefaultEncryptor smallKeyEncryptor = new DefaultEncryptor("123", new 
SecurityProperties().getDigester());
         String encPassword = smallKeyEncryptor.encode(PASSWORD_VALUE, 
CipherAlgorithm.AES);
         String decPassword = smallKeyEncryptor.decode(encPassword, 
CipherAlgorithm.AES);
         assertEquals(PASSWORD_VALUE, decPassword);
diff --git a/core/starter/src/main/resources/core.properties 
b/core/starter/src/main/resources/core.properties
index 54595f25fe..59b6e05dd8 100644
--- a/core/starter/src/main/resources/core.properties
+++ b/core/starter/src/main/resources/core.properties
@@ -91,7 +91,14 @@ security.jwtIssuer=ApacheSyncope
 security.jwsAlgorithm=HS512
 security.jwsKey=${jwsKey}
 
-security.secretKey=${secretKey}
+# Key length drives AES algorithm variant selection:
+#
+# * 16 chars => AES-128
+# * 24 chars => AES-192
+# * 32 chars => AES-256
+#
+# Shorter keys will be padded to the nearest longer option available; keys > 
32 will be trucated
+security.aesSecretKey=${secretKey}
 
 # default for LDAP / RFC2307 SSHA
 security.digester.saltIterations=1
diff --git 
a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
 
b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
index 496f1791a3..013af583a9 100644
--- 
a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
+++ 
b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
@@ -137,8 +137,8 @@ public abstract class AbstractUserWorkflowAdapter extends 
AbstractWorkflowAdapte
                         matching = 
pwdHistory.subList(policy.getHistoryLength() >= pwdHistory.size()
                                 ? 0
                                 : pwdHistory.size() - 
policy.getHistoryLength(), pwdHistory.size()).stream().
-                                map(old -> 
encryptorManager.getInstance().verify(
-                                clearPassword, user.getCipherAlgorithm(), 
old)).
+                                map(old -> encryptorManager.getInstance().
+                                verify(clearPassword, 
user.getCipherAlgorithm(), old)).
                                 reduce(matching, (accumulator, item) -> 
accumulator | item);
                     }
                     if (matching) {
diff --git a/fit/core-reference/src/main/resources/core-embedded.properties 
b/fit/core-reference/src/main/resources/core-embedded.properties
index d44c2cc484..d45dc0dac3 100644
--- a/fit/core-reference/src/main/resources/core-embedded.properties
+++ b/fit/core-reference/src/main/resources/core-embedded.properties
@@ -30,7 +30,7 @@ spring.devtools.restart.enabled=false
 security.adminUser=${adminUser}
 security.anonymousUser=${anonymousUser}
 security.jwsKey=${jwsKey}
-security.secretKey=${secretKey}
+security.aesSecretKey=NyefOIpekEJVBASbbETMbcns11HouPNn
 
 persistence.domain[0].key=Master
 persistence.domain[0].jdbcDriver=org.postgresql.Driver
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 4dfd1a47e4..d3c7bf7b1e 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -178,6 +178,7 @@ import 
org.apache.syncope.common.rest.api.service.wa.WAConfigService;
 import 
org.apache.syncope.common.rest.api.service.wa.WebAuthnRegistrationService;
 import org.apache.syncope.core.persistence.api.EncryptorManager;
 import org.apache.syncope.core.spring.security.DefaultEncryptorManager;
+import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.apache.syncope.fit.AbstractITCase.KeymasterInitializer;
 import org.apache.syncope.fit.core.AbstractTaskITCase;
 import org.apache.syncope.fit.core.CoreITContext;
@@ -1158,6 +1159,11 @@ public abstract class AbstractITCase {
     @Autowired
     protected DataSource testDataSource;
 
-    protected final EncryptorManager encryptorManager = new 
DefaultEncryptorManager();
+    protected final EncryptorManager encryptorManager;
 
+    protected AbstractITCase() {
+        SecurityProperties securityProperties = new SecurityProperties();
+        securityProperties.setAesSecretKey(StringUtils.EMPTY);
+        encryptorManager = new DefaultEncryptorManager(securityProperties);
+    }
 }
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
index d2f523e33d..cf30a5de4e 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java
@@ -337,8 +337,8 @@ public class KeymasterITCase extends AbstractITCase {
         try {
             // 1. change admin pwd for domain Two
             domainOps.changeAdminPassword(two.getKey(),
-                    encryptorManager.getInstance().encode("password3", 
CipherAlgorithm.AES),
-                    CipherAlgorithm.AES);
+                    encryptorManager.getInstance().encode("password3", 
CipherAlgorithm.BCRYPT),
+                    CipherAlgorithm.BCRYPT);
 
             // 2. attempt to access with old pwd -> fail
             try {
diff --git 
a/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc 
b/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc
index bba639466b..0bfb7b5088 100644
--- 
a/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc
+++ 
b/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc
@@ -24,13 +24,16 @@ Most run-time configuration options are available as 
parameters and can be tuned
 * `password.cipher.algorithm` - which cipher algorithm shall be used for 
encrypting password values; supported 
 algorithms include `SHA-1`, `SHA-256`, `SHA-512`, `AES`, `S-MD5`, `S-SHA-1`, 
`S-SHA-256`, `S-SHA-512` and `BCRYPT`;
 salting options are available in the `core.properties` file;
+* `security.aesSecretKey` - used for AES-based encryption / decryption: 
besides password values, this is also used
+whenever reversible encryption is needed, throughout the whole system;
 [WARNING]
-The value of the `security.secretKey` property in the `core.properties` file 
is used for AES-based encryption / decryption.
-Besides password values, this is also used whenever reversible encryption is 
needed, throughout the whole system. +
-When the `secretKey` value has length less than 16, it is right-padded by 
random characters during startup, to reach
-such mininum value. +
-It is *strongly* recommended to provide a value long at least 16 characters, 
in order to avoid unexpected behaviors
-at runtime, expecially with high-availability. 
+The actual length of the `security.aesSecretKey` value is used to drive the 
AES algorithm variant selection:
+16 characters implies `AES-128`, 24 selects `AES-192` and 32 configures 
`AES-256`. +
+When the `security.aesSecretKey` value has length less than 16, between 17 and 
23 or between 25 and 31, it is
+right-padded by random characters during startup, to reach the nearest option. 
If the specified value is instead longer
+than 32 characters, it is truncated to 32. +
+It is *strongly* recommended to provide a value long exactly 16, 24 or 32 
characters, in order to avoid unexpected
+behaviors at runtime, expecially with high-availability. 
 * `jwt.lifetime.minutes` - validity of 
https://en.wikipedia.org/wiki/JSON_Web_Token[JSON Web Token^] values used for
 <<rest-authentication-and-authorization,authentication>> (in minutes);
 * `notificationjob.cronExpression` -


Reply via email to