From 8a323730744d0feac3610f507f07f37385880f48 Mon Sep 17 00:00:00 2001 From: Sebastian Luehr Date: Wed, 14 Oct 2020 10:02:31 +1100 Subject: [PATCH 1/3] Encryption autoconfiguration with AES as a default --- .../CryptoShreddingKeyService.java | 6 +-- .../CryptoShreddingSerializer.java | 12 +++--- .../AesEncrypterDecrypterFactory.java | 23 ---------- .../cryptoshredding/encryption/Decrypter.java | 7 ++++ ...ecrypter.java => DefaultAesDecrypter.java} | 4 +- ...ncrypter.java => DefaultAesEncrypter.java} | 4 +- .../DefaultAesEncrypterDecrypterFactory.java | 20 +++++++++ ...rator.java => DefaultAesKeyGenerator.java} | 7 +--- .../cryptoshredding/encryption/Encrypter.java | 7 ++++ .../encryption/EncrypterDecrypterFactory.java | 7 ++++ .../EncryptionAutoConfiguration.java | 23 ++++++++++ .../encryption/KeyGenerator.java | 7 ++++ .../CryptoShreddingSerializerTest.java | 42 +++++++++---------- ...AesEncrypterDecrypterIntegrationTest.java} | 36 ++++++++++------ ...aultAesEncrypterDecrypterFactoryTest.java} | 10 ++--- 15 files changed, 135 insertions(+), 80 deletions(-) delete mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactory.java create mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Decrypter.java rename src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/{AesDecrypter.java => DefaultAesDecrypter.java} (93%) rename src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/{AesEncrypter.java => DefaultAesEncrypter.java} (95%) create mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterFactory.java rename src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/{AesKeyGenerator.java => DefaultAesKeyGenerator.java} (78%) create mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Encrypter.java create mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncrypterDecrypterFactory.java create mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncryptionAutoConfiguration.java create mode 100644 src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/KeyGenerator.java rename src/test/java/engineering/everest/starterkit/axon/cryptoshredding/{ => encryption}/CryptoShreddingSerializerTest.java (90%) rename src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/{AesEncrypterDecrypterIntegrationTest.java => DefaultAesEncrypterDecrypterIntegrationTest.java} (66%) rename src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/{AesEncrypterDecrypterFactoryTest.java => DefaultDefaultAesEncrypterDecrypterFactoryTest.java} (51%) diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingKeyService.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingKeyService.java index 3372b36..2f84ea2 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingKeyService.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingKeyService.java @@ -1,6 +1,6 @@ package engineering.everest.starterkit.axon.cryptoshredding; -import engineering.everest.starterkit.axon.cryptoshredding.encryption.AesKeyGenerator; +import engineering.everest.starterkit.axon.cryptoshredding.encryption.KeyGenerator; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.MissingEncryptionKeyRecordException; import engineering.everest.starterkit.axon.cryptoshredding.persistence.PersistableSecretKey; import engineering.everest.starterkit.axon.cryptoshredding.persistence.SecretKeyRepository; @@ -15,9 +15,9 @@ public class CryptoShreddingKeyService { private static final String DEFAULT_KEY_TYPE = ""; private final SecretKeyRepository secretKeyRepository; - private final AesKeyGenerator secretKeyGenerator; + private final KeyGenerator secretKeyGenerator; - public CryptoShreddingKeyService(SecretKeyRepository secretKeyRepository, AesKeyGenerator secretKeyGenerator) { + public CryptoShreddingKeyService(SecretKeyRepository secretKeyRepository, KeyGenerator secretKeyGenerator) { this.secretKeyRepository = secretKeyRepository; this.secretKeyGenerator = secretKeyGenerator; } diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializer.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializer.java index 80c07ab..1f5d69c 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializer.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializer.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import engineering.everest.starterkit.axon.cryptoshredding.annotations.EncryptedField; import engineering.everest.starterkit.axon.cryptoshredding.annotations.EncryptionKeyIdentifier; -import engineering.everest.starterkit.axon.cryptoshredding.encryption.AesEncrypterDecrypterFactory; +import engineering.everest.starterkit.axon.cryptoshredding.encryption.EncrypterDecrypterFactory; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.EncryptionKeyDeletedException; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.MissingEncryptionKeyIdentifierAnnotation; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.MissingSerializedEncryptionKeyIdentifierFieldException; @@ -37,16 +37,16 @@ public class CryptoShreddingSerializer implements Serializer { private final Serializer wrappedSerializer; private final CryptoShreddingKeyService cryptoShreddingKeyService; - private final AesEncrypterDecrypterFactory aesEncrypterDecrypterFactory; + private final EncrypterDecrypterFactory encrypterDecrypterFactory; private final ObjectMapper objectMapper; public CryptoShreddingSerializer(@Qualifier("eventSerializer") Serializer wrappedSerializer, CryptoShreddingKeyService cryptoShreddingKeyService, - AesEncrypterDecrypterFactory aesEncrypterDecrypterFactory, + EncrypterDecrypterFactory encrypterDecrypterFactory, ObjectMapper objectMapper) { this.wrappedSerializer = wrappedSerializer; this.cryptoShreddingKeyService = cryptoShreddingKeyService; - this.aesEncrypterDecrypterFactory = aesEncrypterDecrypterFactory; + this.encrypterDecrypterFactory = encrypterDecrypterFactory; this.objectMapper = objectMapper; } @@ -172,7 +172,7 @@ private Map mapAndEncryptAnnotatedFields(Object object, List>() { }); var serializedFieldNameMapping = buildFieldNamingSerializationStrategyIndependentMapping(mappedObject); - var encrypter = aesEncrypterDecrypterFactory.createEncrypter(); + var encrypter = encrypterDecrypterFactory.createEncrypter(); encryptedFields.forEach(field -> { var fieldKey = serializedFieldNameMapping.get(field.getName().toLowerCase()); @@ -193,7 +193,7 @@ private Map decryptAnnotatedFields(Map encrypted Map serializedFieldNameMapping, List encryptedFields, SecretKey secretKey) { - var decrypter = aesEncrypterDecrypterFactory.createDecrypter(); + var decrypter = encrypterDecrypterFactory.createDecrypter(); encryptedFields.forEach(field -> { var serializedFieldKey = serializedFieldNameMapping.get(field.getName().toLowerCase()); diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactory.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactory.java deleted file mode 100644 index 7afaacd..0000000 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package engineering.everest.starterkit.axon.cryptoshredding.encryption; - -import org.springframework.stereotype.Component; - -import java.security.SecureRandom; - -@Component -public class AesEncrypterDecrypterFactory { - - private final SecureRandom secureRandom; - - public AesEncrypterDecrypterFactory() { - this.secureRandom = new SecureRandom(); - } - - public AesEncrypter createEncrypter() { - return new AesEncrypter(secureRandom); - } - - public AesDecrypter createDecrypter() { - return new AesDecrypter(secureRandom); - } -} diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Decrypter.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Decrypter.java new file mode 100644 index 0000000..a780e5c --- /dev/null +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Decrypter.java @@ -0,0 +1,7 @@ +package engineering.everest.starterkit.axon.cryptoshredding.encryption; + +import javax.crypto.SecretKey; + +public interface Decrypter { + String decrypt(SecretKey secretKey, byte[] ciphertext); +} diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesDecrypter.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java similarity index 93% rename from src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesDecrypter.java rename to src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java index feeba3f..05164b6 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesDecrypter.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java @@ -13,13 +13,13 @@ import static javax.crypto.Cipher.DECRYPT_MODE; -public class AesDecrypter { +class DefaultAesDecrypter implements Decrypter { private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5PADDING"; private static final int INITIALIZATION_VECTOR_LENGTH = 16; private final SecureRandom secureRandom; - public AesDecrypter(SecureRandom secureRandom) { + public DefaultAesDecrypter(SecureRandom secureRandom) { this.secureRandom = secureRandom; } diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypter.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java similarity index 95% rename from src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypter.java rename to src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java index 7485c47..2e5af34 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypter.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java @@ -14,13 +14,13 @@ import static java.lang.System.arraycopy; import static javax.crypto.Cipher.ENCRYPT_MODE; -public class AesEncrypter { +class DefaultAesEncrypter implements Encrypter { private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5PADDING"; private static final int INITIALIZATION_VECTOR_LENGTH = 16; private final SecureRandom secureRandom; - public AesEncrypter(SecureRandom secureRandom) { + public DefaultAesEncrypter(SecureRandom secureRandom) { this.secureRandom = secureRandom; } diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterFactory.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterFactory.java new file mode 100644 index 0000000..8492758 --- /dev/null +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterFactory.java @@ -0,0 +1,20 @@ +package engineering.everest.starterkit.axon.cryptoshredding.encryption; + +import java.security.SecureRandom; + +class DefaultAesEncrypterDecrypterFactory implements EncrypterDecrypterFactory { + + private final SecureRandom secureRandom; + + public DefaultAesEncrypterDecrypterFactory() { + this.secureRandom = new SecureRandom(); + } + + public Encrypter createEncrypter() { + return new DefaultAesEncrypter(secureRandom); + } + + public Decrypter createDecrypter() { + return new DefaultAesDecrypter(secureRandom); + } +} diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesKeyGenerator.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesKeyGenerator.java similarity index 78% rename from src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesKeyGenerator.java rename to src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesKeyGenerator.java index 175bbb0..047300e 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesKeyGenerator.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesKeyGenerator.java @@ -1,21 +1,18 @@ package engineering.everest.starterkit.axon.cryptoshredding.encryption; -import org.springframework.stereotype.Component; - import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -@Component -public class AesKeyGenerator { +class DefaultAesKeyGenerator implements engineering.everest.starterkit.axon.cryptoshredding.encryption.KeyGenerator { public static final String ALGORITHM = "AES"; private static final int KEY_SIZE = 256; private final SecureRandom securerandom; private final KeyGenerator keygenerator; - public AesKeyGenerator() throws NoSuchAlgorithmException { + public DefaultAesKeyGenerator() throws NoSuchAlgorithmException { this.securerandom = new SecureRandom(); this.keygenerator = KeyGenerator.getInstance(ALGORITHM); this.keygenerator.init(KEY_SIZE, this.securerandom); diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Encrypter.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Encrypter.java new file mode 100644 index 0000000..dd1f070 --- /dev/null +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/Encrypter.java @@ -0,0 +1,7 @@ +package engineering.everest.starterkit.axon.cryptoshredding.encryption; + +import javax.crypto.SecretKey; + +public interface Encrypter { + byte[] encrypt(SecretKey secretKey, String cleartext); +} diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncrypterDecrypterFactory.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncrypterDecrypterFactory.java new file mode 100644 index 0000000..db466cd --- /dev/null +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncrypterDecrypterFactory.java @@ -0,0 +1,7 @@ +package engineering.everest.starterkit.axon.cryptoshredding.encryption; + +public interface EncrypterDecrypterFactory { + Encrypter createEncrypter(); + + Decrypter createDecrypter(); +} diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncryptionAutoConfiguration.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncryptionAutoConfiguration.java new file mode 100644 index 0000000..1d9c911 --- /dev/null +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/EncryptionAutoConfiguration.java @@ -0,0 +1,23 @@ +package engineering.everest.starterkit.axon.cryptoshredding.encryption; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.security.NoSuchAlgorithmException; + +@Configuration +public class EncryptionAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(EncrypterDecrypterFactory.class) + public EncrypterDecrypterFactory encrypterDecrypterFactory() { + return new DefaultAesEncrypterDecrypterFactory(); + } + + @Bean + @ConditionalOnMissingBean(engineering.everest.starterkit.axon.cryptoshredding.encryption.KeyGenerator.class) + public KeyGenerator keyGenerator() throws NoSuchAlgorithmException { + return new DefaultAesKeyGenerator(); + } +} diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/KeyGenerator.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/KeyGenerator.java new file mode 100644 index 0000000..dabbf1f --- /dev/null +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/KeyGenerator.java @@ -0,0 +1,7 @@ +package engineering.everest.starterkit.axon.cryptoshredding.encryption; + +import javax.crypto.SecretKey; + +public interface KeyGenerator { + SecretKey generateKey(); +} diff --git a/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializerTest.java b/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/CryptoShreddingSerializerTest.java similarity index 90% rename from src/test/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializerTest.java rename to src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/CryptoShreddingSerializerTest.java index 1b9c27d..b7eeb0c 100644 --- a/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/CryptoShreddingSerializerTest.java +++ b/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/CryptoShreddingSerializerTest.java @@ -1,9 +1,9 @@ -package engineering.everest.starterkit.axon.cryptoshredding; +package engineering.everest.starterkit.axon.cryptoshredding.encryption; import com.fasterxml.jackson.databind.ObjectMapper; -import engineering.everest.starterkit.axon.cryptoshredding.encryption.AesDecrypter; -import engineering.everest.starterkit.axon.cryptoshredding.encryption.AesEncrypter; -import engineering.everest.starterkit.axon.cryptoshredding.encryption.AesEncrypterDecrypterFactory; +import engineering.everest.starterkit.axon.cryptoshredding.CryptoShreddingKeyService; +import engineering.everest.starterkit.axon.cryptoshredding.CryptoShreddingSerializer; +import engineering.everest.starterkit.axon.cryptoshredding.TypeDifferentiatedSecretKeyId; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.EncryptionKeyDeletedException; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.MissingEncryptionKeyIdentifierAnnotation; import engineering.everest.starterkit.axon.cryptoshredding.exceptions.MissingSerializedEncryptionKeyIdentifierFieldException; @@ -49,8 +49,8 @@ class CryptoShreddingSerializerTest { private CryptoShreddingSerializer cryptoShreddingSerializerWithMock; private CryptoShreddingSerializer jsonCryptoShreddingSerializer; - private AesEncrypter aesEncrypter; - private AesDecrypter aesDecrypter; + private DefaultAesEncrypter defaultAesEncrypter; + private DefaultAesDecrypter defaultAesDecrypter; @Mock private Serializer mockWrappedSerializer; @@ -59,21 +59,21 @@ class CryptoShreddingSerializerTest { @Mock private CryptoShreddingKeyService cryptoShreddingKeyService; @Mock - private AesEncrypterDecrypterFactory encrypterFactory; + private DefaultAesEncrypterDecrypterFactory encrypterFactory; @BeforeEach void setUp() { cryptoShreddingSerializerWithMock = new CryptoShreddingSerializer(mockWrappedSerializer, cryptoShreddingKeyService, encrypterFactory, new ObjectMapper()); jsonCryptoShreddingSerializer = new CryptoShreddingSerializer(JacksonSerializer.defaultSerializer(), cryptoShreddingKeyService, encrypterFactory, new ObjectMapper()); var secureRandom = new SecureRandom(); - aesEncrypter = new AesEncrypter(secureRandom); - aesDecrypter = new AesDecrypter(secureRandom); + defaultAesEncrypter = new DefaultAesEncrypter(secureRandom); + defaultAesDecrypter = new DefaultAesDecrypter(secureRandom); } @Test void serialize_WillGenerateNewKeyAndEncodeAnnotatedFields() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); @@ -83,7 +83,7 @@ void serialize_WillGenerateNewKeyAndEncodeAnnotatedFields() { @Test void serialize_WillReturnASerializedObjectWithTypeOfSerializedClass() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); var serialized = jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); @@ -137,8 +137,8 @@ void deserialize_WillDeserializeUnencryptedEventsCreatedByThisSerializer() { void deserialize_WillDecryptEventsSerializedByJsonCryptoShreddingSerializer_WhenEventContainsEncryptedFields() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); when(cryptoShreddingKeyService.getExistingSecretKey(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); - when(encrypterFactory.createDecrypter()).thenReturn(aesDecrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); + when(encrypterFactory.createDecrypter()).thenReturn(defaultAesDecrypter); var serializedAndEncryptedEvent = jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); SimpleSerializedObject typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), byte[].class, @@ -154,8 +154,8 @@ void deserialize_WillDecryptEventsSerializedByXmlCryptoShreddingSerializer_WhenE when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); when(cryptoShreddingKeyService.getExistingSecretKey(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); - when(encrypterFactory.createDecrypter()).thenReturn(aesDecrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); + when(encrypterFactory.createDecrypter()).thenReturn(defaultAesDecrypter); var serializedAndEncryptedEvent = xmlCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), String.class); SimpleSerializedObject typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), String.class, @@ -169,7 +169,7 @@ void deserialize_WillDecryptEventsSerializedByXmlCryptoShreddingSerializer_WhenE void deserialize_WillReplaceEncryptedFieldsWithDefaultValues_WhenEncryptionKeyHasBeenDeleted() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); when(cryptoShreddingKeyService.getExistingSecretKey(KEY_IDENTIFIER)).thenReturn(Optional.empty()); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); var serializedAndEncryptedEvent = jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); SimpleSerializedObject typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), byte[].class, @@ -182,7 +182,7 @@ void deserialize_WillReplaceEncryptedFieldsWithDefaultValues_WhenEncryptionKeyHa @Test void deserialize_WillFail_WhenEncryptionKeyIdentifierFieldHasBeenDeletedOrRenamedInSerializedForm() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); var serializedAndEncryptedEvent = jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); byte[] mangledPayload = new String(serializedAndEncryptedEvent.getData()).replace("keyIdentifier", "renamed-or-lost!").getBytes(); @@ -198,7 +198,7 @@ void deserialize_WillFail_WhenEncryptionKeyIdentifierFieldHasBeenDeletedOrRename @Test void deserialize_WillFail_WhenEncryptionKeyIdentifierValueHasBeenDeletedOrRenamedInSerializedForm() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); var serializedAndEncryptedEvent = jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); byte[] mangledPayload = new String(serializedAndEncryptedEvent.getData()).replace("key-identifier", "").getBytes(); @@ -214,7 +214,7 @@ void deserialize_WillFail_WhenEncryptionKeyIdentifierValueHasBeenDeletedOrRename @Test void deserialize_WillFail_WhenEncryptionKeyIdentifierAnnotationIsMissing() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(KEY_IDENTIFIER)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); var serializedAndEncryptedEvent = jsonCryptoShreddingSerializer.serialize(EventWithEncryptedFields.createTestInstance(), byte[].class); SimpleSerializedObject typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), byte[].class, @@ -231,8 +231,8 @@ void keyTypeCanBeUsedToDifferentiateBetweenPrimitiveIdentifiers() { when(cryptoShreddingKeyService.getOrCreateSecretKeyUnlessDeleted(typeDifferentiatedKey)).thenReturn(Optional.of(ENCRYPTION_KEY)); when(cryptoShreddingKeyService.getExistingSecretKey(typeDifferentiatedKey)).thenReturn(Optional.of(ENCRYPTION_KEY)); - when(encrypterFactory.createEncrypter()).thenReturn(aesEncrypter); - when(encrypterFactory.createDecrypter()).thenReturn(aesDecrypter); + when(encrypterFactory.createEncrypter()).thenReturn(defaultAesEncrypter); + when(encrypterFactory.createDecrypter()).thenReturn(defaultAesDecrypter); var eventWithDifferentiatedKeyType = new EventWithDifferentiatedKeyType(1234L, "field value"); var serializedAndEncryptedEvent = jsonCryptoShreddingSerializer.serialize(eventWithDifferentiatedKeyType, byte[].class); diff --git a/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterIntegrationTest.java b/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterIntegrationTest.java similarity index 66% rename from src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterIntegrationTest.java rename to src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterIntegrationTest.java index 01c578e..0533e82 100644 --- a/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterIntegrationTest.java +++ b/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypterDecrypterIntegrationTest.java @@ -13,8 +13,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(MockitoExtension.class) -class AesEncrypterDecrypterIntegrationTest { +class DefaultAesEncrypterDecrypterIntegrationTest { + private static final String TINY_MESSAGE = "42"; private static final String PLAIN_TEXT_MESSAGE = "The quick brown fox jumped ship. This is a long payload by design. " + "Please keep it such so that we have confidence in our ability to handle long messages. Lorem ipsum dolor sit amet, " + "consectetur adipiscing elit. Nunc sit amet nulla id lacus vulputate fringilla at sed est. Sed viverra rhoncus " + @@ -32,37 +33,46 @@ class AesEncrypterDecrypterIntegrationTest { "Nullam vitae pretium erat. Ut ut diam risus. Maecenas mauris ligula, pretium ac lectus vitae, tincidunt venenatis mi. " + "Sed odio nisi, placerat id lectus non, condimentum ultrices arcu."; - private AesKeyGenerator aesKeyGenerator; - private AesEncrypter base64EncodingAesEncrypter; - private AesDecrypter aesDecrypter; + private DefaultAesKeyGenerator defaultAesKeyGenerator; + private DefaultAesEncrypter base64EncodingDefaultAesEncrypter; + private DefaultAesDecrypter defaultAesDecrypter; @BeforeEach void setUp() throws NoSuchAlgorithmException { var secureRandom = new SecureRandom(); - aesKeyGenerator = new AesKeyGenerator(); - base64EncodingAesEncrypter = new AesEncrypter(secureRandom); - aesDecrypter = new AesDecrypter(secureRandom); + defaultAesKeyGenerator = new DefaultAesKeyGenerator(); + base64EncodingDefaultAesEncrypter = new DefaultAesEncrypter(secureRandom); + defaultAesDecrypter = new DefaultAesDecrypter(secureRandom); } @Test void willDecryptItsOwnEncryptedMessages() { - var secretKey = aesKeyGenerator.generateKey(); - var encodedCipherText = base64EncodingAesEncrypter.encrypt(secretKey, PLAIN_TEXT_MESSAGE); - var decodedPlainText = aesDecrypter.decrypt(secretKey, encodedCipherText); + var secretKey = defaultAesKeyGenerator.generateKey(); + var encodedCipherText = base64EncodingDefaultAesEncrypter.encrypt(secretKey, PLAIN_TEXT_MESSAGE); + var decodedPlainText = defaultAesDecrypter.decrypt(secretKey, encodedCipherText); assertEquals(PLAIN_TEXT_MESSAGE, decodedPlainText); } + @Test + void paddingWorksForTinyMessages() { + var secretKey = defaultAesKeyGenerator.generateKey(); + var encodedCipherText = base64EncodingDefaultAesEncrypter.encrypt(secretKey, TINY_MESSAGE); + var decodedPlainText = defaultAesDecrypter.decrypt(secretKey, encodedCipherText); + + assertEquals(TINY_MESSAGE, decodedPlainText); + } + @Test void encrypt_WillFail_WhenEncryptionKeyIsInvalid() { SecretKeySpec invalidAlgorithm = new SecretKeySpec("blah".getBytes(), "invalid algorithm"); - assertThrows(RuntimeException.class, () -> base64EncodingAesEncrypter.encrypt(invalidAlgorithm, PLAIN_TEXT_MESSAGE), PLAIN_TEXT_MESSAGE); + assertThrows(RuntimeException.class, () -> base64EncodingDefaultAesEncrypter.encrypt(invalidAlgorithm, PLAIN_TEXT_MESSAGE), PLAIN_TEXT_MESSAGE); } @Test void decrypt_WillFail_WhenDecryptionKeyIsInvalid() { - var encodedCipherText = base64EncodingAesEncrypter.encrypt(aesKeyGenerator.generateKey(), PLAIN_TEXT_MESSAGE); + var encodedCipherText = base64EncodingDefaultAesEncrypter.encrypt(defaultAesKeyGenerator.generateKey(), PLAIN_TEXT_MESSAGE); - assertThrows(RuntimeException.class, () -> aesDecrypter.decrypt(aesKeyGenerator.generateKey(), encodedCipherText)); + assertThrows(RuntimeException.class, () -> defaultAesDecrypter.decrypt(defaultAesKeyGenerator.generateKey(), encodedCipherText)); } } diff --git a/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactoryTest.java b/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultDefaultAesEncrypterDecrypterFactoryTest.java similarity index 51% rename from src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactoryTest.java rename to src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultDefaultAesEncrypterDecrypterFactoryTest.java index 3f03790..93ec04a 100644 --- a/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/AesEncrypterDecrypterFactoryTest.java +++ b/src/test/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultDefaultAesEncrypterDecrypterFactoryTest.java @@ -8,22 +8,22 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; @ExtendWith(MockitoExtension.class) -class AesEncrypterDecrypterFactoryTest { +class DefaultDefaultAesEncrypterDecrypterFactoryTest { - private AesEncrypterDecrypterFactory aesEncrypterDecrypterFactory; + private DefaultAesEncrypterDecrypterFactory defaultAesEncrypterDecrypterFactory; @BeforeEach void setUp() { - aesEncrypterDecrypterFactory = new AesEncrypterDecrypterFactory(); + defaultAesEncrypterDecrypterFactory = new DefaultAesEncrypterDecrypterFactory(); } @Test void createEncrypterReturnsNewInstances() { - assertNotEquals(aesEncrypterDecrypterFactory.createEncrypter(), aesEncrypterDecrypterFactory.createEncrypter()); + assertNotEquals(defaultAesEncrypterDecrypterFactory.createEncrypter(), defaultAesEncrypterDecrypterFactory.createEncrypter()); } @Test void createDecrypterReturnsNewInstances() { - assertNotEquals(aesEncrypterDecrypterFactory.createDecrypter(), aesEncrypterDecrypterFactory.createDecrypter()); + assertNotEquals(defaultAesEncrypterDecrypterFactory.createDecrypter(), defaultAesEncrypterDecrypterFactory.createDecrypter()); } } \ No newline at end of file From 8dd2a54b0d5a400093d94a666e2f0ec3003b3df4 Mon Sep 17 00:00:00 2001 From: Sebastian Luehr Date: Wed, 14 Oct 2020 10:20:47 +1100 Subject: [PATCH 2/3] Remove TODOs warning about thread safety in default encrypter/decrypter as a new instance of Cipher is instantiated for each message --- .../axon/cryptoshredding/encryption/DefaultAesDecrypter.java | 1 - .../axon/cryptoshredding/encryption/DefaultAesEncrypter.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java index 05164b6..7ffde96 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesDecrypter.java @@ -25,7 +25,6 @@ public DefaultAesDecrypter(SecureRandom secureRandom) { public String decrypt(SecretKey secretKey, byte[] initializationVectorAndCipherText) { try { - // TODO this doesn't look reentrant var cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(DECRYPT_MODE, secretKey, new IvParameterSpec(initializationVectorAndCipherText, 0, INITIALIZATION_VECTOR_LENGTH), secureRandom); return new String(cipher.doFinal(initializationVectorAndCipherText, INITIALIZATION_VECTOR_LENGTH, initializationVectorAndCipherText.length - INITIALIZATION_VECTOR_LENGTH)); diff --git a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java index 2e5af34..ac85eb2 100644 --- a/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java +++ b/src/main/java/engineering/everest/starterkit/axon/cryptoshredding/encryption/DefaultAesEncrypter.java @@ -26,7 +26,6 @@ public DefaultAesEncrypter(SecureRandom secureRandom) { public byte[] encrypt(SecretKey secretKey, String cleartext) { try { - // TODO this doesn't look reentrant var cipher = Cipher.getInstance(CIPHER_ALGORITHM); byte[] initializationVector = createInitializationVector(); cipher.init(ENCRYPT_MODE, secretKey, new IvParameterSpec(initializationVector), secureRandom); From 24462e1aae39323656acfb8f572d3c13f7917db4 Mon Sep 17 00:00:00 2001 From: Sebastian Luehr Date: Wed, 14 Oct 2020 10:27:24 +1100 Subject: [PATCH 3/3] Build badge now tracks 'main' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 563bbcf..6c58c6a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Crypro shredding extensions for Axon -[![Build Status](https://travis-ci.com/everest-engineering/axon-crypto-shredding-extension.svg?branch=master)](https://travis-ci.com/everest-engineering/axon-crypto-shredding-extension) ![Latest release](https://img.shields.io/github/v/release/everest-engineering/axon-crypto-shredding-extension) +[![Build Status](https://travis-ci.com/everest-engineering/axon-crypto-shredding-extension.svg?branch=main)](https://travis-ci.com/everest-engineering/axon-crypto-shredding-extension) ![Latest release](https://img.shields.io/github/v/release/everest-engineering/axon-crypto-shredding-extension) This is a supporting repository for [Lhotse](https://github.com/everest-engineering/lhotse), a starter kit for writing event sourced web applications following domain driven design principles.