Adds a class for AES encryption which might be used instead of the current encryption methods
This commit is contained in:
parent
48ac82f4d4
commit
a9f2017a40
@ -0,0 +1,159 @@
|
||||
package net.knarcraft.bookswithoutborders.encryption;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* This class represents and AES encryptor/decryptor
|
||||
*
|
||||
* <p>The AES encryptor/decryptor can encrypt/decrypt strings using a supplied password. It has a function for
|
||||
* generating the IV, if a new one is necessary.</p>
|
||||
*
|
||||
* @author EpicKnarvik97
|
||||
*/
|
||||
public class AES {
|
||||
|
||||
//TODO: Generate salt for each installation, and figure out what to to with the iv parameter
|
||||
private final IvParameterSpec ivParameterSpec;
|
||||
private final byte[] passwordSalt;
|
||||
|
||||
/**
|
||||
* Instantiates a new AES encryptor
|
||||
* @param initializationVector <p>The initialization vector to use for CBC</p>
|
||||
* @param passwordSalt <p>The password salt to use</p>
|
||||
*/
|
||||
public AES(byte[] initializationVector, byte[] passwordSalt) {
|
||||
this.ivParameterSpec = new IvParameterSpec(initializationVector);
|
||||
this.passwordSalt = passwordSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts or decrypts the given text
|
||||
* @param input <p>The input to encrypt or decrypt</p>
|
||||
* @param password <p>The password to use for key generation</p>
|
||||
* @param encrypt <p>Whether to encrypt or decrypt the input</p>
|
||||
* @return <p>The encrypted/decrypted input, or null if anything went wrong</p>
|
||||
*/
|
||||
public String encryptDecryptText(String input, String password, boolean encrypt) {
|
||||
//Make a key from the password
|
||||
SecretKeySpec secretKeySpec = getKeyFromPassword(password);
|
||||
//Get cipher instance
|
||||
Cipher aes = getAESCipher();
|
||||
if (aes == null || secretKeySpec == null) {
|
||||
return null;
|
||||
}
|
||||
int mode;
|
||||
if (encrypt) {
|
||||
mode = Cipher.ENCRYPT_MODE;
|
||||
} else {
|
||||
mode = Cipher.DECRYPT_MODE;
|
||||
}
|
||||
//Initialize cipher
|
||||
try {
|
||||
aes.init(mode, secretKeySpec, ivParameterSpec);
|
||||
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
//Perform encryption/decryption and output result
|
||||
try {
|
||||
byte[] output = aes.doFinal(getInputBytes(input, encrypt));
|
||||
return createResult(output, encrypt);
|
||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a 16-byte initialization vector
|
||||
* @return <p>An initialization vector</p>
|
||||
*/
|
||||
public static byte[] generateIV() {
|
||||
byte[] initializationVector = new byte[16];
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
secureRandom.nextBytes(initializationVector);
|
||||
return initializationVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the input string into bytes
|
||||
* @param input <p>The input to encrypt or decrypt</p>
|
||||
* @param encryption <p>Whether the input should be encrypted or decrypted</p>
|
||||
* @return <p>The input in byte format</p>
|
||||
*/
|
||||
private byte[] getInputBytes(String input, boolean encryption) {
|
||||
if (encryption) {
|
||||
return input.getBytes();
|
||||
} else {
|
||||
return Base64.getDecoder().decode(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the result bytes into a string
|
||||
* @param output <p>The output from encryption or decryption</p>
|
||||
* @param encryption <p>Whether the output came from encryption or decryption</p>
|
||||
* @return <p>The output as a string</p>
|
||||
*/
|
||||
private String createResult(byte[] output, boolean encryption) {
|
||||
if (encryption) {
|
||||
return Base64.getEncoder().encodeToString(output);
|
||||
} else {
|
||||
return new String(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an AES cipher instance
|
||||
* @return <p>An AES cipher instance, or null if something went wrong</p>
|
||||
*/
|
||||
private Cipher getAESCipher() {
|
||||
Cipher aes;
|
||||
try {
|
||||
aes = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return aes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an encryption key from a supplied password
|
||||
* @param password <p>A user supplied password</p>
|
||||
* @return <p>A secret key spec or null if something went wrong</p>
|
||||
*/
|
||||
private SecretKeySpec getKeyFromPassword(String password) {
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), this.passwordSalt, 1000, 128);
|
||||
SecretKeyFactory keyFactory;
|
||||
try {
|
||||
keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
SecretKey tmp;
|
||||
try {
|
||||
tmp = keyFactory.generateSecret(spec);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return new SecretKeySpec(tmp.getEncoded(), "AES");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.knarcraft.bookswithoutborders.encryption;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class AESTest {
|
||||
|
||||
@Test
|
||||
public void encryptDecryptTest() {
|
||||
String plainText = "A lot of text";
|
||||
String password = "abc123";
|
||||
|
||||
AES aes = new AES(AES.generateIV(), AES.generateIV());
|
||||
|
||||
String encrypted = aes.encryptDecryptText(plainText, password, true);
|
||||
assertFalse(encrypted.equals(plainText));
|
||||
String decrypted = aes.encryptDecryptText(encrypted, password, false);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user