You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/04/02 01:34:27 UTC

[james-project] branch master updated (da56bdd -> ed78aaa)

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

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from da56bdd  JAMES-1489 Failing test for UTF-8 IMAP search
     new 964b7a1  JAMES-3524 Maven module for AES blob encryption
     new 1867643  JAMES-3524 Restore classes deleted after S3 blobStore rewrite
     new 289ccbb  JAMES-3524 Write a BlobStoreDAO wrapper performing AES encryption
     new 6752870  JAMES-3524 BlobStoreCacheModulesChooser should enable encryption
     new 925b0d5  JAMES-3524 Configuration should help setting up AES encryption
     new 8ead5fc  JAMES-3524 Document blobStore encryption
     new 4169d88  JAMES-3524 ChangeLog entry
     new ed78aaa  JAMES-3254 Encryption to a temporary file

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.md                                       |   3 +
 .../destination/conf/blob.properties               |  14 ++-
 .../destination/conf/blob.properties               |  14 ++-
 .../pages/distributed/configure/blobstore.adoc     |  27 +++++
 pom.xml                                            |   5 +
 server/blob/{mail-store => blob-aes}/pom.xml       |  46 +++----
 .../org/apache/james/blob/aes/AESBlobStoreDAO.java | 134 +++++++++++++++++++++
 .../org/apache/james/blob/aes/CryptoConfig.java    |  43 +++----
 .../james/blob/aes/CryptoConfigBuilder.java}       |  30 +++--
 .../org/apache/james/blob/aes/CryptoException.java |  12 +-
 .../james/blob/aes/PBKDF2StreamingAeadFactory.java |  59 +++++++++
 .../apache/james/blob/aes/AESBlobStoreDAOTest.java |  86 +++++++++++++
 server/blob/pom.xml                                |   1 +
 .../james/CassandraRabbitMQAwsS3JmapTestRule.java  |   3 +-
 .../james/CassandraRabbitMQJamesServerFixture.java |   3 +-
 .../apache/james/NamespaceConfigurationTest.java   |   3 +-
 .../org/apache/james/WithCacheImmutableTest.java   |   3 +-
 ...ssandraDeduplicationBlobStoreImmutableTest.java |   3 +-
 ...CassandraDeduplicationBlobStoreMutableTest.java |   3 +-
 ...CassandraPassThroughBlobStoreImmutableTest.java |   3 +-
 ...thCassandraPassThroughBlobStoreMutableTest.java |   5 +-
 ...va => WithEncryptedBlobStoreImmutableTest.java} |  10 +-
 ...java => WithEncryptedBlobStoreMutableTest.java} |  13 +-
 .../james/WithScanningSearchImmutableTest.java     |   3 +-
 ...oreConfigurationValidationStartUpCheckTest.java |   8 +-
 .../CassandraRabbitMQLdapJmapJamesServerTest.java  |   6 +-
 server/container/guice/distributed/pom.xml         |   4 +
 .../modules/blobstore/BlobStoreChoosingModule.java |   1 -
 .../modules/blobstore/BlobStoreConfiguration.java  |  71 +++++++++--
 .../modules/blobstore/BlobStoreModulesChooser.java |  44 ++++++-
 .../BlobStoreCacheModulesChooserTest.java          |   6 +-
 .../blobstore/BlobStoreConfigurationTest.java      | 100 ++++++++++++++-
 .../blobstore/BlobStoreModulesChooserTest.java     |  41 ++++++-
 ...eStrategyValidationEventSourcingSystemTest.java |  24 ++--
 .../rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java   |   3 +-
 .../RabbitMQAwsS3SpamAssassinContractTest.java     |   3 +-
 .../cucumber/awss3/RabbitMQAwsS3Stepdefs.java      |   3 +-
 .../distributed/DistributedAuthenticationTest.java |   3 +-
 .../distributed/DistributedDownloadTest.java       |   3 +-
 .../distributed/DistributedEchoMethodTest.java     |   3 +-
 .../DistributedEmailChangeMethodTest.java          |   3 +-
 .../distributed/DistributedEmailGetMethodTest.java |   3 +-
 .../DistributedEmailQueryMethodNoViewTest.java     |   3 +-
 .../DistributedEmailQueryMethodTest.java           |   3 +-
 .../distributed/DistributedEmailSetMethodTest.java |   3 +-
 .../DistributedEmailSubmissionSetMethodtest.java   |   3 +-
 .../distributed/DistributedIdentityGetTest.java    |   3 +-
 .../DistributedMailboxChangeMethodTest.java        |   3 +-
 .../DistributedMailboxGetMethodTest.java           |   3 +-
 .../DistributedMailboxQueryMethodTest.java         |   3 +-
 .../DistributedMailboxSetMethodTest.java           |   3 +-
 .../distributed/DistributedProvisioningTest.java   |   3 +-
 .../distributed/DistributedSessionRouteTest.java   |   3 +-
 .../rfc8621/distributed/DistributedUploadTest.java |   3 +-
 .../DistributedVacationResponseGetMethodTest.java  |   3 +-
 .../DistributedVacationResponseSetMethodTest.java  |   3 +-
 .../distributed/DistributedWebSocketTest.java      |   3 +-
 .../jmap/rfc8621/distributed/ReadLevelTest.java    |   3 +-
 .../rabbitmq/ConsistencyTasksIntegrationTest.java  |   3 +-
 .../rabbitmq/FixingGhostMailboxTest.java           |   3 +-
 .../rabbitmq/RabbitMQAuthorizedEndpointsTest.java  |   3 +-
 .../RabbitMQEventDeadLettersIntegrationTest.java   |   3 +-
 ...stViewProjectionHealthCheckIntegrationTest.java |   3 +-
 .../rabbitmq/RabbitMQForwardIntegrationTest.java   |   3 +-
 .../rabbitmq/RabbitMQJwtFilterIntegrationTest.java |   3 +-
 ...RabbitMQReindexingWithEventDeadLettersTest.java |   3 +-
 .../RabbitMQUnauthorizedEndpointsTest.java         |   3 +-
 ...itMQWebAdminServerIntegrationImmutableTest.java |   3 +-
 .../RabbitMQWebAdminServerIntegrationTest.java     |   3 +-
 ...rTaskSerializationIntegrationImmutableTest.java |   3 +-
 ...dminServerTaskSerializationIntegrationTest.java |   3 +-
 ...RabbitMQDeletedMessageVaultIntegrationTest.java |   3 +-
 ...LinshareBlobExportMechanismIntegrationTest.java |   3 +-
 src/site/xdoc/server/config-blobstore.xml          |  36 +++++-
 74 files changed, 826 insertions(+), 158 deletions(-)
 copy server/blob/{mail-store => blob-aes}/pom.xml (65%)
 create mode 100644 server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java
 copy backends-common/elasticsearch-v7/src/main/java/org/apache/james/backends/es/v7/RoutingKey.java => server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java (64%)
 copy server/{protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/validation/MailboxName.java => blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java} (62%)
 copy mailbox/api/src/main/java/org/apache/james/mailbox/exception/AnnotationException.java => server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoException.java (83%)
 create mode 100644 server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/PBKDF2StreamingAeadFactory.java
 create mode 100644 server/blob/blob-aes/src/test/java/org/apache/james/blob/aes/AESBlobStoreDAOTest.java
 copy server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/{WithCassandraDeduplicationBlobStoreImmutableTest.java => WithEncryptedBlobStoreImmutableTest.java} (83%)
 copy server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/{WithCassandraDeduplicationBlobStoreImmutableTest.java => WithEncryptedBlobStoreMutableTest.java} (82%)

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 08/08: JAMES-3254 Encryption to a temporary file

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit ed78aaab8dff9778e678ca8017c166314abc06fe
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 26 17:39:24 2021 +0700

    JAMES-3254 Encryption to a temporary file
    
    This prevents trashing the memory
    
    Note that ObjectStorage would end up copying
    data to a temp file as knowing (encrypted)
    data size is needed.
    
    On the read path, we avoid allocating temporary
    resources as higher level API (like mailbox) miss
    decent resource management for the read path.
---
 .../org/apache/james/blob/aes/AESBlobStoreDAO.java | 81 ++++++++--------------
 1 file changed, 30 insertions(+), 51 deletions(-)

diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java
index 3d39278..8f45e0f 100644
--- a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java
@@ -22,13 +22,8 @@ package org.apache.james.blob.aes;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
-
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.james.blob.api.BlobId;
@@ -41,53 +36,37 @@ import org.reactivestreams.Publisher;
 import com.github.fge.lambdas.Throwing;
 import com.google.common.base.Preconditions;
 import com.google.common.io.ByteSource;
-import com.google.crypto.tink.Aead;
-import com.google.crypto.tink.aead.AeadConfig;
-import com.google.crypto.tink.subtle.AesGcmJce;
+import com.google.common.io.FileBackedOutputStream;
+import com.google.crypto.tink.subtle.AesGcmHkdfStreaming;
 
 import reactor.core.publisher.Mono;
-import reactor.core.scheduler.Schedulers;
 
 public class AESBlobStoreDAO implements BlobStoreDAO {
-    private static final byte[] EMPTY_ASSOCIATED_DATA = new byte[0];
-    private static final int PBKDF2_ITERATIONS = 65536;
-    private static final int KEY_SIZE = 256;
-    private static final String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA256";
-
+    // For now, aligned with with MimeMessageInputStreamSource file threshold, detailed benchmarking might be conducted to challenge this choice
+    public static final int FILE_THRESHOLD_100_KB = 100 * 1024;
     private final BlobStoreDAO underlying;
-    private final Aead aead;
+    private final AesGcmHkdfStreaming streamingAead;
 
     public AESBlobStoreDAO(BlobStoreDAO underlying, CryptoConfig cryptoConfig) {
         this.underlying = underlying;
-
-        try {
-            AeadConfig.register();
-
-            SecretKey secretKey = deriveKey(cryptoConfig);
-            aead = new AesGcmJce(secretKey.getEncoded());
-        } catch (GeneralSecurityException e) {
-            throw new RuntimeException("Error while starting AESPayloadCodec", e);
-        }
+        this.streamingAead = PBKDF2StreamingAeadFactory.newAesGcmHkdfStreaming(cryptoConfig);
     }
 
-    private static SecretKey deriveKey(CryptoConfig cryptoConfig) throws NoSuchAlgorithmException, InvalidKeySpecException {
-        byte[] saltBytes = cryptoConfig.salt();
-        SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
-        PBEKeySpec spec = new PBEKeySpec(cryptoConfig.password(), saltBytes, PBKDF2_ITERATIONS, KEY_SIZE);
-        return skf.generateSecret(spec);
-    }
-
-    public byte[] encrypt(byte[] input) {
-        try {
-            return aead.encrypt(input, EMPTY_ASSOCIATED_DATA);
-        } catch (GeneralSecurityException e) {
+    public FileBackedOutputStream encrypt(InputStream input) {
+        try (FileBackedOutputStream encryptedContent = new FileBackedOutputStream(FILE_THRESHOLD_100_KB)) {
+            OutputStream outputStream = streamingAead.newEncryptingStream(encryptedContent, PBKDF2StreamingAeadFactory.EMPTY_ASSOCIATED_DATA);
+            input.transferTo(outputStream);
+            outputStream.close();
+            return encryptedContent;
+        } catch (GeneralSecurityException | IOException e) {
             throw new RuntimeException("Unable to build payload for object storage, failed to encrypt", e);
         }
     }
 
-    public byte[] decrypt(byte[] ciphertext) throws IOException {
+    public InputStream decrypt(InputStream ciphertext) throws IOException {
+        // We break symmetry and avoid allocating resources like files as we are not able, in higher level APIs (mailbox) to do resource cleanup.
         try {
-            return aead.decrypt(ciphertext, EMPTY_ASSOCIATED_DATA);
+            return streamingAead.newDecryptingStream(ciphertext, PBKDF2StreamingAeadFactory.EMPTY_ASSOCIATED_DATA);
         } catch (GeneralSecurityException e) {
             throw new IOException("Incorrect crypto setup", e);
         }
@@ -95,17 +74,19 @@ public class AESBlobStoreDAO implements BlobStoreDAO {
 
     @Override
     public InputStream read(BucketName bucketName, BlobId blobId) throws ObjectStoreIOException, ObjectNotFoundException {
-        return Mono.from(underlying.readBytes(bucketName, blobId))
-            .map(Throwing.function(this::decrypt))
-            .map(ByteArrayInputStream::new)
-            .subscribeOn(Schedulers.elastic())
-            .block();
+        try {
+            return decrypt(underlying.read(bucketName, blobId));
+        } catch (IOException e) {
+            throw new ObjectStoreIOException("Error reading blob " + blobId.asString(), e);
+        }
     }
 
     @Override
     public Publisher<byte[]> readBytes(BucketName bucketName, BlobId blobId) {
         return Mono.from(underlying.readBytes(bucketName, blobId))
-            .map(Throwing.function(this::decrypt));
+            .map(ByteArrayInputStream::new)
+            .map(Throwing.function(this::decrypt))
+            .map(Throwing.function(IOUtils::toByteArray));
     }
 
     @Override
@@ -114,10 +95,7 @@ public class AESBlobStoreDAO implements BlobStoreDAO {
         Preconditions.checkNotNull(blobId);
         Preconditions.checkNotNull(data);
 
-        return Mono.just(data)
-            .flatMap(payload -> Mono.fromCallable(() -> encrypt(payload)).subscribeOn(Schedulers.parallel()))
-            .flatMap(encryptedPayload -> Mono.from(underlying.save(bucketName, blobId, encryptedPayload)))
-            .onErrorMap(e -> new ObjectStoreIOException("Exception occurred while saving bytearray", e));
+        return save(bucketName, blobId, new ByteArrayInputStream(data));
     }
 
     @Override
@@ -126,9 +104,10 @@ public class AESBlobStoreDAO implements BlobStoreDAO {
         Preconditions.checkNotNull(blobId);
         Preconditions.checkNotNull(inputStream);
 
-        return Mono.just(inputStream)
-            .flatMap(data -> Mono.fromCallable(() -> IOUtils.toByteArray(inputStream)).subscribeOn(Schedulers.parallel()))
-            .flatMap(encryptedData -> Mono.from(save(bucketName, blobId, encryptedData)))
+        return Mono.using(
+                () -> encrypt(inputStream),
+                fileBackedOutputStream -> Mono.from(underlying.save(bucketName, blobId, fileBackedOutputStream.asByteSource())),
+                Throwing.consumer(FileBackedOutputStream::reset))
             .onErrorMap(e -> new ObjectStoreIOException("Exception occurred while saving bytearray", e));
     }
 

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 04/08: JAMES-3524 BlobStoreCacheModulesChooser should enable encryption

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 6752870b46641405c5471ca1fc9bb8acd491c2f0
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 26 14:12:10 2021 +0700

    JAMES-3524 BlobStoreCacheModulesChooser should enable encryption
---
 pom.xml                                            |   5 ++
 .../org/apache/james/blob/aes/CryptoConfig.java    |  19 ++++
 .../apache/james/blob/aes/CryptoConfigBuilder.java |   5 +-
 .../james/CassandraRabbitMQAwsS3JmapTestRule.java  |   3 +-
 .../james/CassandraRabbitMQJamesServerFixture.java |   3 +-
 .../apache/james/NamespaceConfigurationTest.java   |   3 +-
 .../org/apache/james/WithCacheImmutableTest.java   |   3 +-
 ...ssandraDeduplicationBlobStoreImmutableTest.java |   3 +-
 ...CassandraDeduplicationBlobStoreMutableTest.java |   3 +-
 ...CassandraPassThroughBlobStoreImmutableTest.java |   3 +-
 ...thCassandraPassThroughBlobStoreMutableTest.java |   5 +-
 ...va => WithEncryptedBlobStoreImmutableTest.java} |  10 ++-
 ...java => WithEncryptedBlobStoreMutableTest.java} |  13 ++-
 .../james/WithScanningSearchImmutableTest.java     |   3 +-
 ...oreConfigurationValidationStartUpCheckTest.java |   8 +-
 .../CassandraRabbitMQLdapJmapJamesServerTest.java  |   6 +-
 server/container/guice/distributed/pom.xml         |   4 +
 .../modules/blobstore/BlobStoreChoosingModule.java |   1 -
 .../modules/blobstore/BlobStoreConfiguration.java  |  71 ++++++++++++---
 .../modules/blobstore/BlobStoreModulesChooser.java |  44 ++++++++-
 .../BlobStoreCacheModulesChooserTest.java          |   6 +-
 .../blobstore/BlobStoreConfigurationTest.java      | 100 ++++++++++++++++++++-
 .../blobstore/BlobStoreModulesChooserTest.java     |  41 +++++++--
 ...eStrategyValidationEventSourcingSystemTest.java |  24 +++--
 .../rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java   |   3 +-
 .../RabbitMQAwsS3SpamAssassinContractTest.java     |   3 +-
 .../cucumber/awss3/RabbitMQAwsS3Stepdefs.java      |   3 +-
 .../distributed/DistributedAuthenticationTest.java |   3 +-
 .../distributed/DistributedDownloadTest.java       |   3 +-
 .../distributed/DistributedEchoMethodTest.java     |   3 +-
 .../DistributedEmailChangeMethodTest.java          |   3 +-
 .../distributed/DistributedEmailGetMethodTest.java |   3 +-
 .../DistributedEmailQueryMethodNoViewTest.java     |   3 +-
 .../DistributedEmailQueryMethodTest.java           |   3 +-
 .../distributed/DistributedEmailSetMethodTest.java |   3 +-
 .../DistributedEmailSubmissionSetMethodtest.java   |   3 +-
 .../distributed/DistributedIdentityGetTest.java    |   3 +-
 .../DistributedMailboxChangeMethodTest.java        |   3 +-
 .../DistributedMailboxGetMethodTest.java           |   3 +-
 .../DistributedMailboxQueryMethodTest.java         |   3 +-
 .../DistributedMailboxSetMethodTest.java           |   3 +-
 .../distributed/DistributedProvisioningTest.java   |   3 +-
 .../distributed/DistributedSessionRouteTest.java   |   3 +-
 .../rfc8621/distributed/DistributedUploadTest.java |   3 +-
 .../DistributedVacationResponseGetMethodTest.java  |   3 +-
 .../DistributedVacationResponseSetMethodTest.java  |   3 +-
 .../distributed/DistributedWebSocketTest.java      |   3 +-
 .../jmap/rfc8621/distributed/ReadLevelTest.java    |   3 +-
 .../rabbitmq/ConsistencyTasksIntegrationTest.java  |   3 +-
 .../rabbitmq/FixingGhostMailboxTest.java           |   3 +-
 .../rabbitmq/RabbitMQAuthorizedEndpointsTest.java  |   3 +-
 .../RabbitMQEventDeadLettersIntegrationTest.java   |   3 +-
 ...stViewProjectionHealthCheckIntegrationTest.java |   3 +-
 .../rabbitmq/RabbitMQForwardIntegrationTest.java   |   3 +-
 .../rabbitmq/RabbitMQJwtFilterIntegrationTest.java |   3 +-
 ...RabbitMQReindexingWithEventDeadLettersTest.java |   3 +-
 .../RabbitMQUnauthorizedEndpointsTest.java         |   3 +-
 ...itMQWebAdminServerIntegrationImmutableTest.java |   3 +-
 .../RabbitMQWebAdminServerIntegrationTest.java     |   3 +-
 ...rTaskSerializationIntegrationImmutableTest.java |   3 +-
 ...dminServerTaskSerializationIntegrationTest.java |   3 +-
 ...RabbitMQDeletedMessageVaultIntegrationTest.java |   3 +-
 ...LinshareBlobExportMechanismIntegrationTest.java |   3 +-
 63 files changed, 404 insertions(+), 99 deletions(-)

diff --git a/pom.xml b/pom.xml
index 8e1a0bc..e9be952 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1033,6 +1033,11 @@
             </dependency>
             <dependency>
                 <groupId>${james.groupId}</groupId>
+                <artifactId>blob-aes</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${james.groupId}</groupId>
                 <artifactId>blob-api</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java
index 0625535..1f0938b 100644
--- a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java
@@ -19,6 +19,9 @@
 
 package org.apache.james.blob.aes;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 import com.google.crypto.tink.subtle.Hex;
 
 public class CryptoConfig {
@@ -42,4 +45,20 @@ public class CryptoConfig {
     public char[] password() {
         return password;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof CryptoConfig) {
+            CryptoConfig that = (CryptoConfig) o;
+
+            return Objects.equals(this.salt, that.salt)
+                && Arrays.equals(this.password, that.password);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(salt, password);
+    }
 }
\ No newline at end of file
diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java
index f3c9916..7ab8664 100644
--- a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java
@@ -41,8 +41,9 @@ public class CryptoConfigBuilder {
     }
 
     public CryptoConfig build() {
-        Preconditions.checkState(!Strings.isNullOrEmpty(salt));
-        Preconditions.checkState(password != null && password.length > 0);
+        Preconditions.checkState(!Strings.isNullOrEmpty(salt), "'salt' is mandatory and must not be empty");
+        Preconditions.checkState(password != null && password.length > 0, "'password' is mandatory and must not be empty");
+
         return new CryptoConfig(Hex.encode(Hex.decode(salt)), password);
     }
 }
\ No newline at end of file
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java
index c553a01..0e0b844 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java
@@ -64,7 +64,8 @@ public class CassandraRabbitMQAwsS3JmapTestRule implements TestRule {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build();
 
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java
index 1219646..2ac2825 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java
@@ -36,7 +36,8 @@ public class CassandraRabbitMQJamesServerFixture {
                 .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
                 .searchConfiguration(SearchConfiguration.elasticSearch())
                 .build())
             .extension(new DockerElasticSearchExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/NamespaceConfigurationTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/NamespaceConfigurationTest.java
index bd933b8..4398fcb 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/NamespaceConfigurationTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/NamespaceConfigurationTest.java
@@ -57,7 +57,8 @@ class NamespaceConfigurationTest {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheImmutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheImmutableTest.java
index 194367c..eebc25f 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheImmutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheImmutableTest.java
@@ -41,7 +41,8 @@ class WithCacheImmutableTest implements JmapJamesServerContract, JamesServerCont
                 .blobStore(BlobStoreConfiguration.builder()
                         .s3()
                         .enableCache()
-                        .deduplication())
+                        .deduplication()
+                        .noCryptoConfig())
                 .searchConfiguration(SearchConfiguration.elasticSearch())
                 .build())
             .extension(new DockerElasticSearchExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
index f18bfcf..9f845f0 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
@@ -32,7 +32,8 @@ public class WithCassandraDeduplicationBlobStoreImmutableTest implements JmapJam
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
             .blobStore(BlobStoreConfiguration.cassandra()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreMutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreMutableTest.java
index 655795e..5846eb1 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreMutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreMutableTest.java
@@ -60,7 +60,8 @@ public class WithCassandraDeduplicationBlobStoreMutableTest implements MailsShou
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
             .blobStore(BlobStoreConfiguration.cassandra()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreImmutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreImmutableTest.java
index a4b40b7..cc81e6a 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreImmutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreImmutableTest.java
@@ -32,7 +32,8 @@ public class WithCassandraPassThroughBlobStoreImmutableTest implements JmapJames
         .workingDirectory(tmpDir)
                 .configurationFromClasspath()
                 .blobStore(BlobStoreConfiguration.cassandra()
-                    .passthrough())
+                    .passthrough()
+                    .noCryptoConfig())
         .searchConfiguration(SearchConfiguration.elasticSearch())
         .build())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreMutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreMutableTest.java
index 1dd7ce0..1a3ba04 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreMutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraPassThroughBlobStoreMutableTest.java
@@ -29,7 +29,6 @@ import org.apache.james.backends.cassandra.init.configuration.CassandraConsisten
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
 import org.apache.james.blob.cassandra.BlobTables;
 import org.apache.james.core.Domain;
-import org.apache.james.jmap.draft.JmapJamesServerContract;
 import org.apache.james.mailbox.DefaultMailboxes;
 import org.apache.james.modules.MailboxProbeImpl;
 import org.apache.james.modules.RabbitMQExtension;
@@ -44,7 +43,6 @@ import org.apache.james.utils.SMTPMessageSender;
 import org.apache.james.utils.SpoolerProbe;
 import org.apache.james.utils.TestIMAPClient;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import com.datastax.driver.core.Cluster;
@@ -62,7 +60,8 @@ public class WithCassandraPassThroughBlobStoreMutableTest implements MailsShould
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
             .blobStore(BlobStoreConfiguration.cassandra()
-                .passthrough())
+                .passthrough()
+                .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithEncryptedBlobStoreImmutableTest.java
similarity index 83%
copy from server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
copy to server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithEncryptedBlobStoreImmutableTest.java
index f18bfcf..3caf921 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithEncryptedBlobStoreImmutableTest.java
@@ -19,20 +19,26 @@
 
 package org.apache.james;
 
+import org.apache.james.blob.aes.CryptoConfig;
 import org.apache.james.jmap.draft.JmapJamesServerContract;
 import org.apache.james.modules.RabbitMQExtension;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.apache.james.modules.blobstore.BlobStoreConfiguration;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class WithCassandraDeduplicationBlobStoreImmutableTest implements JmapJamesServerContract, JamesServerContract {
+public class WithEncryptedBlobStoreImmutableTest implements JmapJamesServerContract, JamesServerContract {
     @RegisterExtension
     static JamesServerExtension jamesServerExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
             .blobStore(BlobStoreConfiguration.cassandra()
-                .deduplication())
+                .deduplication()
+                .cryptoConfig(CryptoConfig.builder()
+                    .password("myPass".toCharArray())
+                    // Hex.encode("salty".getBytes(StandardCharsets.UTF_8))
+                    .salt("73616c7479")
+                    .build()))
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithEncryptedBlobStoreMutableTest.java
similarity index 82%
copy from server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
copy to server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithEncryptedBlobStoreMutableTest.java
index f18bfcf..da8e056 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraDeduplicationBlobStoreImmutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithEncryptedBlobStoreMutableTest.java
@@ -19,20 +19,25 @@
 
 package org.apache.james;
 
-import org.apache.james.jmap.draft.JmapJamesServerContract;
+import org.apache.james.blob.aes.CryptoConfig;
 import org.apache.james.modules.RabbitMQExtension;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.apache.james.modules.blobstore.BlobStoreConfiguration;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class WithCassandraDeduplicationBlobStoreImmutableTest implements JmapJamesServerContract, JamesServerContract {
+public class WithEncryptedBlobStoreMutableTest implements MailsShouldBeWellReceived {
     @RegisterExtension
     static JamesServerExtension jamesServerExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
             .blobStore(BlobStoreConfiguration.cassandra()
-                .deduplication())
+                .deduplication()
+                .cryptoConfig(CryptoConfig.builder()
+                    .password("myPass".toCharArray())
+                    // Hex.encode("salty".getBytes(StandardCharsets.UTF_8))
+                    .salt("73616c7479")
+                    .build()))
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
@@ -40,6 +45,6 @@ public class WithCassandraDeduplicationBlobStoreImmutableTest implements JmapJam
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
         .extension(new RabbitMQExtension())
-        .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+        .lifeCycle(JamesServerExtension.Lifecycle.PER_TEST)
         .build();
 }
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchImmutableTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchImmutableTest.java
index cddaa97..7c973e4 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchImmutableTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchImmutableTest.java
@@ -35,7 +35,8 @@ class WithScanningSearchImmutableTest implements JmapJamesServerContract, JamesS
                 .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
                 .searchConfiguration(SearchConfiguration.scanning())
                 .build())
             .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationValidationStartUpCheckTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationValidationStartUpCheckTest.java
index 81de1a4..287594b 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationValidationStartUpCheckTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationValidationStartUpCheckTest.java
@@ -29,7 +29,6 @@ import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.eventsourcing.Event;
 import org.apache.james.eventsourcing.eventstore.EventStore;
-import org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStore;
 import org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStoreExtension;
 import org.apache.james.eventsourcing.eventstore.cassandra.CassandraEventStoreModule;
 import org.apache.james.eventsourcing.eventstore.cassandra.JsonEventSerializer;
@@ -38,7 +37,6 @@ import org.apache.james.eventsourcing.eventstore.cassandra.dto.EventDTOModule;
 import org.apache.james.lifecycle.api.StartUpCheck;
 import org.apache.james.modules.blobstore.validation.EventsourcingStorageStrategy;
 import org.apache.james.modules.blobstore.validation.StorageStrategyModule;
-import org.apache.james.server.blob.deduplication.StorageStrategy;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
@@ -66,12 +64,14 @@ class BlobStoreConfigurationValidationStartUpCheckTest {
             .builder()
             .cassandra()
             .disableCache()
-            .deduplication();
+            .deduplication()
+            .noCryptoConfig();
     private static BlobStoreConfiguration PASSTHROUGH_STRATEGY = BlobStoreConfiguration
             .builder()
             .cassandra()
             .disableCache()
-            .passthrough();
+            .passthrough()
+            .noCryptoConfig();
 
     private EventsourcingStorageStrategy eventsourcingStorageStrategy;
 
diff --git a/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java b/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
index 0f2aec0..4161a2f 100644
--- a/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
+++ b/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
@@ -59,7 +59,8 @@ class CassandraRabbitMQLdapJmapJamesServerTest {
         @RegisterExtension
         JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.s3()
                 .disableCache()
-                .passthrough())
+                .passthrough()
+                .noCryptoConfig())
             .extension(new AwsS3BlobStoreExtension())
             .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
             .build();
@@ -72,7 +73,8 @@ class CassandraRabbitMQLdapJmapJamesServerTest {
         JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.builder()
                 .cassandra()
                 .disableCache()
-                .passthrough())
+                .passthrough()
+                .noCryptoConfig())
             .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
             .build();
     }
diff --git a/server/container/guice/distributed/pom.xml b/server/container/guice/distributed/pom.xml
index 74d47fa..51e3b1d 100644
--- a/server/container/guice/distributed/pom.xml
+++ b/server/container/guice/distributed/pom.xml
@@ -49,6 +49,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>blob-aes</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>blob-s3-guice</artifactId>
         </dependency>
         <dependency>
diff --git a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreChoosingModule.java b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreChoosingModule.java
index 27c3231..61d2ef9 100644
--- a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreChoosingModule.java
+++ b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreChoosingModule.java
@@ -28,7 +28,6 @@ import com.google.inject.multibindings.Multibinder;
 public class BlobStoreChoosingModule extends AbstractModule {
     @Override
     protected void configure() {
-
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().toInstance(CassandraBlobModule.MODULE);
     }
diff --git a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java
index 215c4f9..ba9a383 100644
--- a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java
+++ b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java
@@ -28,6 +28,7 @@ import java.util.stream.Stream;
 import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.james.blob.aes.CryptoConfig;
 import org.apache.james.modules.mailbox.ConfigurationComponent;
 import org.apache.james.server.blob.deduplication.StorageStrategy;
 import org.apache.james.server.core.filesystem.FileSystemImpl;
@@ -71,19 +72,33 @@ public class BlobStoreConfiguration {
 
     @FunctionalInterface
     public interface RequireStoringStrategy {
-        BlobStoreConfiguration strategy(StorageStrategy storageStrategy);
+        RequireCryptoConfig strategy(StorageStrategy storageStrategy);
 
-        default BlobStoreConfiguration passthrough() {
+        default RequireCryptoConfig passthrough() {
             return strategy(StorageStrategy.PASSTHROUGH);
         }
 
-        default BlobStoreConfiguration deduplication() {
+        default RequireCryptoConfig deduplication() {
             return strategy(StorageStrategy.DEDUPLICATION);
         }
     }
 
+    @FunctionalInterface
+    public interface RequireCryptoConfig {
+        BlobStoreConfiguration cryptoConfig(Optional<CryptoConfig> cryptoConfig);
+
+        default BlobStoreConfiguration noCryptoConfig() {
+            return cryptoConfig(Optional.empty());
+        }
+
+        default BlobStoreConfiguration cryptoConfig(CryptoConfig cryptoConfig) {
+            return cryptoConfig(Optional.of(cryptoConfig));
+        }
+    }
+
     public static RequireImplementation builder() {
-        return implementation -> enableCache -> storageStrategy -> new BlobStoreConfiguration(implementation, enableCache, storageStrategy);
+        return implementation -> enableCache -> storageStrategy -> cryptoConfig ->
+            new BlobStoreConfiguration(implementation, enableCache, storageStrategy, cryptoConfig);
     }
 
     public enum BlobStoreImplName {
@@ -117,6 +132,9 @@ public class BlobStoreConfiguration {
 
     static final String BLOBSTORE_IMPLEMENTATION_PROPERTY = "implementation";
     static final String CACHE_ENABLE_PROPERTY = "cache.enable";
+    static final String ENCRYPTION_ENABLE_PROPERTY = "encryption.aes.enable";
+    static final String ENCRYPTION_PASSWORD_PROPERTY = "encryption.aes.password";
+    static final String ENCRYPTION_SALT_PROPERTY = "encryption.aes.salt";
     static final boolean CACHE_ENABLED = true;
     static final String DEDUPLICATION_ENABLE_PROPERTY = "deduplication.enable";
 
@@ -134,9 +152,10 @@ public class BlobStoreConfiguration {
         } catch (FileNotFoundException e) {
             LOGGER.warn("Could not find " + ConfigurationComponent.NAME + " configuration file, using cassandra blobstore as the default");
             return BlobStoreConfiguration.builder()
-                    .cassandra()
-                    .disableCache()
-                    .passthrough();
+                .cassandra()
+                .disableCache()
+                .passthrough()
+                .noCryptoConfig();
         }
     }
 
@@ -155,12 +174,32 @@ public class BlobStoreConfiguration {
                         "Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all\n" +
                         "the mails sharing the same content once one is deleted.\n" +
                         "Upgrade note: If you are upgrading from James 3.5 or older, the deduplication was enabled."));
+        Optional<CryptoConfig> cryptoConfig = parseCryptoConfig(configuration);
 
         if (deduplicationEnabled) {
-            return new BlobStoreConfiguration(blobStoreImplName, cacheEnabled, StorageStrategy.DEDUPLICATION);
+            return builder()
+                .implementation(blobStoreImplName)
+                .enableCache(cacheEnabled)
+                .deduplication()
+                .cryptoConfig(cryptoConfig);
         } else {
-            return new BlobStoreConfiguration(blobStoreImplName, cacheEnabled, StorageStrategy.PASSTHROUGH);
+            return builder()
+                .implementation(blobStoreImplName)
+                .enableCache(cacheEnabled)
+                .passthrough()
+                .cryptoConfig(cryptoConfig);
+        }
+    }
+
+    private static Optional<CryptoConfig> parseCryptoConfig(Configuration configuration) {
+        final boolean enabled = configuration.getBoolean(ENCRYPTION_ENABLE_PROPERTY, false);
+        if (enabled) {
+            return Optional.of(CryptoConfig.builder()
+                .password(Optional.ofNullable(configuration.getString(ENCRYPTION_PASSWORD_PROPERTY, null)).map(String::toCharArray).orElse(null))
+                .salt(configuration.getString(ENCRYPTION_SALT_PROPERTY, null))
+                .build());
         }
+        return Optional.empty();
     }
 
     @VisibleForTesting
@@ -177,11 +216,13 @@ public class BlobStoreConfiguration {
     private final BlobStoreImplName implementation;
     private final boolean cacheEnabled;
     private final StorageStrategy storageStrategy;
+    private final Optional<CryptoConfig> cryptoConfig;
 
-    BlobStoreConfiguration(BlobStoreImplName implementation, boolean cacheEnabled, StorageStrategy storageStrategy) {
+    BlobStoreConfiguration(BlobStoreImplName implementation, boolean cacheEnabled, StorageStrategy storageStrategy, Optional<CryptoConfig> cryptoConfig) {
         this.implementation = implementation;
         this.cacheEnabled = cacheEnabled;
         this.storageStrategy = storageStrategy;
+        this.cryptoConfig = cryptoConfig;
     }
 
     public boolean cacheEnabled() {
@@ -196,6 +237,10 @@ public class BlobStoreConfiguration {
         return implementation;
     }
 
+    public Optional<CryptoConfig> getCryptoConfig() {
+        return cryptoConfig;
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof BlobStoreConfiguration) {
@@ -203,14 +248,15 @@ public class BlobStoreConfiguration {
 
             return Objects.equals(this.implementation, that.implementation)
                 && Objects.equals(this.cacheEnabled, that.cacheEnabled)
-                && Objects.equals(this.storageStrategy, that.storageStrategy);
+                && Objects.equals(this.storageStrategy, that.storageStrategy)
+                && Objects.equals(this.cryptoConfig, that.cryptoConfig);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hash(implementation, cacheEnabled, storageStrategy);
+        return Objects.hash(implementation, cacheEnabled, storageStrategy, cryptoConfig);
     }
 
     @Override
@@ -219,6 +265,7 @@ public class BlobStoreConfiguration {
             .add("implementation", implementation)
             .add("cacheEnabled", cacheEnabled)
             .add("storageStrategy", storageStrategy.name())
+            .add("cryptoConfig", cryptoConfig)
             .toString();
     }
 }
diff --git a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
index dfa2292..7e4680f 100644
--- a/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
+++ b/server/container/guice/distributed/src/main/java/org/apache/james/modules/blobstore/BlobStoreModulesChooser.java
@@ -20,7 +20,10 @@
 package org.apache.james.modules.blobstore;
 
 import java.util.List;
+import java.util.Optional;
 
+import org.apache.james.blob.aes.AESBlobStoreDAO;
+import org.apache.james.blob.aes.CryptoConfig;
 import org.apache.james.blob.api.BlobStore;
 import org.apache.james.blob.api.BlobStoreDAO;
 import org.apache.james.blob.cassandra.CassandraBlobStoreDAO;
@@ -44,19 +47,23 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.AbstractModule;
 import com.google.inject.Module;
+import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
 import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
 import com.google.inject.name.Names;
 
 public class BlobStoreModulesChooser {
+    private static final String UNENCRYPTED = "unencrypted";
+
     static class CassandraBlobStoreDAODeclarationModule extends AbstractModule {
         @Override
         protected void configure() {
             install(new CassandraBlobStoreDependenciesModule());
             install(new CassandraBucketModule());
 
-            bind(BlobStoreDAO.class).to(CassandraBlobStoreDAO.class);
+            bind(BlobStoreDAO.class).annotatedWith(Names.named(UNENCRYPTED)).to(CassandraBlobStoreDAO.class);
         }
     }
 
@@ -66,12 +73,37 @@ public class BlobStoreModulesChooser {
             install(new S3BlobStoreModule());
             install(new DefaultBucketModule());
 
-            bind(BlobStoreDAO.class).to(S3BlobStoreDAO.class);
+            bind(BlobStoreDAO.class).annotatedWith(Names.named(UNENCRYPTED)).to(S3BlobStoreDAO.class);
+        }
+    }
+
+    static class NoEncryptionModule extends AbstractModule {
+        @Provides
+        BlobStoreDAO blobStoreDAO(@Named(UNENCRYPTED) BlobStoreDAO unencrypted) {
+            return unencrypted;
+        }
+    }
+
+    static class EncryptionModule extends AbstractModule {
+        private final CryptoConfig cryptoConfig;
+
+        EncryptionModule(CryptoConfig cryptoConfig) {
+            this.cryptoConfig = cryptoConfig;
+        }
+
+        @Provides
+        BlobStoreDAO blobStoreDAO(@Named(UNENCRYPTED) BlobStoreDAO unencrypted) {
+            return new AESBlobStoreDAO(unencrypted, cryptoConfig);
+        }
+
+        @Provides
+        CryptoConfig cryptoConfig() {
+            return cryptoConfig;
         }
     }
 
     static class StoragePolicyConfigurationSanityEnforcementModule extends AbstractModule {
-        private BlobStoreConfiguration choosingConfiguration;
+        private final BlobStoreConfiguration choosingConfiguration;
 
         StoragePolicyConfigurationSanityEnforcementModule(BlobStoreConfiguration choosingConfiguration) {
             this.choosingConfiguration = choosingConfiguration;
@@ -94,6 +126,7 @@ public class BlobStoreModulesChooser {
     @VisibleForTesting
     public static List<Module> chooseModules(BlobStoreConfiguration choosingConfiguration) {
         return ImmutableList.<Module>builder()
+            .add(chooseEncryptionModule(choosingConfiguration.getCryptoConfig()))
             .add(chooseBlobStoreDAOModule(choosingConfiguration.getImplementation()))
             .add(chooseStoragePolicyModule(choosingConfiguration.storageStrategy()))
             .add(new StoragePolicyConfigurationSanityEnforcementModule(choosingConfiguration))
@@ -111,6 +144,11 @@ public class BlobStoreModulesChooser {
         }
     }
 
+    public static Module chooseEncryptionModule(Optional<CryptoConfig> cryptoConfig) {
+        Optional<Module> encryptionModule = cryptoConfig.map(EncryptionModule::new);
+        return encryptionModule.orElse(new NoEncryptionModule());
+    }
+
     private static Module chooseStoragePolicyModule(StorageStrategy storageStrategy) {
         switch (storageStrategy) {
             case DEDUPLICATION:
diff --git a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java
index 2452d41..6486a0e 100644
--- a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java
+++ b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java
@@ -30,7 +30,8 @@ class BlobStoreCacheModulesChooserTest {
         assertThat(BlobStoreCacheModulesChooser.chooseModules(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication()))
+                    .deduplication()
+                    .noCryptoConfig()))
             .hasSize(1)
             .first()
             .isInstanceOf(BlobStoreCacheModulesChooser.CacheDisabledModule.class);
@@ -41,7 +42,8 @@ class BlobStoreCacheModulesChooserTest {
         assertThat(BlobStoreCacheModulesChooser.chooseModules(BlobStoreConfiguration.builder()
                 .s3()
                 .enableCache()
-                .deduplication()))
+                .deduplication()
+                .noCryptoConfig()))
             .hasSize(2)
             .allSatisfy(module ->
                 assertThat(module).isOfAnyClassIn(
diff --git a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java
index 2625a3c..a0c7ef9 100644
--- a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java
+++ b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java
@@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.apache.commons.configuration2.PropertiesConfiguration;
 import org.apache.james.FakePropertiesProvider;
+import org.apache.james.blob.aes.CryptoConfig;
 import org.apache.james.modules.mailbox.ConfigurationComponent;
 import org.apache.james.server.blob.deduplication.StorageStrategy;
 import org.junit.jupiter.api.Test;
@@ -67,6 +68,96 @@ class BlobStoreConfigurationTest {
     }
 
     @Test
+    void encryptionShouldRequirePassword() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", "s3");
+        configuration.addProperty("deduplication.enable", false);
+        configuration.addProperty("encryption.aes.enable", true);
+        // Hex.encode("salty".getBytes(StandardCharsets.UTF_8))
+        configuration.addProperty("encryption.aes.salt", "73616c7479");
+        FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
+            .register(ConfigurationComponent.NAME, configuration)
+            .build();
+
+        assertThatThrownBy(() -> parse(propertyProvider))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    void encryptionShouldRequireSalt() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", "cassandra");
+        configuration.addProperty("deduplication.enable", false);
+        configuration.addProperty("encryption.aes.enable", true);
+        configuration.addProperty("encryption.aes.password", "salty");
+        FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
+            .register(ConfigurationComponent.NAME, configuration)
+            .build();
+
+        assertThatThrownBy(() -> parse(propertyProvider))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    void encryptionShouldBeDisabledByDefault() throws Exception {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", "cassandra");
+        configuration.addProperty("deduplication.enable", false);
+        FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
+            .register(ConfigurationComponent.NAME, configuration)
+            .build();
+
+        assertThat(parse(propertyProvider))
+            .isEqualTo(BlobStoreConfiguration.builder()
+                .cassandra()
+                .disableCache()
+                .passthrough()
+                .noCryptoConfig());
+    }
+
+    @Test
+    void encryptionShouldBeDisableable() throws Exception {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", "cassandra");
+        configuration.addProperty("deduplication.enable", false);
+        configuration.addProperty("encryption.aes.enable", false);
+        FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
+            .register(ConfigurationComponent.NAME, configuration)
+            .build();
+
+        assertThat(parse(propertyProvider))
+            .isEqualTo(BlobStoreConfiguration.builder()
+                .cassandra()
+                .disableCache()
+                .passthrough()
+                .noCryptoConfig());
+    }
+
+    @Test
+    void encryptionCanBeActivated() throws Exception {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", "cassandra");
+        configuration.addProperty("deduplication.enable", false);
+        configuration.addProperty("encryption.aes.enable", true);
+        configuration.addProperty("encryption.aes.password", "myPass");
+        // Hex.encode("salty".getBytes(StandardCharsets.UTF_8))
+        configuration.addProperty("encryption.aes.salt", "73616c7479");
+        FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
+            .register(ConfigurationComponent.NAME, configuration)
+            .build();
+
+        assertThat(parse(propertyProvider))
+            .isEqualTo(BlobStoreConfiguration.builder()
+                .cassandra()
+                .disableCache()
+                .passthrough()
+                .cryptoConfig(CryptoConfig.builder()
+                    .password("myPass".toCharArray())
+                    .salt("73616c7479")
+                    .build()));
+    }
+
+    @Test
     void provideChoosingConfigurationShouldThrowWhenPropertyFieldIsNotInSupportedList() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", "gabouzomeuh");
@@ -88,7 +179,8 @@ class BlobStoreConfigurationTest {
             .isEqualTo(BlobStoreConfiguration.builder()
                     .cassandra()
                     .disableCache()
-                    .passthrough());
+                    .passthrough()
+                    .noCryptoConfig());
     }
 
     @Test
@@ -104,7 +196,8 @@ class BlobStoreConfigurationTest {
             .isEqualTo(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication());
+                    .deduplication()
+                    .noCryptoConfig());
     }
 
     @Test
@@ -120,7 +213,8 @@ class BlobStoreConfigurationTest {
             .isEqualTo(BlobStoreConfiguration.builder()
                     .cassandra()
                     .disableCache()
-                    .passthrough());
+                    .passthrough()
+                    .noCryptoConfig());
     }
 
     @Test
diff --git a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java
index 51f1cd1..60b8d6d 100644
--- a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java
+++ b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java
@@ -21,6 +21,7 @@ package org.apache.james.modules.blobstore;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import org.apache.james.blob.aes.CryptoConfig;
 import org.junit.jupiter.api.Test;
 
 class BlobStoreModulesChooserTest {
@@ -30,9 +31,10 @@ class BlobStoreModulesChooserTest {
         assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication()))
-            .first()
-            .isInstanceOf(BlobStoreModulesChooser.ObjectStorageBlobStoreDAODeclarationModule.class);
+                    .deduplication()
+                    .noCryptoConfig()))
+            .filteredOn(module -> module instanceof BlobStoreModulesChooser.ObjectStorageBlobStoreDAODeclarationModule)
+            .hasSize(1);
     }
 
     @Test
@@ -40,8 +42,35 @@ class BlobStoreModulesChooserTest {
         assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.builder()
                 .cassandra()
                 .disableCache()
-                .passthrough()))
-            .first()
-            .isInstanceOf(BlobStoreModulesChooser.CassandraBlobStoreDAODeclarationModule.class);
+                .passthrough()
+                .noCryptoConfig()))
+            .filteredOn(module -> module instanceof BlobStoreModulesChooser.CassandraBlobStoreDAODeclarationModule)
+            .hasSize(1);
+    }
+
+    @Test
+    void provideBlobStoreShouldReturnNoEncryptionWhenNoneConfigured() {
+        assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.builder()
+                    .s3()
+                    .disableCache()
+                    .deduplication()
+                    .noCryptoConfig()))
+            .filteredOn(module -> module instanceof BlobStoreModulesChooser.NoEncryptionModule)
+            .hasSize(1);
+    }
+
+    @Test
+    void provideBlobStoreShouldReturnEncryptionWhenConfigured() {
+        assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.builder()
+                .cassandra()
+                .disableCache()
+                .passthrough()
+                .cryptoConfig(CryptoConfig.builder()
+                    .password("myPass".toCharArray())
+                    // Hex.encode("salty".getBytes(StandardCharsets.UTF_8))
+                    .salt("73616c7479")
+                    .build())))
+            .filteredOn(module -> module instanceof BlobStoreModulesChooser.EncryptionModule)
+            .hasSize(1);
     }
 }
\ No newline at end of file
diff --git a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/validation/StorageStrategyValidationEventSourcingSystemTest.java b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/validation/StorageStrategyValidationEventSourcingSystemTest.java
index ed64f5e..8788ecc 100644
--- a/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/validation/StorageStrategyValidationEventSourcingSystemTest.java
+++ b/server/container/guice/distributed/src/test/java/org/apache/james/modules/blobstore/validation/StorageStrategyValidationEventSourcingSystemTest.java
@@ -45,7 +45,8 @@ public class StorageStrategyValidationEventSourcingSystemTest {
     void startingForTheFirstTimeShouldSucceedWhenPassThrough() {
         StartUpCheck.CheckResult checkResult = testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .passthrough());
+            .passthrough()
+            .noCryptoConfig());
 
         assertThat(checkResult.getResultType()).isEqualTo(StartUpCheck.ResultType.GOOD);
     }
@@ -54,7 +55,8 @@ public class StorageStrategyValidationEventSourcingSystemTest {
     void startingForTheFirstTimeShouldSucceedWhenDeduplication() {
         StartUpCheck.CheckResult checkResult = testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .deduplication());
+            .deduplication()
+            .noCryptoConfig());
 
         assertThat(checkResult.getResultType()).isEqualTo(StartUpCheck.ResultType.GOOD);
     }
@@ -63,11 +65,13 @@ public class StorageStrategyValidationEventSourcingSystemTest {
     void startingShouldSucceedWhenTurningOnDeduplication() {
         testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .passthrough());
+            .passthrough()
+            .noCryptoConfig());
 
         StartUpCheck.CheckResult checkResult = testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .deduplication());
+            .deduplication()
+            .noCryptoConfig());
 
         assertThat(checkResult.getResultType()).isEqualTo(StartUpCheck.ResultType.GOOD);
     }
@@ -76,11 +80,13 @@ public class StorageStrategyValidationEventSourcingSystemTest {
     void startingShouldFailWhenTurningOffDeduplication() {
         testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .deduplication());
+            .deduplication()
+            .noCryptoConfig());
 
         StartUpCheck.CheckResult checkResult = testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .passthrough());
+            .passthrough()
+            .noCryptoConfig());
 
         assertThat(checkResult.getResultType()).isEqualTo(StartUpCheck.ResultType.BAD);
     }
@@ -89,10 +95,12 @@ public class StorageStrategyValidationEventSourcingSystemTest {
     void validatingSeveralTimeTheSameStrategyShouldNotAddEventsToTheHistory() {
         testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .deduplication());
+            .deduplication()
+            .noCryptoConfig());
         testee.validate(BlobStoreConfiguration.builder().implementation(BlobStoreImplName.S3)
             .disableCache()
-            .deduplication());
+            .deduplication()
+            .noCryptoConfig());
 
         History history = Mono.from(eventStore.getEventsOfAggregate(RegisterStorageStrategyCommandHandler.AGGREGATE_ID)).block();
 
diff --git a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java
index 512cc61..33d0501 100644
--- a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java
@@ -45,7 +45,8 @@ public class RabbitMQAwsS3SendMDNMethodTest extends SendMDNMethodTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java
index e68eb91..1d74754 100644
--- a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java
+++ b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java
@@ -44,7 +44,8 @@ class RabbitMQAwsS3SpamAssassinContractTest implements SpamAssassinContract {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java
index cb4eefe..99b4058 100644
--- a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java
+++ b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java
@@ -79,7 +79,8 @@ public class RabbitMQAwsS3Stepdefs {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build();
 
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java
index 7d9767e..84904a9 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java
@@ -42,7 +42,8 @@ class DistributedAuthenticationTest implements AuthenticationContract {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDownloadTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDownloadTest.java
index 04e057b..eb30676 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDownloadTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDownloadTest.java
@@ -47,7 +47,8 @@ public class DistributedDownloadTest implements DownloadContract {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
index 80f0194..b1b1df3 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
@@ -43,7 +43,8 @@ public class DistributedEchoMethodTest implements EchoMethodContract {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailChangeMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailChangeMethodTest.java
index d471d81..778bcda 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailChangeMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailChangeMethodTest.java
@@ -43,7 +43,8 @@ public class DistributedEmailChangeMethodTest implements EmailChangesMethodContr
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailGetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailGetMethodTest.java
index 01af00e..86d1e8e 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailGetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailGetMethodTest.java
@@ -47,7 +47,8 @@ public class DistributedEmailGetMethodTest implements EmailGetMethodContract {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java
index 235118b..3562c64 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodNoViewTest.java
@@ -44,7 +44,8 @@ public class DistributedEmailQueryMethodNoViewTest implements EmailQueryMethodCo
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .build())
         .extension(ELASTIC_SEARCH_EXTENSION)
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodTest.java
index 9f6c24f..e05c3a8 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailQueryMethodTest.java
@@ -43,7 +43,8 @@ public class DistributedEmailQueryMethodTest implements EmailQueryMethodContract
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .build())
         .extension(ELASTIC_SEARCH_EXTENSION)
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSetMethodTest.java
index b103eac..2e82789 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSetMethodTest.java
@@ -51,7 +51,8 @@ public class DistributedEmailSetMethodTest implements EmailSetMethodContract {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(ELASTIC_SEARCH_EXTENSION)
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodtest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodtest.java
index c7164f9..421aa16 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodtest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodtest.java
@@ -48,7 +48,8 @@ class DistributedEmailSubmissionSetMethodtest implements EmailSubmissionSetMetho
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(ELASTIC_SEARCH_EXTENSION)
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedIdentityGetTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedIdentityGetTest.java
index 0475b48..ffd004b 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedIdentityGetTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedIdentityGetTest.java
@@ -42,7 +42,8 @@ class DistributedIdentityGetTest implements IdentityGetContract {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxChangeMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxChangeMethodTest.java
index 76d5c5f..25046da 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxChangeMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxChangeMethodTest.java
@@ -45,7 +45,8 @@ public class DistributedMailboxChangeMethodTest implements MailboxChangesMethodC
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
index 0235d09..e6170a4 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
@@ -45,7 +45,8 @@ public class DistributedMailboxGetMethodTest implements MailboxGetMethodContract
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxQueryMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxQueryMethodTest.java
index c81a477..ea72456 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxQueryMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxQueryMethodTest.java
@@ -43,7 +43,8 @@ public class DistributedMailboxQueryMethodTest implements MailboxQueryMethodCont
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .build())
         .extension(ELASTIC_SEARCH_EXTENSION)
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java
index 7c98863..db1e1db 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java
@@ -48,7 +48,8 @@ public class DistributedMailboxSetMethodTest implements MailboxSetMethodContract
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java
index b278584..c89a915 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedProvisioningTest.java
@@ -41,7 +41,8 @@ public class DistributedProvisioningTest implements ProvisioningContract {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java
index ae801b8..de8d786 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java
@@ -41,7 +41,8 @@ public class DistributedSessionRouteTest implements SessionRoutesContract {
             .configurationFromClasspath()
             .blobStore(BlobStoreConfiguration.s3()
                 .disableCache()
-                .passthrough())
+                .passthrough()
+                .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedUploadTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedUploadTest.java
index 9d432db..b7ffbed 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedUploadTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedUploadTest.java
@@ -41,7 +41,8 @@ public class DistributedUploadTest implements UploadContract {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseGetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseGetMethodTest.java
index b318e97..56e1919 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseGetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseGetMethodTest.java
@@ -41,7 +41,8 @@ public class DistributedVacationResponseGetMethodTest implements VacationRespons
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseSetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseSetMethodTest.java
index 57a746a..091e744 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseSetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedVacationResponseSetMethodTest.java
@@ -41,7 +41,8 @@ public class DistributedVacationResponseSetMethodTest implements VacationRespons
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedWebSocketTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedWebSocketTest.java
index 6445ae5..3d1cccb 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedWebSocketTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedWebSocketTest.java
@@ -41,7 +41,8 @@ public class DistributedWebSocketTest implements WebSocketContract {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/ReadLevelTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/ReadLevelTest.java
index 4234152..f19e222 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/ReadLevelTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/ReadLevelTest.java
@@ -108,7 +108,8 @@ public class ReadLevelTest {
             .blobStore(BlobStoreConfiguration.builder()
                 .cassandra()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java
index 9a0fb33..20fd522 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java
@@ -131,7 +131,8 @@ class ConsistencyTasksIntegrationTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
index fce1e7d..3e1a135 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
@@ -120,7 +120,8 @@ class FixingGhostMailboxTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java
index 727de20..88c0902 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java
@@ -49,7 +49,8 @@ class RabbitMQAuthorizedEndpointsTest extends AuthorizedEndpointsTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
index 0484fc4..2ab7bea 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
@@ -209,7 +209,8 @@ class RabbitMQEventDeadLettersIntegrationTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java
index 187e89c..43a1461 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java
@@ -43,7 +43,8 @@ class RabbitMQFastViewProjectionHealthCheckIntegrationTest extends FastViewProje
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java
index dbeaae4..f9970cd 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java
@@ -43,7 +43,8 @@ class RabbitMQForwardIntegrationTest extends ForwardIntegrationTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
index 42e975a..6a4f0ac 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
@@ -49,7 +49,8 @@ class RabbitMQJwtFilterIntegrationTest extends JwtFilterIntegrationTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java
index 5252864..8a5f916 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java
@@ -88,7 +88,8 @@ class RabbitMQReindexingWithEventDeadLettersTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(dockerElasticSearch)
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQUnauthorizedEndpointsTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQUnauthorizedEndpointsTest.java
index 45c8fc5..0296304 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQUnauthorizedEndpointsTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQUnauthorizedEndpointsTest.java
@@ -73,7 +73,8 @@ class RabbitMQUnauthorizedEndpointsTest extends UnauthorizedEndpointsTest {
             .blobStore(BlobStoreConfiguration.builder()
                 .s3()
                 .disableCache()
-                .deduplication())
+                .deduplication()
+                .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
index 038fad2..088d5cd 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationImmutableTest.java
@@ -62,7 +62,8 @@ class RabbitMQWebAdminServerIntegrationImmutableTest extends WebAdminServerInteg
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
index 64cf61d..019412a 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
@@ -67,7 +67,8 @@ class RabbitMQWebAdminServerIntegrationTest extends WebAdminServerIntegrationTes
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationImmutableTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationImmutableTest.java
index b969e49..b78bbd5 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationImmutableTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationImmutableTest.java
@@ -72,7 +72,8 @@ class RabbitMQWebAdminServerTaskSerializationIntegrationImmutableTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
index ca7190c..95fa954 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
@@ -102,7 +102,8 @@ class RabbitMQWebAdminServerTaskSerializationIntegrationTest {
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
index 82a3123..2cf535f 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
@@ -52,7 +52,8 @@ class RabbitMQDeletedMessageVaultIntegrationTest extends DeletedMessageVaultInte
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(ES_EXTENSION)
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
index e5f9323..2cd5c90 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
@@ -50,7 +50,8 @@ class RabbitMQLinshareBlobExportMechanismIntegrationTest extends LinshareBlobExp
             .blobStore(BlobStoreConfiguration.builder()
                     .s3()
                     .disableCache()
-                    .deduplication())
+                    .deduplication()
+                    .noCryptoConfig())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 07/08: JAMES-3524 ChangeLog entry

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 4169d88eb99b358d5bea559c6c96f4a13ab96e4f
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 26 15:26:20 2021 +0700

    JAMES-3524 ChangeLog entry
---
 CHANGELOG.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26f233d..e9f0516 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 
 ## [Unreleased]
 
+### Added
+ - JAMES-3524 Support symmetric encryption support on top of BlobStore
+
 ## [3.6.0] - 2021-03-16
 
 ### Added

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 03/08: JAMES-3524 Write a BlobStoreDAO wrapper performing AES encryption

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 289ccbb0f425170f58cc3db1eab712b0fdf013da
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Mar 25 14:56:32 2021 +0700

    JAMES-3524 Write a BlobStoreDAO wrapper performing AES encryption
    
    Uses byte array as an intermediate data structure...
    
    Inspiration: Jean Helou, AESPayloadCodec
    
    https://github.com/apache/james-project/blame/james-project-3.5.0/server/blob/blob-objectstorage/src/main/java/org/apache/james/blob/objectstorage/AESPayloadCodec.java
---
 server/blob/blob-aes/pom.xml                       |  12 ++
 .../org/apache/james/blob/aes/AESBlobStoreDAO.java | 155 +++++++++++++++++++++
 .../apache/james/blob/aes/AESBlobStoreDAOTest.java |  86 ++++++++++++
 3 files changed, 253 insertions(+)

diff --git a/server/blob/blob-aes/pom.xml b/server/blob/blob-aes/pom.xml
index dc1cf0a..c67ff33 100644
--- a/server/blob/blob-aes/pom.xml
+++ b/server/blob/blob-aes/pom.xml
@@ -53,6 +53,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.github.fge</groupId>
+            <artifactId>throwing-lambdas</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.crypto.tink</groupId>
             <artifactId>tink</artifactId>
             <version>1.5.0</version>
@@ -61,6 +65,14 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.projectreactor</groupId>
+            <artifactId>reactor-core</artifactId>
+        </dependency>
     </dependencies>
 
 
diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java
new file mode 100644
index 0000000..3d39278
--- /dev/null
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/AESBlobStoreDAO.java
@@ -0,0 +1,155 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.blob.aes;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BlobStoreDAO;
+import org.apache.james.blob.api.BucketName;
+import org.apache.james.blob.api.ObjectNotFoundException;
+import org.apache.james.blob.api.ObjectStoreIOException;
+import org.reactivestreams.Publisher;
+
+import com.github.fge.lambdas.Throwing;
+import com.google.common.base.Preconditions;
+import com.google.common.io.ByteSource;
+import com.google.crypto.tink.Aead;
+import com.google.crypto.tink.aead.AeadConfig;
+import com.google.crypto.tink.subtle.AesGcmJce;
+
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
+public class AESBlobStoreDAO implements BlobStoreDAO {
+    private static final byte[] EMPTY_ASSOCIATED_DATA = new byte[0];
+    private static final int PBKDF2_ITERATIONS = 65536;
+    private static final int KEY_SIZE = 256;
+    private static final String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA256";
+
+    private final BlobStoreDAO underlying;
+    private final Aead aead;
+
+    public AESBlobStoreDAO(BlobStoreDAO underlying, CryptoConfig cryptoConfig) {
+        this.underlying = underlying;
+
+        try {
+            AeadConfig.register();
+
+            SecretKey secretKey = deriveKey(cryptoConfig);
+            aead = new AesGcmJce(secretKey.getEncoded());
+        } catch (GeneralSecurityException e) {
+            throw new RuntimeException("Error while starting AESPayloadCodec", e);
+        }
+    }
+
+    private static SecretKey deriveKey(CryptoConfig cryptoConfig) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        byte[] saltBytes = cryptoConfig.salt();
+        SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
+        PBEKeySpec spec = new PBEKeySpec(cryptoConfig.password(), saltBytes, PBKDF2_ITERATIONS, KEY_SIZE);
+        return skf.generateSecret(spec);
+    }
+
+    public byte[] encrypt(byte[] input) {
+        try {
+            return aead.encrypt(input, EMPTY_ASSOCIATED_DATA);
+        } catch (GeneralSecurityException e) {
+            throw new RuntimeException("Unable to build payload for object storage, failed to encrypt", e);
+        }
+    }
+
+    public byte[] decrypt(byte[] ciphertext) throws IOException {
+        try {
+            return aead.decrypt(ciphertext, EMPTY_ASSOCIATED_DATA);
+        } catch (GeneralSecurityException e) {
+            throw new IOException("Incorrect crypto setup", e);
+        }
+    }
+
+    @Override
+    public InputStream read(BucketName bucketName, BlobId blobId) throws ObjectStoreIOException, ObjectNotFoundException {
+        return Mono.from(underlying.readBytes(bucketName, blobId))
+            .map(Throwing.function(this::decrypt))
+            .map(ByteArrayInputStream::new)
+            .subscribeOn(Schedulers.elastic())
+            .block();
+    }
+
+    @Override
+    public Publisher<byte[]> readBytes(BucketName bucketName, BlobId blobId) {
+        return Mono.from(underlying.readBytes(bucketName, blobId))
+            .map(Throwing.function(this::decrypt));
+    }
+
+    @Override
+    public Publisher<Void> save(BucketName bucketName, BlobId blobId, byte[] data) {
+        Preconditions.checkNotNull(bucketName);
+        Preconditions.checkNotNull(blobId);
+        Preconditions.checkNotNull(data);
+
+        return Mono.just(data)
+            .flatMap(payload -> Mono.fromCallable(() -> encrypt(payload)).subscribeOn(Schedulers.parallel()))
+            .flatMap(encryptedPayload -> Mono.from(underlying.save(bucketName, blobId, encryptedPayload)))
+            .onErrorMap(e -> new ObjectStoreIOException("Exception occurred while saving bytearray", e));
+    }
+
+    @Override
+    public Publisher<Void> save(BucketName bucketName, BlobId blobId, InputStream inputStream) {
+        Preconditions.checkNotNull(bucketName);
+        Preconditions.checkNotNull(blobId);
+        Preconditions.checkNotNull(inputStream);
+
+        return Mono.just(inputStream)
+            .flatMap(data -> Mono.fromCallable(() -> IOUtils.toByteArray(inputStream)).subscribeOn(Schedulers.parallel()))
+            .flatMap(encryptedData -> Mono.from(save(bucketName, blobId, encryptedData)))
+            .onErrorMap(e -> new ObjectStoreIOException("Exception occurred while saving bytearray", e));
+    }
+
+    @Override
+    public Publisher<Void> save(BucketName bucketName, BlobId blobId, ByteSource content) {
+        Preconditions.checkNotNull(bucketName);
+        Preconditions.checkNotNull(blobId);
+        Preconditions.checkNotNull(content);
+
+        return Mono.using(content::openStream,
+            in -> Mono.from(save(bucketName, blobId, in)),
+            Throwing.consumer(InputStream::close));
+    }
+
+    @Override
+    public Publisher<Void> delete(BucketName bucketName, BlobId blobId) {
+        return underlying.delete(bucketName, blobId);
+    }
+
+    @Override
+    public Publisher<Void> deleteBucket(BucketName bucketName) {
+        return underlying.deleteBucket(bucketName);
+    }
+}
diff --git a/server/blob/blob-aes/src/test/java/org/apache/james/blob/aes/AESBlobStoreDAOTest.java b/server/blob/blob-aes/src/test/java/org/apache/james/blob/aes/AESBlobStoreDAOTest.java
new file mode 100644
index 0000000..8be3728
--- /dev/null
+++ b/server/blob/blob-aes/src/test/java/org/apache/james/blob/aes/AESBlobStoreDAOTest.java
@@ -0,0 +1,86 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.blob.aes;
+
+import static org.apache.james.blob.api.BlobStoreDAOFixture.SHORT_BYTEARRAY;
+import static org.apache.james.blob.api.BlobStoreDAOFixture.TEST_BLOB_ID;
+import static org.apache.james.blob.api.BlobStoreDAOFixture.TEST_BUCKET_NAME;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+
+import org.apache.james.blob.api.BlobStoreDAO;
+import org.apache.james.blob.api.BlobStoreDAOContract;
+import org.apache.james.blob.memory.MemoryBlobStoreDAO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.io.ByteSource;
+
+import reactor.core.publisher.Mono;
+
+class AESBlobStoreDAOTest implements BlobStoreDAOContract {
+    private static final String SAMPLE_SALT = "c603a7327ee3dcbc031d8d34b1096c605feca5e1";
+    private static final CryptoConfig CRYPTO_CONFIG = CryptoConfig.builder()
+        .salt(SAMPLE_SALT)
+        .password("testing".toCharArray())
+        .build();
+
+    private AESBlobStoreDAO testee;
+    private MemoryBlobStoreDAO underlying;
+
+    @BeforeEach
+    void setUp() {
+        underlying = new MemoryBlobStoreDAO();
+        testee = new AESBlobStoreDAO(underlying, CRYPTO_CONFIG);
+    }
+
+    @Override
+    public BlobStoreDAO testee() {
+        return testee;
+    }
+
+    @Test
+    void underlyingDataShouldBeEncrypted() {
+        Mono.from(testee.save(TEST_BUCKET_NAME, TEST_BLOB_ID, SHORT_BYTEARRAY)).block();
+
+        byte[] bytes = Mono.from(underlying.readBytes(TEST_BUCKET_NAME, TEST_BLOB_ID)).block();
+
+        assertThat(bytes).isNotEqualTo(SHORT_BYTEARRAY);
+    }
+
+    @Test
+    void underlyingDataShouldBeEncryptedWhenUsingStream() {
+        Mono.from(testee.save(TEST_BUCKET_NAME, TEST_BLOB_ID, new ByteArrayInputStream(SHORT_BYTEARRAY))).block();
+
+        byte[] bytes = Mono.from(underlying.readBytes(TEST_BUCKET_NAME, TEST_BLOB_ID)).block();
+
+        assertThat(bytes).isNotEqualTo(SHORT_BYTEARRAY);
+    }
+
+    @Test
+    void underlyingDataShouldBeEncryptedWhenUsingByteSource() {
+        Mono.from(testee.save(TEST_BUCKET_NAME, TEST_BLOB_ID, ByteSource.wrap(SHORT_BYTEARRAY))).block();
+
+        byte[] bytes = Mono.from(underlying.readBytes(TEST_BUCKET_NAME, TEST_BLOB_ID)).block();
+
+        assertThat(bytes).isNotEqualTo(SHORT_BYTEARRAY);
+    }
+}
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 05/08: JAMES-3524 Configuration should help setting up AES encryption

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 925b0d5fee9cc621a5257b303b289d82f6358533
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 26 15:16:39 2021 +0700

    JAMES-3524 Configuration should help setting up AES encryption
---
 .../destination/conf/blob.properties                       | 14 +++++++++++++-
 .../cassandra-rabbitmq/destination/conf/blob.properties    | 14 +++++++++++++-
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
index c70b3c7..2e06135 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
@@ -5,13 +5,25 @@
 # Mandatory, allowed values are: cassandra, s3
 implementation=s3
 
-# ========================================= ObjectStorage deduplication ========================================
+# ========================================= Deduplication ========================================
 # If you choose to enable deduplication, the mails with the same content will be stored only once.
 # Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all
 # the mails sharing the same content once one is deleted.
 # Mandatory, Allowed values are: true, false
 deduplication.enable=false
 
+# ========================================= Encryption ========================================
+# If you choose to enable encryption, the blob content will be encrypted before storing them in the BlobStore.
+# Warning: Once this feature is enabled, there is no turning back as turning it off will lead to all content being
+# encrypted. This comes at a performance impact but presents you from leaking data if, for instance the third party
+# offering you a S3 service is compromised.
+# Optional, Allowed values are: true, false, defaults to false
+encryption.aes.enable=false
+
+# Mandatory (if AES encryption is enabled) salt and password. Salt needs to be an hexadecimal encoded string
+#encryption.aes.password=xxx
+#encryption.aes.salt=73616c7479
+
 # ========================================= Cassandra BlobStore Cache ======================================
 # A cassandra cache can be enabled to reduce latency when reading small blobs frequently
 # A dedicated keyspace with a replication factor of one is then used
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
index 1797038..481ef70 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
@@ -5,13 +5,25 @@
 # Mandatory, allowed values are: cassandra, s3
 implementation=s3
 
-# ========================================= ObjectStorage deduplication ========================================
+# ========================================= Deduplication ========================================
 # If you choose to enable deduplication, the mails with the same content will be stored only once.
 # Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all
 # the mails sharing the same content once one is deleted.
 # Mandatory, Allowed values are: true, false
 deduplication.enable=false
 
+# ========================================= Encryption ========================================
+# If you choose to enable encryption, the blob content will be encrypted before storing them in the BlobStore.
+# Warning: Once this feature is enabled, there is no turning back as turning it off will lead to all content being
+# encrypted. This comes at a performance impact but presents you from leaking data if, for instance the third party
+# offering you a S3 service is compromised.
+# Optional, Allowed values are: true, false, defaults to false
+encryption.aes.enable=false
+
+# Mandatory (if AES encryption is enabled) salt and password. Salt needs to be an hexadecimal encoded string
+#encryption.aes.password=xxx
+#encryption.aes.salt=73616c7479
+
 # ========================================= Cassandra BlobStore Cache ======================================
 # A cassandra cache can be enabled to reduce latency when reading small blobs frequently
 # A dedicated keyspace with a replication factor of one is then used

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 06/08: JAMES-3524 Document blobStore encryption

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 8ead5fcf073a8bb426dc3e4a66431e32a7045dc6
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 26 15:25:19 2021 +0700

    JAMES-3524 Document blobStore encryption
---
 .../pages/distributed/configure/blobstore.adoc     | 27 ++++++++++++++++
 src/site/xdoc/server/config-blobstore.xml          | 36 +++++++++++++++++++++-
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/docs/modules/servers/pages/distributed/configure/blobstore.adoc b/docs/modules/servers/pages/distributed/configure/blobstore.adoc
index 139c961..c256b13 100644
--- a/docs/modules/servers/pages/distributed/configure/blobstore.adoc
+++ b/docs/modules/servers/pages/distributed/configure/blobstore.adoc
@@ -35,6 +35,33 @@ Consequently, all the requested deletions will not be performed, meaning that bl
 
 NOTE: If you are upgrading from James 3.5 or older, the deduplication was enabled.
 
+=== Encryption choice
+
+Data can be optionally encrypted with a symmetric key using AES before being stored in the blobStore. As many user relies
+on third party for object storage, a compromised third party will not escalate to a data disclosure. Of course, a
+performance price have to be paid, as encryption takes resources.
+
+*encryption.aes.enable* : Optional boolean, defaults to false.
+
+If AES encryption is enabled, then the following properties MUST be present:
+
+ - *encryption.aes.password* : String
+ - *encryption.aes.salt* : Hexadecimal string
+
+WARNING: Once chosen this choice can not be reverted, all the data is either clear or encrypted. Mixed encryption
+is not supported.
+
+Here is an example of how you can generate the above values (be mindful to customize the byte lengths in order to add
+enough entropy.
+
+....
+# Password generation
+openssl rand -base64 64
+
+# Salt generation
+generate salt with : openssl rand -hex 16
+....
+
 === Cassandra BlobStore Cache
 
 A Cassandra cache can be enabled to reduce latency when reading small blobs frequently.
diff --git a/src/site/xdoc/server/config-blobstore.xml b/src/site/xdoc/server/config-blobstore.xml
index 92de092..15ae7b2 100644
--- a/src/site/xdoc/server/config-blobstore.xml
+++ b/src/site/xdoc/server/config-blobstore.xml
@@ -85,7 +85,41 @@
                         Maximum size of stored objects expressed in bytes.</dd>
                 </dl>
             </subsection>
-
+            <subsection name="Encryption choice">
+                <p>
+                    Data can be optionally encrypted with a symmetric key using AES before being stored in the blobStore. As many user relies
+                    on third party for object storage, a compromised third party will not escalate to a data disclosure. Of course, a
+                    performance price have to be paid, as encryption takes resources.
+                </p>
+                <dl>
+                    <dt><strong>encryption.aes.enable</strong></dt>
+                    <dd>Optional boolean, defaults to false</dd>
+                </dl>
+                <p>If AES encryption is enabled, then the following properties MUST be present:</p>
+                <dl>
+                    <dt><strong>encryption.aes.password</strong></dt>
+                    <dd>String</dd>
+                </dl>
+                <dl>
+                    <dt><strong>encryption.aes.salt</strong></dt>
+                    <dd>Hexadecimal string.</dd>
+                </dl>
+                <p><b>WARNING:</b> Once chosen this choice can not be reverted, all the data is either clear or encrypted. Mixed encryption
+                    is not supported.</p>
+                <p>
+                    Here is an example of how you can generate the above values (be mindful to customize the byte lengths in order to add
+                    enough entropy.
+                </p>
+                <pre>
+                    <code>
+# Password generation
+openssl rand -base64 64
+
+# Salt generation
+generate salt with : openssl rand -hex 16
+                    </code>
+                </pre>
+            </subsection>
             <subsection name="ObjectStorage BlobStore Buckets Configuration">
                 <dl>
                     <dt><strong>objectstorage.bucketPrefix</strong></dt>

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 01/08: JAMES-3524 Maven module for AES blob encryption

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 964b7a145ba25cf647936543028d3c4c1a04588c
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Mar 25 14:28:33 2021 +0700

    JAMES-3524 Maven module for AES blob encryption
---
 server/blob/blob-aes/pom.xml | 67 ++++++++++++++++++++++++++++++++++++++++++++
 server/blob/pom.xml          |  1 +
 2 files changed, 68 insertions(+)

diff --git a/server/blob/blob-aes/pom.xml b/server/blob/blob-aes/pom.xml
new file mode 100644
index 0000000..dc1cf0a
--- /dev/null
+++ b/server/blob/blob-aes/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>james-project</artifactId>
+        <version>3.7.0-SNAPSHOT</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>blob-aes</artifactId>
+    <name>Apache James :: Server :: Blob :: AES encryption</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>blob-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-testing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.crypto.tink</groupId>
+            <artifactId>tink</artifactId>
+            <version>1.5.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+
+</project>
\ No newline at end of file
diff --git a/server/blob/pom.xml b/server/blob/pom.xml
index 59e22a2..ff7b1f0 100644
--- a/server/blob/pom.xml
+++ b/server/blob/pom.xml
@@ -33,6 +33,7 @@
     <name>Apache James :: Server :: Blob</name>
 
     <modules>
+        <module>blob-aes</module>
         <module>blob-api</module>
         <module>blob-cassandra</module>
         <module>blob-common</module>

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 02/08: JAMES-3524 Restore classes deleted after S3 blobStore rewrite

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 18676437865ca4926ce799f3d8bba52fef51c186
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Mar 25 14:29:23 2021 +0700

    JAMES-3524 Restore classes deleted after S3 blobStore rewrite
    
    Credit: Jean HELOU
    
    Restored from: https://github.com/apache/james-project/tree/james-project-3.5.0/server/blob/blob-objectstorage/src/main/java/org/apache/james/blob/objectstorage/crypto
---
 .../org/apache/james/blob/aes/CryptoConfig.java    | 45 +++++++++++++++++
 .../apache/james/blob/aes/CryptoConfigBuilder.java | 48 ++++++++++++++++++
 .../org/apache/james/blob/aes/CryptoException.java | 34 +++++++++++++
 .../james/blob/aes/PBKDF2StreamingAeadFactory.java | 59 ++++++++++++++++++++++
 4 files changed, 186 insertions(+)

diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java
new file mode 100644
index 0000000..0625535
--- /dev/null
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfig.java
@@ -0,0 +1,45 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.blob.aes;
+
+import com.google.crypto.tink.subtle.Hex;
+
+public class CryptoConfig {
+
+    public static CryptoConfigBuilder builder() {
+        return new CryptoConfigBuilder();
+    }
+
+    private final String salt;
+    private final char[] password;
+
+    public CryptoConfig(String salt, char[] password) {
+        this.salt = salt;
+        this.password = password;
+    }
+
+    public byte[] salt() {
+        return Hex.decode(salt);
+    }
+
+    public char[] password() {
+        return password;
+    }
+}
\ No newline at end of file
diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java
new file mode 100644
index 0000000..f3c9916
--- /dev/null
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoConfigBuilder.java
@@ -0,0 +1,48 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.blob.aes;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.crypto.tink.subtle.Hex;
+
+public class CryptoConfigBuilder {
+    private String salt;
+    private char[] password;
+
+    CryptoConfigBuilder() {
+    }
+
+    public CryptoConfigBuilder salt(String salt) {
+        this.salt = salt;
+        return this;
+    }
+
+    public CryptoConfigBuilder password(char[] password) {
+        this.password = password;
+        return this;
+    }
+
+    public CryptoConfig build() {
+        Preconditions.checkState(!Strings.isNullOrEmpty(salt));
+        Preconditions.checkState(password != null && password.length > 0);
+        return new CryptoConfig(Hex.encode(Hex.decode(salt)), password);
+    }
+}
\ No newline at end of file
diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoException.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoException.java
new file mode 100644
index 0000000..c8f0a91
--- /dev/null
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/CryptoException.java
@@ -0,0 +1,34 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.blob.aes;
+
+public class CryptoException extends RuntimeException {
+    public CryptoException() {
+        super();
+    }
+
+    public CryptoException(String message) {
+        super(message);
+    }
+
+    public CryptoException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
\ No newline at end of file
diff --git a/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/PBKDF2StreamingAeadFactory.java b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/PBKDF2StreamingAeadFactory.java
new file mode 100644
index 0000000..d3ca2b0
--- /dev/null
+++ b/server/blob/blob-aes/src/main/java/org/apache/james/blob/aes/PBKDF2StreamingAeadFactory.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.blob.aes;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+import com.google.crypto.tink.subtle.AesGcmHkdfStreaming;
+
+public class PBKDF2StreamingAeadFactory {
+    private static final int PBKDF2_ITERATIONS = 65536;
+    private static final int KEY_SIZE = 256;
+    private static final String SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
+    private static final String HKDF_ALGO = "HmacSha256";
+    private static final int KEY_SIZE_IN_BYTES = 32;
+    private static final int SEGMENT_SIZE = 4096;
+    private static final int OFFSET = 0;
+    public static final byte[] EMPTY_ASSOCIATED_DATA = new byte[0];
+
+    public static AesGcmHkdfStreaming newAesGcmHkdfStreaming(CryptoConfig config) {
+        try {
+            SecretKey secretKey = deriveKey(config);
+            return new AesGcmHkdfStreaming(secretKey.getEncoded(), HKDF_ALGO, KEY_SIZE_IN_BYTES, SEGMENT_SIZE, OFFSET);
+        } catch (GeneralSecurityException e) {
+            throw new CryptoException("Incorrect crypto setup", e);
+
+        }
+    }
+
+    private static SecretKey deriveKey(CryptoConfig cryptoConfig)
+        throws NoSuchAlgorithmException, InvalidKeySpecException {
+        byte[] saltBytes = cryptoConfig.salt();
+        SecretKeyFactory skf = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY_ALGORITHM);
+        PBEKeySpec spec = new PBEKeySpec(cryptoConfig.password(), saltBytes, PBKDF2_ITERATIONS, KEY_SIZE);
+        return skf.generateSecret(spec);
+    }
+}
\ No newline at end of file

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org