Skip to content

Commit

Permalink
Refactor AES encryption tests and methods
Browse files Browse the repository at this point in the history
The commit refactors AES encryption tests and methods to use DigestAlgorithm instead of the previously used 'Algorithm' inner class in AES. The changes also improve code consistency by adding 'this' keyword to object property accesses and reformatting some blocks for readability. There are also some changes to the import statements to adapt to the new changes.
  • Loading branch information
bobaikato committed Jan 25, 2024
1 parent 1288e20 commit 2da2a37
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 47 deletions.
57 changes: 33 additions & 24 deletions src/main/java/art/cutils/security/AES.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@

package art.cutils.security;

import static art.cutils.security.AES.Algorithm.SHA256;
import static art.cutils.standards.DigestAlgorithm.SHA256;
import static art.cutils.security.DigestAlgorithm.SHA256;
import static java.security.MessageDigest.getInstance;
import static java.util.Objects.hash;
import static java.util.Objects.isNull;
Expand All @@ -34,7 +33,6 @@
import static org.apache.commons.lang3.Validate.isTrue;

import art.cutils.Serialization;
import art.cutils.standards.DigestAlgorithm;
import java.io.IOException;
import java.io.Serializable;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -70,14 +68,17 @@
*/
public final class AES<T> implements Serializable {
private static final long serialVersionUID = 977987773346721438L;

/**
* Default encryption Key.
*
* @since 1.0
*/
private static final String DEFAULT_KEY = "Set yours with: `AES.init('fW&yNtP2peBndT5Hz&')`";

/** GCM Length. */
private static final int GCM_IV_LENGTH = 12;

/**
* Instance of {@link Cipher}.
*
Expand All @@ -91,6 +92,7 @@ public final class AES<T> implements Serializable {
* @since 1.0
*/
private final SecretKeySpec secretKey;

/**
* Additional Authentication Data for GCM.
*
Expand All @@ -107,18 +109,18 @@ public final class AES<T> implements Serializable {
* @throws NoSuchPaddingException if the specified padding mechanism is not available in the
* environment
*/
private AES(final AES.@NotNull Algorithm algorithm, final @NotNull String encryptionKey)
private AES(final @NotNull DigestAlgorithm algorithm, final @NotNull String encryptionKey)
throws NoSuchPaddingException, NoSuchAlgorithmException {
this.cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] key = this.aad = encryptionKey.getBytes(StandardCharsets.UTF_8);
final MessageDigest messageDigest = getInstance(algorithm.type);
final MessageDigest messageDigest = getInstance(algorithm.getType());
key = Arrays.copyOf(messageDigest.digest(key), 16);
this.secretKey = new SecretKeySpec(key, "AES");
}

/**
* This initiates encryption with default {@link MessageDigest} {@link Algorithm#type} and
* encryption key.
* This initiates encryption with default {@link MessageDigest} {@link DigestAlgorithm#getType()}
* and encryption key.
*
* @param <T> Type of value
* @return Instance of {@link AES}
Expand All @@ -134,8 +136,8 @@ private AES(final AES.@NotNull Algorithm algorithm, final @NotNull String encryp
}

/**
* This initiates encryption with the default {@link MessageDigest} {@link Algorithm#type}:{@code
* SHA-256}.
* This initiates encryption with the default {@link MessageDigest} {@link
* DigestAlgorithm#getType()}:{@code SHA-256}.
*
* @param encryptionKey user encryption Key
* @param <T> Type of value
Expand All @@ -154,11 +156,11 @@ private AES(final AES.@NotNull Algorithm algorithm, final @NotNull String encryp
}

/**
* This initiates encryption with the specified {@link MessageDigest} {@link Algorithm#type} type
* and default Key.
* This initiates encryption with the specified {@link MessageDigest} {@link
* DigestAlgorithm#getType()} type and default Key.
*
* @param algorithm encryption algorithm of {@link Algorithm} instance. SHA256 is set if {@code
* null}.
* @param algorithm encryption algorithm of {@link DigestAlgorithm} instance. SHA256 is set if
* {@code null}.
* @param <T> Type of value
* @return Instance of {@link AES}
* @throws NoSuchAlgorithmException This exception is thrown when a particular cryptographic
Expand All @@ -168,18 +170,18 @@ private AES(final AES.@NotNull Algorithm algorithm, final @NotNull String encryp
* @since 1.0
*/
@Contract("_ -> new")
public static <T> @NotNull AES<T> init(final AES.Algorithm algorithm)
public static <T> @NotNull AES<T> init(final DigestAlgorithm algorithm)
throws NoSuchAlgorithmException, NoSuchPaddingException {
return AES.init(algorithm, AES.DEFAULT_KEY);
}

/**
* This initiates encryption with the specified {@link MessageDigest} {@link Algorithm#type} and
* encryption key.
* This initiates encryption with the specified {@link MessageDigest} {@link
* DigestAlgorithm#getType()} and encryption key.
*
* @param encryptionKey user encryption Key
* @param algorithm encryption algorithm of {@link Algorithm} instance. SHA256 is set if {@code
* null}.
* @param algorithm encryption algorithm of {@link DigestAlgorithm} instance. SHA256 is set if
* {@code null}.
* @param <T> Type of value
* @return Instance of {@link AES}
* @throws NoSuchAlgorithmException This exception is thrown when a particular cryptographic
Expand Down Expand Up @@ -212,13 +214,16 @@ private AES(final AES.@NotNull Algorithm algorithm, final @NotNull String encryp
* @throws IOException Signals that an I/O exception of some sort has occurred.
*/
public String encrypt(@Valid final T itemToEncrypt)
throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
IllegalBlockSizeException, IOException {
throws InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException,
IOException {
Validate.isTrue(isNotEmpty(itemToEncrypt), "Item to encrypt cannot be null.", itemToEncrypt);
final Supplier<byte[]> ivSupplier =
() -> {
final SecureRandom secureRandom = new SecureRandom();
final byte[] iv = new byte[GCM_IV_LENGTH]; // NEVER REUSE THIS IV WITH SAME KEY
final byte[] iv = new byte[AES.GCM_IV_LENGTH]; // NEVER REUSE THIS IV WITH SAME KEY
do {
secureRandom.nextBytes(iv);
} while (iv[0] == 0);
Expand Down Expand Up @@ -258,13 +263,16 @@ public String encrypt(@Valid final T itemToEncrypt)
* a block * cipher is incorrect, i.e., does not match the block size of the cipher.
*/
public T decrypt(final @NotNull String itemToDecrypt)
throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
throws InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {

isTrue(isNotEmpty(itemToDecrypt), "Item to decrypt cannot be null.", itemToDecrypt);

final byte[] cipherMessage = Base64.getUrlDecoder().decode(itemToDecrypt);
final AlgorithmParameterSpec spec = new GCMParameterSpec(128, cipherMessage, 0, GCM_IV_LENGTH);
final AlgorithmParameterSpec spec =
new GCMParameterSpec(128, cipherMessage, 0, AES.GCM_IV_LENGTH);

this.cipher.init(Cipher.DECRYPT_MODE, this.secretKey, spec);

Expand All @@ -273,7 +281,8 @@ public T decrypt(final @NotNull String itemToDecrypt)
}

final byte[] plainText =
this.cipher.doFinal(cipherMessage, GCM_IV_LENGTH, cipherMessage.length - GCM_IV_LENGTH);
this.cipher.doFinal(
cipherMessage, AES.GCM_IV_LENGTH, cipherMessage.length - AES.GCM_IV_LENGTH);

return SerializationUtils.deserialize(plainText);
}
Expand Down
50 changes: 27 additions & 23 deletions src/test/java/security/AesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,15 @@
*/
package security;

import static art.cutils.security.AES.Algorithm;
import static art.cutils.security.AES.Algorithm.MD2;
import static art.cutils.security.AES.Algorithm.MD5;
import static art.cutils.security.AES.Algorithm.SHA1;
import static art.cutils.security.AES.Algorithm.SHA224;
import static art.cutils.security.AES.Algorithm.SHA256;
import static art.cutils.security.AES.Algorithm.SHA384;
import static art.cutils.security.AES.Algorithm.SHA512;
import static art.cutils.security.DigestAlgorithm.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.of;

import art.cutils.security.AES;
import art.cutils.security.DigestAlgorithm;
import java.io.Serializable;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
Expand All @@ -58,17 +52,22 @@ final class AesTest {
private static @NotNull Stream<Arguments> customObjectResource() {
final PersonExample personExampleI =
new PersonExample() {

private static final long serialVersionUID = 366454167891286432L;

{
setAge(10);
setName("B0B");
this.setAge(10);
this.setName("B0B");
}
};

final PersonExample personExampleII =
new PersonExample() {
private static final long serialVersionUID = -9074187748710070750L;

{
setAge(11);
setName("PETER");
this.setAge(11);
this.setName("PETER");
}
};

Expand Down Expand Up @@ -132,8 +131,8 @@ void encryptionAndDecryptionCustomObjectWithCustomKey(final PersonExample input,
"Should successfully Encrypt and Decrypt Object with all DigestAlgorithm type and Key")
@ParameterizedTest(name = "{index} => DigestAlgorithm={0}")
@MethodSource("algorithmTypes")
void encryptionAndDecryptionWithSpecifiedAlgorithmTypeAndCustomKey(final Algorithm algorithm)
throws Exception {
void encryptionAndDecryptionWithSpecifiedAlgorithmTypeAndCustomKey(
final DigestAlgorithm algorithm) throws Exception {

final AES<PersonExample> aes = AES.init(algorithm, "Encrypt and Decrypt Key");

Expand All @@ -151,7 +150,7 @@ void encryptionAndDecryptionWithSpecifiedAlgorithmTypeAndCustomKey(final Algorit
@DisplayName("Should successfully Encrypt and Decrypt Object with all DigestAlgorithm type")
@ParameterizedTest(name = "{index} => DigestAlgorithm={0}")
@MethodSource("algorithmTypes")
void encryptionAndDecryptionWithSpecifiedAlgorithmType(final Algorithm algorithm)
void encryptionAndDecryptionWithSpecifiedAlgorithmType(final DigestAlgorithm algorithm)
throws Exception {

final AES<PersonExample> aes = AES.init(algorithm);
Expand Down Expand Up @@ -205,7 +204,7 @@ void shouldEncryptStringTypeValuesWithCustomKey(final String input, final String
@ParameterizedTest(name = "{index} => input={0}, Key={1}, DigestAlgorithm={2}")
@MethodSource("stringEncryptionValues")
void shouldEncryptStringTypeValuesWithCustomKeyAndAlgorithmType(
final String input, final String key, final Algorithm algorithm) throws Exception {
final String input, final String key, final DigestAlgorithm algorithm) throws Exception {
final String encryptValue = AES.init(algorithm, key).encrypt(input);
final String decryptedValue = AES.<String>init(algorithm, key).decrypt(encryptValue);

Expand Down Expand Up @@ -245,7 +244,8 @@ void shouldEncryptDoubleTypeValues(final double input) throws Exception {
@DisplayName("Should successfully encrypt Double type values With Custom Key.")
@ParameterizedTest(name = "{index} => input={0}, Key={1}")
@MethodSource("doubleEncryptionValues")
void shouldEncryptDoubleTypeValuesWithCustomKey(double input, String key) throws Exception {
void shouldEncryptDoubleTypeValuesWithCustomKey(final double input, final String key)
throws Exception {
final String encryptValue = AES.init(key).encrypt(input);
final double decryptedValue = AES.<Double>init(key).decrypt(encryptValue);

Expand Down Expand Up @@ -287,28 +287,32 @@ public PersonExample() {}

@Override
public int hashCode() {
return Objects.hash(getAge(), getName());
return Objects.hash(this.getAge(), this.getName());
}

@Contract(value = "null -> false", pure = true)
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof PersonExample)) return false;
if (this == o) {
return true;
}
if (!(o instanceof PersonExample)) {
return false;
}
final PersonExample that = (PersonExample) o;
return getAge() == that.getAge() && Objects.equals(getName(), that.getName());
return this.getAge() == that.getAge() && Objects.equals(this.getName(), that.getName());
}

public int getAge() {
return age;
return this.age;
}

public void setAge(final int age) {
this.age = age;
}

public String getName() {
return name;
return this.name;
}

public void setName(final String name) {
Expand Down

0 comments on commit 2da2a37

Please sign in to comment.