Skip to content

Commit

Permalink
Add Encryptor and Hasher abstration
Browse files Browse the repository at this point in the history
  • Loading branch information
mkay1375 committed Aug 4, 2024
1 parent 70448a7 commit 3f4ad58
Show file tree
Hide file tree
Showing 22 changed files with 570 additions and 421 deletions.
65 changes: 26 additions & 39 deletions src/main/java/io/github/tap30/hiss/Hiss.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
package io.github.tap30.hiss;

import io.github.tap30.hiss.key.KeyHashGenerator;
import io.github.tap30.hiss.properties.HissProperties;
import io.github.tap30.hiss.utils.EncryptionUtils;
import io.github.tap30.hiss.encryptor.HissEncryptor;
import io.github.tap30.hiss.hasher.HissHasher;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Hiss {

private static final Logger logger = Logger.getLogger(Hiss.class.getName());

private final HissEncryptor hissEncryptor;
private final HissHasher hissHasher;
private final HissObjectEncryptor hissObjectEncryptor;

Hiss(HissProperties hissProperties, KeyHashGenerator keyHashGenerator) {
Objects.requireNonNull(hissProperties);
Objects.requireNonNull(keyHashGenerator);

this.hissEncryptor = new HissEncryptor(hissProperties);
this.hissObjectEncryptor = new HissObjectEncryptor(this.hissEncryptor);

logHissInitialized(hissProperties);
if (hissProperties.isKeyHashGenerationEnabled()) {
keyHashGenerator.generateAndLogHashes(hissProperties.getKeys().values());
}
Hiss(HissEncryptor hissEncryptor,
HissHasher hissHasher,
HissObjectEncryptor hissObjectEncryptor) {
this.hissEncryptor = Objects.requireNonNull(hissEncryptor);
this.hissHasher = Objects.requireNonNull(hissHasher);
this.hissObjectEncryptor = Objects.requireNonNull(hissObjectEncryptor);
}

/**
Expand All @@ -49,7 +40,11 @@ public String encrypt(@Nullable String content) {
* @return encrypted content or null if the content is null.
*/
public String encrypt(@Nullable String content, @Language("regexp") @Nullable String pattern) {
return hissEncryptor.encrypt(content, pattern);
try {
return hissEncryptor.encrypt(content, pattern);
} catch (Exception e) { // todo: remove try-catch?
throw new RuntimeException(e);
}
}

/**
Expand All @@ -62,7 +57,11 @@ public String encrypt(@Nullable String content, @Language("regexp") @Nullable St
* @throws IllegalArgumentException if key ID or algorithm is not loaded/supported.
*/
public String decrypt(@Nullable String content) {
return hissEncryptor.decrypt(content);
try {
return hissEncryptor.decrypt(content);
} catch (Exception e) { // todo: remove try-catch?
throw new RuntimeException(e);
}
}

/**
Expand All @@ -84,7 +83,11 @@ public String hash(@Nullable String content) {
* @return hashed content or null if the content is null.
*/
public String hash(@Nullable String content, @Language("regexp") @Nullable String pattern) {
return hissEncryptor.hash(content, pattern);
try {
return hissHasher.hash(content, pattern);
} catch (Exception e) { // todo: remove try-catch?
throw new RuntimeException(e);
}
}

/**
Expand All @@ -94,7 +97,7 @@ public String hash(@Nullable String content, @Language("regexp") @Nullable Strin
* @return <code>true</code> if the content or parts of it is encrypted.
*/
public boolean isEncrypted(@Nullable String content) {
return EncryptionUtils.isHavingEncryptionPattern(content);
return hissEncryptor.isEncrypted(content);
}

/**
Expand All @@ -104,7 +107,7 @@ public boolean isEncrypted(@Nullable String content) {
* @return <code>true</code> if the content or parts of it is hashed.
*/
public boolean isHashed(@Nullable String content) {
return EncryptionUtils.isHavingEncryptionPattern(content);
return hissHasher.isHashed(content);
}

/**
Expand All @@ -125,20 +128,4 @@ public void decryptObject(@Nullable Object object) {
hissObjectEncryptor.decryptObject(object);
}

private void logHissInitialized(HissProperties hissProperties) {
logger.log(Level.INFO, "Hiss initialized:\n" +
" Loaded Keys: {0}\n" +
" Default Encryption Key ID: {1}\n" +
" Default Encryption Algorithm: {2}\n" +
" Default Hashing Key ID: {3}\n" +
" Default Hashing Algorithm: {4}",
new Object[]{
hissProperties.getKeys().keySet(),
hissProperties.getDefaultEncryptionKeyId(),
hissProperties.getDefaultEncryptionAlgorithm(),
hissProperties.getDefaultHashingKeyId(),
hissProperties.getDefaultHashingAlgorithm()
});
}

}
55 changes: 0 additions & 55 deletions src/main/java/io/github/tap30/hiss/HissEncryptor.java

This file was deleted.

104 changes: 102 additions & 2 deletions src/main/java/io/github/tap30/hiss/HissFactory.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
package io.github.tap30.hiss;

import at.favre.lib.crypto.bcrypt.BCrypt;
import io.github.tap30.hiss.encryptor.*;
import io.github.tap30.hiss.hasher.Hasher;
import io.github.tap30.hiss.hasher.HissHasher;
import io.github.tap30.hiss.hasher.HmacSha256Hasher;
import io.github.tap30.hiss.hasher.TapsiHmacSha256Hasher;
import io.github.tap30.hiss.key.KeyHashGenerator;
import io.github.tap30.hiss.properties.HissProperties;
import io.github.tap30.hiss.properties.HissPropertiesValidator;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class HissFactory {

private static final Logger logger = Logger.getLogger(HissFactory.class.getName());

/**
* Creates a Hiss instance with provided <code>HissProperties</code>.
* Creates a Hiss instance with provided <code>HissProperties</code> and default encryptors and hashers.
*
* @param hissProperties the properties by which hiss will be instantiated;
* {@link io.github.tap30.hiss.properties.HissPropertiesFromEnv}
Expand All @@ -23,9 +35,97 @@ public class HissFactory {
* @throws IllegalArgumentException if the properties are not valid.
*/
public static Hiss createHiss(HissProperties hissProperties) {
return createHiss(hissProperties, Set.of(), Set.of());
}

/**
* Creates a Hiss instance with provided <code>HissProperties</code> and encryptors and hashers.
* <br>
* Provided encryptors and hashers will be added alongside default ones.
*
* @param hissProperties the properties by which hiss will be instantiated;
* {@link io.github.tap30.hiss.properties.HissPropertiesFromEnv}
* or any custom implementation of
* {@link io.github.tap30.hiss.properties.HissProperties}
* can be used.
* @param encryptors custom {@link Encryptor} implementations. Can be empty but not null.
* @param hashers custom {@link Hasher} implementations. Can be empty but not null.
* @return {@link Hiss} instance.
* @throws IllegalArgumentException if the properties are not valid.
*/
public static Hiss createHiss(HissProperties hissProperties,
Set<Encryptor> encryptors,
Set<Hasher> hashers) {
Objects.requireNonNull(hissProperties);
Objects.requireNonNull(encryptors);
Objects.requireNonNull(hashers);

var keyHashGenerator = new KeyHashGenerator(BCrypt.withDefaults(), BCrypt.verifyer());
new HissPropertiesValidator(keyHashGenerator).validate(hissProperties);
return new Hiss(hissProperties, keyHashGenerator);

encryptors = addDefaultEncryptors(encryptors);
hashers = addDefaultHashers(hashers);

var hissEncryptor = new HissEncryptor(
encryptors,
hissProperties.getKeys(),
hissProperties.getDefaultEncryptionAlgorithm(),
hissProperties.getDefaultEncryptionKeyId()
);
var hissHasher = new HissHasher(
hashers,
hissProperties.getKeys(),
hissProperties.getDefaultHashingAlgorithm(),
hissProperties.getDefaultHashingKeyId()
);
var hissObjectEncryptor = new HissObjectEncryptor(hissEncryptor, hissHasher);

logInitializingHiss(hissProperties, encryptors, hashers);
if (hissProperties.isKeyHashGenerationEnabled()) {
keyHashGenerator.generateAndLogHashes(hissProperties.getKeys().values());
}

return new Hiss(hissEncryptor, hissHasher, hissObjectEncryptor);
}

private static @NotNull Set<Encryptor> addDefaultEncryptors(Set<Encryptor> encryptors) {
encryptors = new HashSet<>(encryptors);
encryptors.add(new AesCbcPkcs5PaddingEncryptor());
encryptors.add(new AesGcmNoPaddingEncryptor());
encryptors.add(new TapsiAesCbcEncryptor());
encryptors.add(new TapsiAesGcmEncryptor());
encryptors = Collections.unmodifiableSet(encryptors);
return encryptors;
}

private static @NotNull Set<Hasher> addDefaultHashers(Set<Hasher> hashers) {
hashers = new HashSet<>(hashers);
hashers.add(new HmacSha256Hasher());
hashers.add(new TapsiHmacSha256Hasher());
hashers = Collections.unmodifiableSet(hashers);
return hashers;
}

private static void logInitializingHiss(HissProperties hissProperties,
Set<Encryptor> encryptors,
Set<Hasher> hashers) {
logger.log(Level.INFO, "Hiss initialized:\n" +
" Loaded Keys: {0}\n" +
" Default Encryption Key ID: {1}\n" +
" Default Encryption Algorithm: {2}\n" +
" Default Hashing Key ID: {3}\n" +
" Default Hashing Algorithm: {4}\n" +
" Encryptors: {5}\n" +
" Hashers: {6}\n",
new Object[]{
hissProperties.getKeys().keySet(),
hissProperties.getDefaultEncryptionKeyId(),
hissProperties.getDefaultEncryptionAlgorithm(),
hissProperties.getDefaultHashingKeyId(),
hissProperties.getDefaultHashingAlgorithm(),
encryptors.stream().map(Encryptor::getName).collect(Collectors.toSet()),
hashers.stream().map(Hasher::getName).collect(Collectors.toSet())
});
}

}
18 changes: 11 additions & 7 deletions src/main/java/io/github/tap30/hiss/HissObjectEncryptor.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package io.github.tap30.hiss;

import lombok.Value;
import org.intellij.lang.annotations.Language;
import io.github.tap30.hiss.encryptor.HissEncryptor;
import io.github.tap30.hiss.hasher.HissHasher;
import io.github.tap30.hiss.utils.ReflectionUtils;
import io.github.tap30.hiss.utils.StringUtils;
import lombok.Value;
import org.intellij.lang.annotations.Language;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiConsumer;
Expand All @@ -19,10 +20,13 @@ class HissObjectEncryptor {
private final static Map<Class<?>, ClassDescription> CLASSES_DESCRIPTION_CACHE = new HashMap<>();

private final HissEncryptor hissEncryptor;
private final HissHasher hissHasher;


public HissObjectEncryptor(HissEncryptor hissEncryptor) {
this.hissEncryptor = hissEncryptor;
public HissObjectEncryptor(HissEncryptor hissEncryptor,
HissHasher hissHasher) {
this.hissEncryptor = Objects.requireNonNull(hissEncryptor);
this.hissHasher = Objects.requireNonNull(hissHasher);
}

public void encryptObject(Object domainObject) {
Expand Down Expand Up @@ -78,7 +82,7 @@ private void encryptField(FieldAnnotatedWithEncrypted fieldAnnotatedWithEncrypte
var encryptedContent = this.hissEncryptor.encrypt(content, pattern);
fieldAnnotatedWithEncrypted.getContentField().getSetter().invoke(object, encryptedContent);
if (fieldAnnotatedWithEncrypted.getEncryptedAnnotation().hashingEnabled()) {
var hashedContent = this.hissEncryptor.hash(content, pattern);
var hashedContent = this.hissHasher.hash(content, pattern);
fieldAnnotatedWithEncrypted.getHashField().getSetter().invoke(object, hashedContent);
}
} catch (Exception e) {
Expand Down Expand Up @@ -166,7 +170,7 @@ private static Field getField(String fieldName, Class<?> clazz) {
}
}

private static String getContent(FieldAnnotatedWithEncrypted fieldAnnotatedWithEncrypted, Object object) throws IllegalAccessException, InvocationTargetException {
private static String getContent(FieldAnnotatedWithEncrypted fieldAnnotatedWithEncrypted, Object object) {
return ReflectionUtils.invokeGetter(fieldAnnotatedWithEncrypted.getContentField().getGetter(), object, String.class);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.github.tap30.hiss.encryptor;

import javax.crypto.spec.IvParameterSpec;

public class AesCbcPkcs5PaddingEncryptor extends BaseJavaEncryptor {

private final static String ALGORITHM_NAME = "AES/CBC/PKCS5Padding";

public AesCbcPkcs5PaddingEncryptor() {
super("AES", ALGORITHM_NAME, 16, IvParameterSpec::new);
}

@Override
public String getName() {
return ALGORITHM_NAME;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.tap30.hiss.encryptor;

import javax.crypto.spec.GCMParameterSpec;

public class AesGcmNoPaddingEncryptor extends BaseJavaEncryptor {

private static final String ALGORITHM_NAME = "AES/GCM/NoPadding";

public AesGcmNoPaddingEncryptor() {
super("AES", ALGORITHM_NAME, 16, iv -> new GCMParameterSpec(128, iv));
}

@Override
public String getName() {
return ALGORITHM_NAME;
}

}
Loading

0 comments on commit 3f4ad58

Please sign in to comment.