165 lines
5.3 KiB
Java
165 lines
5.3 KiB
Java
package net.locusworks.crypto;
|
|
|
|
import javax.crypto.BadPaddingException;
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.IllegalBlockSizeException;
|
|
import javax.crypto.KeyGenerator;
|
|
import javax.crypto.spec.IvParameterSpec;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import net.locusworks.crypto.utils.RandomString;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.InvalidAlgorithmParameterException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.SecureRandom;
|
|
import java.util.Base64;
|
|
|
|
|
|
/**
|
|
* AES encryption/decryption class
|
|
* This class will encrypt/decrypt data. The encryption key is never known.
|
|
* Instead it is is generated by the provided seed. As long as the seed stays the same
|
|
* the key will remain the same and the encryption/decryption will work. This
|
|
* provides and added security.
|
|
* @author Isaac Parenteau
|
|
* @version 1.0.0
|
|
* @date 02/15/2018
|
|
*
|
|
*/
|
|
public class AES {
|
|
|
|
private static final String ENCRYPTION_TYPE = "AES";
|
|
private static final String ENCRYPTION_ALGORITH = "AES/CBC/PKCS5Padding";
|
|
private static final String PROVIDER = "SunJCE";
|
|
private static final String ALGORITHM = "SHA1PRNG";
|
|
|
|
private Cipher cipher;
|
|
|
|
private SecretKeySpec secretKeySpec;
|
|
|
|
private IvParameterSpec ivParamSpec;
|
|
|
|
private String seed;
|
|
|
|
private void initSecureKey(String seed) {
|
|
try {
|
|
SecureRandom sr = getSecureRandom(seed);
|
|
KeyGenerator generator = KeyGenerator.getInstance(ENCRYPTION_TYPE);
|
|
generator.init(128, sr);
|
|
init(generator.generateKey().getEncoded(), sr);
|
|
} catch (Exception ex) {
|
|
System.err.println(ex);
|
|
throw new IllegalArgumentException("Unable to initalize encryption:", ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the secure random object to be the same across all platforms
|
|
* with regard to provider and algorithm used
|
|
* @param seed Seed to initialize SecureRandom with
|
|
* @return SecureRandom object
|
|
* @throws NoSuchAlgorithmException thrown when algorithm can't be used
|
|
* @throws NoSuchProviderException thrown when the provider cant be found
|
|
*/
|
|
private static SecureRandom getSecureRandom(String seed) throws NoSuchAlgorithmException {
|
|
SecureRandom sr = SecureRandom.getInstance(ALGORITHM);
|
|
sr.setSeed(seed.getBytes(StandardCharsets.UTF_8));
|
|
return sr;
|
|
}
|
|
|
|
/**
|
|
* Initialize the aes engine
|
|
* @param key secret key to use
|
|
* @param sr
|
|
*/
|
|
private void init(final byte[] key, SecureRandom sr) {
|
|
try {
|
|
this.cipher = Cipher.getInstance(ENCRYPTION_ALGORITH, PROVIDER);
|
|
this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE);
|
|
this.ivParamSpec = new IvParameterSpec(RandomString.getBytes(16, sr));
|
|
} catch (Exception ex) {
|
|
System.err.println(ex);
|
|
throw new IllegalArgumentException("Unable to initalize encryption:", ex);
|
|
}
|
|
}
|
|
|
|
/***
|
|
* Encrypt a text string
|
|
* @param plainText String to encrypt
|
|
* @return encrypted string
|
|
*/
|
|
public String encrypt(String plainText) {
|
|
if (StringUtils.isBlank(plainText)) {
|
|
plainText = "";
|
|
}
|
|
try {
|
|
cipher.init(Cipher.ENCRYPT_MODE, this.secretKeySpec, this.ivParamSpec);
|
|
byte[] cypherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
|
return new String(Base64.getEncoder().encode(cypherText), StandardCharsets.UTF_8);
|
|
} catch (Exception ex) {
|
|
throw new IllegalArgumentException(ex.getMessage(), ex);
|
|
}
|
|
}
|
|
|
|
/***
|
|
* Decrypt an encrypted string
|
|
* @param cipherString encrypted string to decrypt
|
|
* @return unecrypted string
|
|
*/
|
|
public String decrypt(String cipherString) {
|
|
if (StringUtils.isBlank(cipherString)) {
|
|
return "";
|
|
}
|
|
byte[] cipherText = Base64.getDecoder().decode(cipherString.getBytes(StandardCharsets.UTF_8));
|
|
try {
|
|
cipher.init(Cipher.DECRYPT_MODE, this.secretKeySpec, this.ivParamSpec);
|
|
return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8);
|
|
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
|
|
throw new IllegalArgumentException(e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
public AES setSeed(String seed) {
|
|
if (this.seed == null || !this.seed.equals(seed)) {
|
|
initSecureKey(seed);
|
|
}
|
|
this.seed = seed;
|
|
return this;
|
|
}
|
|
|
|
public final String getSeed() {
|
|
return this.seed;
|
|
}
|
|
|
|
public static AES createInstance() {
|
|
return createInstance(RandomString.getString(16));
|
|
}
|
|
|
|
public static AES createInstance(byte[] byteSeed) {
|
|
String seed = new String(byteSeed, StandardCharsets.UTF_8);
|
|
return createInstance(seed);
|
|
}
|
|
|
|
public static AES createInstance(String seed) {
|
|
AES aes = new AES();
|
|
aes.setSeed(seed);
|
|
return aes;
|
|
}
|
|
|
|
public static void main(String[] args) throws NoSuchAlgorithmException {
|
|
if (args == null || !(args.length > 0)) {
|
|
throw new IllegalArgumentException("No args provided. Need password as argument");
|
|
}
|
|
if (args.length % 2 == 0) {
|
|
System.out.println(AES.createInstance(String.valueOf(args[1])).decrypt(String.valueOf(args[0])));
|
|
} else {
|
|
System.out.println(AES.createInstance().encrypt(String.valueOf(args[0])));
|
|
}
|
|
}
|
|
|
|
}
|