Skip to content

Commit

Permalink
Encryption autoconfiguration with AES as a default
Browse files Browse the repository at this point in the history
  • Loading branch information
sluehr committed Oct 17, 2020
1 parent c5d1ecd commit 75766cc
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -172,7 +172,7 @@ private Map<String, Object> mapAndEncryptAnnotatedFields(Object object, List<Fie
var mappedObject = objectMapper.convertValue(object, new TypeReference<HashMap<String, Object>>() {
});
var serializedFieldNameMapping = buildFieldNamingSerializationStrategyIndependentMapping(mappedObject);
var encrypter = aesEncrypterDecrypterFactory.createEncrypter();
var encrypter = encrypterDecrypterFactory.createEncrypter();

encryptedFields.forEach(field -> {
var fieldKey = serializedFieldNameMapping.get(field.getName().toLowerCase());
Expand All @@ -193,7 +193,7 @@ private Map<String, Object> decryptAnnotatedFields(Map<String, Object> encrypted
Map<String, String> serializedFieldNameMapping,
List<Field> encryptedFields,
SecretKey secretKey) {
var decrypter = aesEncrypterDecrypterFactory.createDecrypter();
var decrypter = encrypterDecrypterFactory.createDecrypter();

encryptedFields.forEach(field -> {
var serializedFieldKey = serializedFieldNameMapping.get(field.getName().toLowerCase());
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package engineering.everest.starterkit.axon.cryptoshredding.encryption;

import javax.crypto.SecretKey;

public interface Decrypter {
String decrypt(SecretKey secretKey, byte[] ciphertext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package engineering.everest.starterkit.axon.cryptoshredding.encryption;

import javax.crypto.SecretKey;

public interface Encrypter {
byte[] encrypt(SecretKey secretKey, String cleartext);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package engineering.everest.starterkit.axon.cryptoshredding.encryption;

public interface EncrypterDecrypterFactory {
Encrypter createEncrypter();

Decrypter createDecrypter();
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package engineering.everest.starterkit.axon.cryptoshredding.encryption;

import javax.crypto.SecretKey;

public interface KeyGenerator {
SecretKey generateKey();
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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<byte[]> typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), byte[].class,
Expand All @@ -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<String> typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), String.class,
Expand All @@ -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<byte[]> typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), byte[].class,
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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<byte[]> typeInformationAugmentedEncryptedEvent = new SimpleSerializedObject<>(serializedAndEncryptedEvent.getData(), byte[].class,
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 75766cc

Please sign in to comment.