Skip to content

Commit

Permalink
Introduce a PublicKeyEncryptionService for easier ShadowFlow configur…
Browse files Browse the repository at this point in the history
…ation through starter
  • Loading branch information
martinvisser committed Nov 27, 2023
1 parent db57318 commit c004724
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.Cipher;
import java.security.GeneralSecurityException;

class DefaultEncryptionService implements EncryptionService {
import static java.nio.charset.StandardCharsets.UTF_8;

/**
* The default Encryption Service used by Shadow Tool. It uses a {@link Cipher} to encrypt values.
*
* @see Cipher
*/
public class DefaultEncryptionService implements EncryptionService {
private final Cipher cipher;

DefaultEncryptionService(final Cipher cipher) {
public DefaultEncryptionService(final Cipher cipher) {
this.cipher = cipher;
}

@Override
public String encrypt(final String value) {
try {
return Base64.toBase64String(cipher.doFinal(value.getBytes()));
} catch (final Exception e) {
return Base64.toBase64String(cipher.doFinal(value.getBytes(UTF_8)));
} catch (final GeneralSecurityException e) {
throw new SecurityException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.github.rabobank.shadow_tool;

import javax.crypto.Cipher;
import java.security.GeneralSecurityException;
import java.security.PublicKey;

import static javax.crypto.Cipher.ENCRYPT_MODE;

/**
* A version of the Encryption Service that uses a {@link PublicKey} to encrypt the values.
*/
public class PublicKeyEncryptionService extends DefaultEncryptionService {
private static final String DEFAULT_ALGORITHM = "RSA";
private static final String DEFAULT_ALGORITHM_MODE_PADDING =
DEFAULT_ALGORITHM + "/ECB/OAEPWITHSHA-256ANDMGF1PADDING";

public PublicKeyEncryptionService(final PublicKey publicKey) {
super(createCipher(publicKey));
}

private static Cipher createCipher(final PublicKey publicKey) {
try {
final var cipher = Cipher.getInstance(DEFAULT_ALGORITHM_MODE_PADDING);
cipher.init(ENCRYPT_MODE, publicKey);
return cipher;
} catch (final GeneralSecurityException e) {
throw new IllegalArgumentException(e);
}
}
}
19 changes: 7 additions & 12 deletions src/main/java/io/github/rabobank/shadow_tool/ShadowFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ public class ShadowFlow<T> {
private static final String DEFAULT_INSTANCE_NAME = "default";
private static final String CALLING_NEW_FLOW = "{} Calling new flow: {}";
private static final String FAILED_TO_COMPARE = "{} Failed to run the shadow flow";
private static final String DEFAULT_ALGORITHM = "RSA";
private static final String DEFAULT_ALGORITHM_MODE_PADDING =
DEFAULT_ALGORITHM + "/ECB/OAEPWITHSHA-256ANDMGF1PADDING";
private final int percentage;
private final ExecutorService executorService;
private final EncryptionService encryptionService;
Expand Down Expand Up @@ -285,13 +282,7 @@ public ShadowFlowBuilder<T> withExecutorService(final ExecutorService executorSe
*/
public ShadowFlowBuilder<T> withEncryption(final PublicKey publicKey) {
requireNull();
try {
final var cipher = Cipher.getInstance(DEFAULT_ALGORITHM_MODE_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
withCipher(cipher);
} catch (final Exception e) {
logger.error("Invalid encryption setup. Encryption and logging of values is disabled", e);
}
withEncryptionService(new PublicKeyEncryptionService(publicKey));
return this;
}

Expand All @@ -306,7 +297,7 @@ public ShadowFlowBuilder<T> withEncryption(final PublicKey publicKey) {
*/
public ShadowFlowBuilder<T> withCipher(final Cipher cipher) {
requireNull();
encryptionService = new DefaultEncryptionService(cipher);
withEncryptionService(new DefaultEncryptionService(cipher));
return this;
}

Expand All @@ -320,7 +311,11 @@ public ShadowFlowBuilder<T> withCipher(final Cipher cipher) {
*/
public ShadowFlowBuilder<T> withEncryptionService(final EncryptionService encryptionService) {
requireNull();
this.encryptionService = encryptionService;
try {
this.encryptionService = encryptionService;
} catch (final Exception e) {
logger.error("Invalid encryption setup. Encryption and logging of values is disabled", e);
}
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
import org.junit.jupiter.api.Test;

import javax.crypto.Cipher;
import java.security.GeneralSecurityException;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;

import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

Expand All @@ -26,16 +25,14 @@ class EncryptionServiceTest {
final var pair = keyPairGen.generateKeyPair();
PRIVATE_KEY = pair.getPrivate();
PUBLIC_KEY = pair.getPublic();
} catch (NoSuchAlgorithmException e) {
} catch (final GeneralSecurityException e) {
throw new RuntimeException(e);
}
}

@Test
void encryptAndDecrypt() throws Exception {
final var encryptCipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
encryptCipher.init(ENCRYPT_MODE, PUBLIC_KEY);
final var encryptionService = new DefaultEncryptionService(encryptCipher);
final var encryptionService = new PublicKeyEncryptionService(PUBLIC_KEY);
final var plainDifferences = "'place' changed: 'Dintelooord' -> 'Dinteloord'\n" +
"'madrigals' collection changes :\n" +
" 1. 'Bruno' changed to 'Mirabel'\n" +
Expand All @@ -58,7 +55,7 @@ void encryptAndForgotToInitCipher() throws Exception {
"'madrigals' collection changes :\n" +
" 1. 'Bruno' changed to 'Mirabel'\n" +
" 0. 'Bruno' added";
final var exception = assertThrows(SecurityException.class, () -> encryptionService.encrypt(plainDifferences));
assertEquals("java.lang.IllegalStateException: Cipher not initialized", exception.getMessage());
final var exception = assertThrows(IllegalStateException.class, () -> encryptionService.encrypt(plainDifferences));
assertEquals("Cipher not initialized", exception.getMessage());
}
}

0 comments on commit c004724

Please sign in to comment.