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