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

kwin pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-crypto.git


The following commit(s) were added to refs/heads/master by this push:
     new 905565e  Improve clearing passwords after use
905565e is described below

commit 905565e53f9f4ec07c68ba75e1c52df8e9278af4
Author: Konrad Windszus <[email protected]>
AuthorDate: Tue Sep 15 17:00:24 2026 +0200

    Improve clearing passwords after use
---
 .../sling/commons/crypto/PasswordProvider.java     |  2 +-
 .../crypto/internal/PbeSecretKeyProvider.java      |  8 +++-
 .../crypto/jca/internal/JcaPbeCryptoService.java   | 50 +++++++++++++++-------
 .../jca/internal/JcaPbeCryptoServiceTest.java      |  2 +-
 4 files changed, 43 insertions(+), 19 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/commons/crypto/PasswordProvider.java 
b/src/main/java/org/apache/sling/commons/crypto/PasswordProvider.java
index efa89dc..0b9c9db 100644
--- a/src/main/java/org/apache/sling/commons/crypto/PasswordProvider.java
+++ b/src/main/java/org/apache/sling/commons/crypto/PasswordProvider.java
@@ -32,7 +32,7 @@ public interface PasswordProvider {
     /**
      * Provides the password.
      *
-     * @return The password
+     * @return The password (a newly created character array which should be 
cleared after use).
      */
     public abstract char @NotNull [] getPassword();
 
diff --git 
a/src/main/java/org/apache/sling/commons/crypto/internal/PbeSecretKeyProvider.java
 
b/src/main/java/org/apache/sling/commons/crypto/internal/PbeSecretKeyProvider.java
index 226b750..35f782a 100644
--- 
a/src/main/java/org/apache/sling/commons/crypto/internal/PbeSecretKeyProvider.java
+++ 
b/src/main/java/org/apache/sling/commons/crypto/internal/PbeSecretKeyProvider.java
@@ -20,7 +20,7 @@ package org.apache.sling.commons.crypto.internal;
 
 import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
+import java.util.Arrays;
 import java.util.Objects;
 
 import javax.crypto.SecretKey;
@@ -100,11 +100,15 @@ public final class PbeSecretKeyProvider implements 
SecretKeyProvider {
     public @NotNull SecretKey getSecretKey() {
         final var configuration = this.configuration;
         Objects.requireNonNull(configuration, "Configuration must not be 
null");
+        char[] password = passwordProvider.getPassword();
+        final PBEKeySpec keySpec = new PBEKeySpec(password, 
saltProvider.getSalt(), configuration.iterationCount(), 
configuration.keyLength());
         try {
-            final KeySpec keySpec = new 
PBEKeySpec(passwordProvider.getPassword(), saltProvider.getSalt(), 
configuration.iterationCount(), configuration.keyLength());
             return factory.generateSecret(keySpec);
         } catch (InvalidKeySpecException e) {
             throw new IllegalArgumentException(e.getMessage(), e);
+        } finally {
+            Arrays.fill(password, '\0');
+            keySpec.clearPassword();
         }
     }
 
diff --git 
a/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
 
b/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
index 8946ff0..c78357b 100644
--- 
a/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
+++ 
b/src/main/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoService.java
@@ -31,6 +31,7 @@ import java.security.SecureRandom;
 import java.security.Security;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.Objects;
 import java.util.Optional;
@@ -121,6 +122,18 @@ public final class JcaPbeCryptoService implements 
CryptoService {
         }
     }
 
+    private static void destroyData(byte[] data) {
+        if (data != null) {
+            Arrays.fill(data, (byte) 0x00);
+        }
+    }
+
+    private static void destroyData(char[] data) {
+        if (data != null) {
+            Arrays.fill(data, '\0');
+        }
+    }
+
     private @NotNull SecretKey createKey(byte[] salt) throws 
NoSuchAlgorithmException, InvalidKeySpecException {
         final char[] password = passwordProvider.getPassword();
         // for regular PBE key this is completely ignored except for the 
password (as all logic is encapsulated in the actual cipher
@@ -131,21 +144,28 @@ public final class JcaPbeCryptoService implements 
CryptoService {
                 salt,
                 configuration.numKeyIterations(),
                 configuration.keyLengthBits());
-        SecretKeyFactory secretKeyFactory = securityProvider.isPresent()
-                ? 
SecretKeyFactory.getInstance(configuration.secretKeyFactoryAlgorithm(), 
securityProvider.get())
-                : 
SecretKeyFactory.getInstance(configuration.secretKeyFactoryAlgorithm());
-        SecretKey originalKey = secretKeyFactory.generateSecret(keySpec);
-        keySpec.clearPassword(); // clear password from memory after use
-        if 
(configuration.secretKeyFactoryAlgorithm().equals(configuration.cipherAlgorithm()))
 {
-            // if the cipher algorithm is the same as the secret key factory 
algorithm then the cipher takes care of the actual logic and
-            // uses the key as is (which is just a wrapper around the given 
password)
-            return originalKey;
-        } else {
-            // wrap as key for the proper cipher algorithm (e.g., AES) instead 
of the PBE algorithm (e.g., PBKDF2WithHmacSHA512)
-            SecretKey derivedKey = new SecretKeySpec(originalKey.getEncoded(), 
extractAlgorithmName(configuration.cipherAlgorithm()));
-            destroyKey(originalKey); // destroy the original key as it is no 
longer needed
-            return derivedKey;
-        }
+        try {
+            SecretKeyFactory secretKeyFactory = securityProvider.isPresent()
+                    ? 
SecretKeyFactory.getInstance(configuration.secretKeyFactoryAlgorithm(), 
securityProvider.get())
+                    : 
SecretKeyFactory.getInstance(configuration.secretKeyFactoryAlgorithm());
+            SecretKey originalKey = secretKeyFactory.generateSecret(keySpec);
+            
+            if 
(configuration.secretKeyFactoryAlgorithm().equals(configuration.cipherAlgorithm()))
 {
+                // if the cipher algorithm is the same as the secret key 
factory algorithm then the cipher takes care of the actual logic and
+                // uses the key as is (which is just a wrapper around the 
given password)
+                return originalKey;
+            } else {
+                // wrap as key for the proper cipher algorithm (e.g., AES) 
instead of the PBE algorithm (e.g., PBKDF2WithHmacSHA512)
+                byte[] keyBytes = originalKey.getEncoded();
+                SecretKey derivedKey = new SecretKeySpec(keyBytes, 
extractAlgorithmName(configuration.cipherAlgorithm()));
+                destroyData(keyBytes); // clear key bytes from memory after use
+                destroyKey(originalKey); // destroy the original key as it is 
no longer needed
+                return derivedKey;
+            }
+         } finally {
+             keySpec.clearPassword(); // clear password from memory after use
+             destroyData(password); // clear password from memory after use
+         }
     }
 
     /** Extracts the algorithm name from the cipher algorithm string.
diff --git 
a/src/test/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceTest.java
 
b/src/test/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceTest.java
index 348c827..df3383e 100644
--- 
a/src/test/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceTest.java
+++ 
b/src/test/java/org/apache/sling/commons/crypto/jca/internal/JcaPbeCryptoServiceTest.java
@@ -88,7 +88,7 @@ class JcaPbeCryptoServiceTest {
     @BeforeEach
     void setUp() throws NoSuchAlgorithmException {
         passwordProvider = mock(PasswordProvider.class);
-        
when(passwordProvider.getPassword()).thenReturn("+AQ?aDes!'DBMkrCi:FE6q\\sOn=Pbmn=PK8n=PK?".toCharArray());
+        when(passwordProvider.getPassword()).thenAnswer(i -> 
"+AQ?aDes!'DBMkrCi:FE6q\\sOn=Pbmn=PK8n=PK?".toCharArray()); // long password to 
test PBKDF2 with high iteration count
         salt = new byte[16];
         Random random = new Random();
         random.nextBytes(salt);

Reply via email to