) loadSettingsGUI(rootWindow.getScene().getWindow()).values().toArray()[0];
+ System.out.println(currentConfigSettings);
+ textKeyEntry.setText(currentConfigSettings.get("textKey"));
+ textSaltEntry.setText(currentConfigSettings.get("textSalt"));
+ textAlgorithmBox.setValue(currentConfigSettings.get("textAlgorithm"));
+
+ fileEnDecryptKeyEntry.setText(currentConfigSettings.get("fileEnDecryptKey"));
+ fileEnDecryptSaltEntry.setText(currentConfigSettings.get("fileEnDecryptSalt"));
+ fileEnDecryptAlgorithmBox.setValue(currentConfigSettings.get("fileEnDecryptAlgorithm"));
+
+ fileDeleteIterationsEntry.setText(currentConfigSettings.get("fileDeleteIterations"));
+
+ removeFileFromFileBox.setSelected(Boolean.parseBoolean(currentConfigSettings.get("removeFromFileBox")));
+ limitNumberOfThreads.setSelected(Boolean.parseBoolean(currentConfigSettings.get("limitNumberOfThreads")));
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ try {
+ SecureDelete.deleteFileLineByLine(config, 5);
+ isConfig = false;
+ } catch (NoSuchAlgorithmException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ });
+ t.start();
+ }
+}
diff --git a/src/org/blueshard/cryptogx/EnDecrypt.java b/src/org/blueshard/cryptogx/EnDecrypt.java
new file mode 100644
index 0000000..2a49c3b
--- /dev/null
+++ b/src/org/blueshard/cryptogx/EnDecrypt.java
@@ -0,0 +1,531 @@
+package org.blueshard.cryptogx;
+
+import javax.crypto.*;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class EnDecrypt {
+
+ public static class AES extends Thread {
+
+ private int iterations = 1000;
+ private int keyLength = 256;
+
+ private final String key;
+ private final byte[] salt;
+
+ public AES(String key, byte[] salt) {
+ this.key = key;
+ this.salt = salt;
+ }
+
+ public AES(String key, byte[] salt, int iterations) {
+ this.key = key;
+ this.salt = salt;
+ this.iterations = iterations;
+ }
+
+ public AES(String key, byte[] salt, int iterations, int keyLength) {
+ this.key = key;
+ this.salt = salt;
+ this.iterations = iterations;
+ this.keyLength = keyLength;
+ }
+
+ /**
+ * Creates a secret key from given (plain text) key and salt
+ *
+ * @param key from which a secret key should be created
+ * @param salt from which a secret key should be created
+ * @return the secret key
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ */
+ public byte[] createSecretKey(String key, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ KeySpec keySpec = new PBEKeySpec(key.toCharArray(), salt, this.iterations, keyLength);
+ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
+
+ return factory.generateSecret(keySpec).getEncoded();
+ }
+
+ /**
+ * Writes {@param inputStream} to {@param outputStream}
+ *
+ * @param inputStream from which is written
+ * @param outputStream to which is written
+ * @param buffer
+ * @throws IOException
+ */
+ public static void writeLineByLine(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws IOException {
+ int numOfBytesRead;
+ while ((numOfBytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, numOfBytesRead);
+ }
+ outputStream.close();
+ inputStream.close();
+ }
+
+ /**
+ * Encrypts a file randomly line by line
+ *
+ * @see EnDecrypt.AES#encryptRandomLineByLine(InputStream, OutputStream, byte[])
+ */
+ public static void encryptFileRandomLineByLine(File inputFile, File outputFile, byte[] buffer) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidAlgorithmParameterException {
+ encryptRandomLineByLine(new FileInputStream(inputFile), new FileOutputStream(outputFile), buffer);
+ }
+
+ /**
+ * Encrypts a {@link InputStream} randomly line by line
+ *
+ * @param inputStream that should be encrypted
+ * @param outputStream to which the encrypted {@param inputStream} should be written to
+ * @param buffer
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public static void encryptRandomLineByLine(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidAlgorithmParameterException {
+ KeyGenerator randomKey = KeyGenerator.getInstance("AES");
+ Key secretKey = new SecretKeySpec(randomKey.generateKey().getEncoded(), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+ CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
+ writeLineByLine(cipherInputStream, outputStream, buffer);
+ }
+
+ /**
+ * En- / decrypts the {@param inputFile}
+ *
+ * @param cipherMode says if the file should be en- or decrypted
+ * @param inputFile that should be en- / decrypted
+ * @param outputFile to which the en- / decrypted {@param inputFile} should be written to
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ */
+ public void enDecryptFileAllInOne(int cipherMode, File inputFile, File outputFile) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
+ Key secretKey = new SecretKeySpec(createSecretKey(key, salt), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(cipherMode, secretKey);
+
+ FileInputStream inputStream = new FileInputStream(inputFile);
+ byte[] inputBytes = new byte[(int) inputFile.length()];
+ inputStream.read(inputBytes);
+
+ byte[] outputBytes = cipher.doFinal(inputBytes);
+
+ FileOutputStream outputStream = new FileOutputStream(outputFile);
+ outputStream.write(outputBytes);
+
+ inputStream.close();
+ outputStream.close();
+ }
+
+ /**
+ * En- / decrypts the {@param inputBytes}
+ *
+ * @param cipherMode says if the file should be en- or decrypted
+ * @param inputBytes that should be en- / decrypted
+ * @param outputStream to which the en- / decrypted {@param inputFile} should be written to
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ */
+ public void enDecryptFileAllInOne(int cipherMode, byte[] inputBytes, OutputStream outputStream) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
+ Key secretKey = new SecretKeySpec(createSecretKey(key, salt), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(cipherMode, secretKey);
+
+ byte[] outputBytes = cipher.doFinal(inputBytes);
+
+ outputStream.write(outputBytes);
+
+ outputStream.close();
+ }
+
+ /**
+ * En- / decrypts the {@param inputFile}
+ *
+ * @param cipherMode says if the file should be en- or decrypted
+ * @param inputFile that should be en- / decrypted
+ * @param outputFile to which the en- / decrypted {@param inputFile} should be written to
+ * @param buffer
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public void enDecryptLineByLine(int cipherMode, File inputFile, File outputFile, byte[] buffer) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidAlgorithmParameterException {
+ Key secretKey = new SecretKeySpec(createSecretKey(key, salt), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(cipherMode, secretKey);
+
+ FileInputStream fileInputStream = new FileInputStream(inputFile);
+ FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
+
+ if (cipherMode == Cipher.ENCRYPT_MODE) {
+ CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);
+ writeLineByLine(cipherInputStream, fileOutputStream, buffer);
+ } else if (cipherMode == Cipher.DECRYPT_MODE) {
+ CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher);
+ writeLineByLine(fileInputStream, cipherOutputStream, buffer);
+ }
+ }
+
+ /**
+ * En- / decrypts the {@param inputStream}
+ *
+ * @param cipherMode says if the file should be en- or decrypted
+ * @param inputStream that should be en- / decrypted
+ * @param outputStream to which the en- / decrypted {@param inputFile} should be written to
+ * @param buffer
+ * @throws InvalidKeySpecException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public void enDecryptLineByLine(int cipherMode, InputStream inputStream, OutputStream outputStream, byte[] buffer) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, InvalidAlgorithmParameterException {
+ Key secretKey = new SecretKeySpec(createSecretKey(key, salt), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(cipherMode, secretKey);
+
+ if (cipherMode == Cipher.ENCRYPT_MODE) {
+ CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
+ writeLineByLine(cipherInputStream, outputStream, buffer);
+ } else if (cipherMode == Cipher.DECRYPT_MODE) {
+ CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
+ writeLineByLine(inputStream, cipherOutputStream, buffer);
+ }
+ }
+
+ /**
+ * Encrypt {@param bytes} randomly
+ *
+ * @see EnDecrypt.AES#encryptRandom(byte[])
+ */
+ public static String encryptRandom(String string) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+ return encryptRandom(string.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Encrypt {@param bytes} randomly
+ *
+ * @param bytes that should be encrypted
+ * @return the encrypted {@param bytes} as {@link String}
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeyException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ */
+ public static String encryptRandom(byte[] bytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+ KeyGenerator randomKey = KeyGenerator.getInstance("AES");
+ Key secretKey = new SecretKeySpec(randomKey.generateKey().getEncoded(), "AES");
+
+ Cipher encryptRandomCipher = Cipher.getInstance("AES");
+ encryptRandomCipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ return Base64.getEncoder().encodeToString(encryptRandomCipher.doFinal(bytes));
+ }
+
+ /**
+ * Encrypts a file randomly at once
+ *
+ * @see EnDecrypt.AES#encryptFileRandomAllInOne(File, File)
+ */
+ public static void encryptFileRandomAllInOne(String inputFilename, String outputFilename) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
+ encryptFileRandomAllInOne(new File(inputFilename), new File(outputFilename));
+ }
+
+ /**
+ * Encrypts a file randomly at once
+ *
+ * @param inputFile that should be encrypted
+ * @param outputFile to which the encrypted file should be written to
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ */
+ public static void encryptFileRandomAllInOne(File inputFile, File outputFile) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
+ KeyGenerator randomKey = KeyGenerator.getInstance("AES");
+ Key secretKey = new SecretKeySpec(randomKey.generateKey().getEncoded(), "AES");
+
+ Cipher cipher = Cipher.getInstance("AES");
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+ FileInputStream inputStream = new FileInputStream(inputFile);
+ byte[] inputBytes = new byte[(int) inputFile.length()];
+ inputStream.read(inputBytes);
+
+ byte[] outputBytes = cipher.doFinal(inputBytes);
+
+ FileOutputStream outputStream = new FileOutputStream(outputFile);
+ outputStream.write(outputBytes);
+
+ inputStream.close();
+ outputStream.close();
+ }
+
+ /**
+ * Encrypts {@param inputFilename} randomly line by line and write it to {@param outputFilename}
+ *
+ * @see EnDecrypt.AES#encryptRandomLineByLine(InputStream, OutputStream, byte[])
+ */
+ public static void encryptFileRandomLineByLine(String inputFilename, String outputFilename) throws InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IOException {
+ encryptRandomLineByLine(new FileInputStream(inputFilename), new FileOutputStream(outputFilename), new byte[64]);
+ }
+
+ /**
+ * Encrypts {@param inputFilename} randomly line by line and write it to {@param outputFilename}
+ *
+ * @see EnDecrypt.AES#encryptRandomLineByLine(InputStream, OutputStream, byte[])
+ */
+ public static void encryptFileRandomLineByLine(String inputFilename, String outputFilename, byte[] buffer) throws InvalidKeyException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IOException {
+ encryptRandomLineByLine(new FileInputStream(inputFilename), new FileOutputStream(outputFilename), buffer);
+ }
+
+ /**
+ * Decrypt encrypted {@param encryptedString}
+ *
+ * @see EnDecrypt.AES#decrypt(byte[])
+ */
+ public String decrypt(String encryptedString) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
+ Key secretKey = new SecretKeySpec(createSecretKey(key, salt), "AES");
+
+ Cipher decryptCipher = Cipher.getInstance("AES");
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
+ return new String(decryptCipher.doFinal(Base64.getDecoder().decode(encryptedString)), StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Decrypt encrypted {@param bytes}
+ *
+ * @param bytes that should be decrypted
+ * @return decrypted bytes
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ * @throws InvalidKeyException
+ */
+ public byte[] decrypt(byte[] bytes) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
+ return decrypt(Arrays.toString(bytes)).getBytes(StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Encrypt {@param bytes}
+ *
+ * @see EnDecrypt.AES#encrypt(byte[])
+ */
+ public String encrypt(String string) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
+ return Base64.getEncoder().encodeToString(encrypt(string.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ /**
+ * Encrypt {@param bytes}
+ *
+ * @param bytes that should be encrypted
+ * @return encrypted bytes
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidKeySpecException
+ * @throws InvalidKeyException
+ */
+ public byte[] encrypt(byte[] bytes) throws BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
+ Key secretKey = new SecretKeySpec(createSecretKey(key, salt), "AES");
+
+ Cipher encryptCipher = Cipher.getInstance("AES");
+ encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ return encryptCipher.doFinal(bytes);
+ }
+
+ /**
+ * Decrypt encrypted {@param inputFilename} to {@param outputFilename} at once
+ *
+ * @param inputFilename that should be decrypted
+ * @param outputFilename to which the decrypted content should be written to
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws InvalidKeyException
+ * @throws InvalidKeySpecException
+ */
+ public void decryptFileAllInOne(String inputFilename, String outputFilename) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptFileAllInOne(Cipher.DECRYPT_MODE, new File(inputFilename), new File(outputFilename));
+ }
+
+ /**
+ * Decrypt encrypted {@param inputBytes} to {@param outputStream} at once
+ *
+ * @param inputBytes that should be decrypted
+ * @param outputStream to which the decrypted content should be written to
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws InvalidKeyException
+ * @throws InvalidKeySpecException
+ */
+ public void decryptFileAllInOne(byte[] inputBytes, OutputStream outputStream) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptFileAllInOne(Cipher.DECRYPT_MODE, inputBytes, outputStream);
+ }
+
+ /**
+ * Decrypt encrypted {@param inputFilename} to {@param outputFilename} line by line
+ *
+ * @see EnDecrypt.AES#decryptFileLineByLine(InputStream, OutputStream, byte[])
+ */
+ public void decryptFileLineByLine(String inputFilename, String outputFilename) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.DECRYPT_MODE, new File(inputFilename), new File(outputFilename), new byte[64]);
+ }
+
+ /**
+ * Decrypt encrypted {@param inputStream} to {@param outputStream} line by line
+ *
+ * @see EnDecrypt.AES#decryptFileLineByLine(InputStream, OutputStream, byte[])
+ */
+ public void decryptFileLineByLine(InputStream inputStream, OutputStream outputStream) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.DECRYPT_MODE, inputStream, outputStream, new byte[64]);
+ }
+
+ /**
+ * Decrypt encrypted {@param inputFilename} to {@param outputFilename} line by line
+ *
+ * @see EnDecrypt.AES#decryptFileLineByLine(InputStream, OutputStream, byte[])
+ */
+ public void decryptFileLineByLine(String inputFilename, String outputFilename, byte[] buffer) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.DECRYPT_MODE, new File(inputFilename), new File(outputFilename), buffer);
+ }
+
+ /**
+ * Decrypt encrypted {@param inputStream} to {@param outputStream} line by line
+ *
+ * @param inputStream that should be decrypted
+ * @param outputStream to which the decrypted content should be written to
+ * @param buffer
+ * @throws NoSuchPaddingException
+ * @throws InvalidAlgorithmParameterException
+ * @throws NoSuchAlgorithmException
+ * @throws IOException
+ * @throws InvalidKeyException
+ * @throws InvalidKeySpecException
+ */
+ public void decryptFileLineByLine(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.DECRYPT_MODE, inputStream, outputStream, buffer);
+ }
+
+ /**
+ * DEncrypt {@param inputFilename} to {@param outputFilename} at once
+ *
+ * @param inputFilename that should be encrypt
+ * @param outputFilename to which the encrypted content should be written to
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws InvalidKeyException
+ * @throws InvalidKeySpecException
+ */
+ public void encryptFileAllInOne(String inputFilename, String outputFilename) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptFileAllInOne(Cipher.ENCRYPT_MODE, new File(inputFilename), new File(outputFilename));
+ }
+
+ /**
+ * Encrypt {@param inputBytes} to {@param outputStream} at once
+ *
+ * @param inputBytes that should be encrypted
+ * @param outputStream to which the encrypted content should be written to
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws IOException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ * @throws InvalidKeyException
+ * @throws InvalidKeySpecException
+ */
+ public void encryptFileAllInOne(byte[] inputBytes, OutputStream outputStream) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptFileAllInOne(Cipher.ENCRYPT_MODE, inputBytes, outputStream);
+ }
+
+ /**
+ * Encrypt {@param inputFilename} to {@param outputFilename} line by line
+ *
+ * @see EnDecrypt.AES#encryptFileLineByLine(InputStream, OutputStream, byte[])
+ */
+ public void encryptFileLineByLine(String inputFilename, String outputFilename) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.ENCRYPT_MODE, new File(inputFilename), new File(outputFilename), new byte[64]);
+ }
+
+ /**
+ * Encrypt {@param inputStream} to {@param outputStream} line by line
+ *
+ * @see EnDecrypt.AES#encryptFileLineByLine(InputStream, OutputStream, byte[])
+ */
+ public void encryptFileLineByLine(InputStream inputStream, OutputStream outputStream) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.ENCRYPT_MODE, inputStream, outputStream, new byte[64]);
+ }
+
+ /**
+ * Encrypt {@param inputFilename} to {@param outputFilename} line by line
+ *
+ * @see EnDecrypt.AES#encryptFileLineByLine(InputStream, OutputStream, byte[])
+ */
+ public void encryptFileLineByLine(String inputFilename, String outputFilename, byte[] buffer) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.ENCRYPT_MODE, new File(inputFilename), new File(outputFilename), buffer);
+ }
+
+ /**
+ * Encrypt {@param inputStream} to {@param outputStream} line by line
+ *
+ * @param inputStream that should be encrypted
+ * @param outputStream to which the encrypted {@param inputStream} should be written to
+ * @param buffer
+ * @throws NoSuchPaddingException
+ * @throws InvalidAlgorithmParameterException
+ * @throws NoSuchAlgorithmException
+ * @throws IOException
+ * @throws InvalidKeyException
+ * @throws InvalidKeySpecException
+ */
+ public void encryptFileLineByLine(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, InvalidKeyException, InvalidKeySpecException {
+ enDecryptLineByLine(Cipher.ENCRYPT_MODE, inputStream, outputStream, buffer);
+ }
+
+ }
+}
diff --git a/src/org/blueshard/cryptogx/Main.java b/src/org/blueshard/cryptogx/Main.java
new file mode 100644
index 0000000..65a9fe7
--- /dev/null
+++ b/src/org/blueshard/cryptogx/Main.java
@@ -0,0 +1,269 @@
+/**
+ *
+ * @author blueShard
+ * @version 1.11.0
+ */
+
+package org.blueshard.cryptogx;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.image.Image;
+import javafx.stage.Screen;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.swing.*;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class Main extends Application {
+
+ protected static final int NON_PORTABLE = 414729643;
+ protected static final int PORTABLE = 245714766;
+
+ protected static final int TYPE = PORTABLE;
+
+ protected final static String configDefaultName = "";
+ protected final static String configDefaultEncryptHash = "";
+ protected final static String configDefaultTextKey = "";
+ protected final static String configDefaultTextSalt = "";
+ protected final static String configDefaultTextAlgorithm = "AES";
+ protected final static String configDefaultFileEnDecryptKey = "";
+ protected final static String configDefaultFileEnDecryptSalt = "";
+ protected final static String configDefaultFileEnDecryptAlgorithm = "AES";
+ protected final static int configDefaultFileDeleteIterations = 5;
+ protected final static String configDefaultFileOutputPath = "";
+ protected final static boolean configDefaultRemoveFileFromFileBox = false;
+ protected final static boolean configDefaultLimitNumberOfThreads = true;
+
+ protected static ArrayList textAlgorithms = new ArrayList<>();
+ protected static ArrayList fileEnDecryptAlgorithms = new ArrayList<>();
+
+ private static Stage mainStage;
+ private double rootWindowX, rootWindowY;
+ protected static File config;
+ protected static boolean isConfig;
+
+ /**
+ * Start the GUI
+ *
+ * @param primaryStage of the GUI
+ * @throws IOException if issues with loading 'mainGUI.fxml'
+ */
+ @Override
+ public void start(Stage primaryStage) throws IOException {
+ Thread.setDefaultUncaughtExceptionHandler(Main::exceptionAlert);
+
+ mainStage = primaryStage;
+
+ Parent root = FXMLLoader.load(getClass().getResource("resources/mainGUI.fxml"));
+ primaryStage.initStyle(StageStyle.UNDECORATED);
+ primaryStage.setResizable(false);
+ primaryStage.setTitle("cryptoGX");
+ primaryStage.getIcons().add(new Image(getClass().getResource("resources/cryptoGX.png").toExternalForm()));
+ Scene scene = new Scene(root, 900, 470);
+
+ scene.setOnMouseDragged(event -> {
+ primaryStage.setX(event.getScreenX() + rootWindowX);
+ primaryStage.setY(event.getScreenY() + rootWindowY);
+ });
+ scene.setOnMousePressed(event -> {
+ rootWindowX = scene.getX() - event.getSceneX();
+ rootWindowY = scene.getY() - event.getSceneY();
+ });
+
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+
+ /**
+ * Enter method for the application.
+ * Can also be used to en- / decrypt text and files or secure delete files without starting GUI
+ *
+ * @param args from the command line
+ * @return status
+ * @throws BadPaddingException
+ * @throws NoSuchAlgorithmException if wrong algorithm is given (command line)
+ * @throws IllegalBlockSizeException if wrong size for key is given (command line)
+ * @throws NoSuchPaddingException
+ * @throws InvalidKeyException if invalid key is given (command line)
+ * @throws InvalidKeySpecException
+ * @throws IOException if files cannot be en- / decrypted or deleted correctly (command line)
+ * @throws InvalidAlgorithmParameterException if wrong algorithm parameters are given (command line)
+ */
+ public static void main(String[] args) throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException, IOException, InvalidAlgorithmParameterException {
+ if (Main.TYPE == Main.NON_PORTABLE) {
+ if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
+ config = new File("C:\\Users\\" + System.getProperty("user.name") + "\\AppData\\Roaming\\cryptoGX\\cryptoGX.config");
+ } else {
+ config = new File("cryptoGX.config");
+ }
+ } else {
+ config = new File("cryptoGX.config");
+ }
+ isConfig = config.isFile();
+ if (args.length == 0) {
+ String version = Runtime.class.getPackage().getImplementationVersion();
+ if (version.startsWith("1.")) {
+ if (Integer.parseInt(version.substring(2, 3)) < 8) {
+ System.out.println("1");
+ JOptionPane.showMessageDialog(null, "Please use java 1.8.0_240 to java 10.*", "ERROR", JOptionPane.ERROR_MESSAGE);
+ } else if (Integer.parseInt(version.substring(6, 9)) < 240) {
+ JOptionPane.showMessageDialog(null, "Please use java 1.8.0_240 to java 10.*", "ERROR", JOptionPane.ERROR_MESSAGE);
+ }
+ } else if (Integer.parseInt(version.substring(0, 2)) > 10) {
+ JOptionPane.showMessageDialog(null, "Please use java 1.8.0_240 to java 10.*", "ERROR", JOptionPane.ERROR_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(null, "Please use java 1.8.0_240 to java 10.*", "ERROR", JOptionPane.ERROR_MESSAGE);
+ }
+ launch(args);
+ } else {
+ args[0] = args[0].replace("-", "");
+ if (args[0].toLowerCase().equals("help") || args[0].toUpperCase().equals("H")) {
+ System.out.println("Usage AES: \n\n" +
+ " Text en- / decryption\n" +
+ " encrypt: AES encrypt \n" +
+ " decrypt: AES decrypt \n\n" +
+ " File en- / decryption\n" +
+ " encrypt: AES encrypt \n" +
+ " decrypt: AES decrypt \n\n" +
+ "File secure delete: ");
+ } else if (args[0].toLowerCase().equals("delete")) {
+ if (args.length > 3) {
+ System.err.println("To many arguments were given, expected 3");
+ } else if (args.length < 3) {
+ System.err.println("To few arguments were given, expected 3");
+ }
+ try {
+ SecureDelete.deleteFileLineByLine(args[2], Integer.parseInt(args[1]));
+ } catch (NumberFormatException e) {
+ System.err.println(args[1] + " must be a number\n Error: " + e.getMessage());
+ }
+ } else if (args[0].toLowerCase().equals("aes")) {
+ if (args.length < 4) {
+ System.err.println("To few arguments were given");
+ System.exit(1);
+ }
+ EnDecrypt.AES aes;
+ if (args[2].isEmpty()) {
+ aes = new EnDecrypt.AES(args[1], new byte[16]);
+ } else {
+ aes = new EnDecrypt.AES(args[1], args[2].getBytes(StandardCharsets.UTF_8));
+ }
+ String type = args[3].toLowerCase();
+ if (type.equals("encrypt")) {
+ System.out.println(aes.encrypt(args[4]));
+ } else if (type.equals("decrypt")) {
+ System.out.println(aes.decrypt(args[4]));
+ } else if (type.equals("fileencrypt") || type.equals("encryptfile")) {
+ aes.encryptFileLineByLine(args[4], args[5]);
+ } else if (type.equals("filedecrypt") ||type.equals("decryptfile")) {
+ aes.decryptFileLineByLine(args[4], args[5]);
+ }
+ }
+ }
+ System.exit(0);
+ }
+
+ /**
+ * "Catch" all uncatched exceptions and opens an alert window
+ *
+ * @param thread which called this method
+ * @param throwable of the thread which called the method
+ */
+ private static void exceptionAlert(Thread thread, Throwable throwable) {
+ throwable.printStackTrace();
+
+ AtomicReference exceptionAlertX = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxX() / 2);
+ AtomicReference exceptionAlertY = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxY() / 2);
+
+ Alert enDecryptError = new Alert(Alert.AlertType.ERROR, "Error: " + throwable, ButtonType.OK);
+ enDecryptError.initStyle(StageStyle.UNDECORATED);
+ enDecryptError.setTitle("Error");
+ ((Stage) enDecryptError.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Main.class.getResource("resources/cryptoGX.png").toExternalForm()));
+
+ Scene window = enDecryptError.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ enDecryptError.setX(dragEvent.getScreenX() + exceptionAlertX.get());
+ enDecryptError.setY(dragEvent.getScreenY() + exceptionAlertY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ exceptionAlertX.set(window.getX() - pressEvent.getSceneX());
+ exceptionAlertY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ enDecryptError.show();
+ }
+
+ /**
+ * Shows an error alert window
+ *
+ * @param message which will the alert show
+ * @param error which will show after the message
+ */
+ protected static void errorAlert(String message, String error) {
+ AtomicReference alertX = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxX() / 2);
+ AtomicReference alertY = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxY() / 2);
+
+ Alert enDecryptError = new Alert(Alert.AlertType.ERROR, message +
+ "\nError: " + error, ButtonType.OK);
+ enDecryptError.initStyle(StageStyle.UNDECORATED);
+ enDecryptError.setTitle("Error");
+ ((Stage) enDecryptError.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Main.class.getResource("resources/cryptoGX.png").toExternalForm()));
+
+ Scene window = enDecryptError.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ enDecryptError.setX(dragEvent.getScreenX() + alertX.get());
+ enDecryptError.setY(dragEvent.getScreenY() + alertY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ alertX.set(window.getX() - pressEvent.getSceneX());
+ alertY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ enDecryptError.show();
+ }
+
+ /**
+ * Shows an warning alert window
+ *
+ * @param message that the alert window will show
+ */
+ protected static void warningAlert(String message) {
+ AtomicReference alertX = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxX() / 2);
+ AtomicReference alertY = new AtomicReference<>(Screen.getPrimary().getBounds().getMaxY() / 2);
+
+ Alert enDecryptError = new Alert(Alert.AlertType.WARNING, message, ButtonType.OK);
+ enDecryptError.initStyle(StageStyle.UNDECORATED);
+ enDecryptError.setTitle("Error");
+ ((Stage) enDecryptError.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Main.class.getResource("resources/cryptoGX.png").toExternalForm()));
+
+ Scene window = enDecryptError.getDialogPane().getScene();
+
+ window.setOnMouseDragged(dragEvent -> {
+ enDecryptError.setX(dragEvent.getScreenX() + alertX.get());
+ enDecryptError.setY(dragEvent.getScreenY() + alertY.get());
+ });
+ window.setOnMousePressed(pressEvent -> {
+ alertX.set(window.getX() - pressEvent.getSceneX());
+ alertY.set(window.getY() - pressEvent.getSceneY());
+ });
+
+ enDecryptError.show();
+ }
+}
diff --git a/src/org/blueshard/cryptogx/SecureDelete.java b/src/org/blueshard/cryptogx/SecureDelete.java
new file mode 100644
index 0000000..6b3a49e
--- /dev/null
+++ b/src/org/blueshard/cryptogx/SecureDelete.java
@@ -0,0 +1,196 @@
+package org.blueshard.cryptogx;
+
+import java.io.*;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+public class SecureDelete {
+
+ /**
+ * Overwrites the file {@param iterations} times at once with random bytes an delete it
+ *
+ * @see SecureDelete#deleteFileAllInOne(File, int)
+ */
+ public static boolean deleteFileAllInOne(String filename, int iterations) throws IOException, NoSuchAlgorithmException {
+ return deleteFileAllInOne(new File(filename), iterations);
+ }
+
+ /**
+ * Overwrites the file {@param iterations} times at once with random bytes and delete it
+ *
+ * @param file that should be deleted
+ * @param iterations how many times the file should be overwritten before it gets deleted
+ * @return if the file could be deleted
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ */
+ public static boolean deleteFileAllInOne(File file, int iterations) throws IOException, NoSuchAlgorithmException {
+ long fileLength = file.length() + 1 ;
+ for (int i=0; i 1000000000) {
+ int numOfByteArrays = (int) Math.ceil((double) fileLength / 1000000000);
+ for (int len=0; lenOverwrites the file {@param iterations} times at once with random bytes (minimal size {@param minFileSize}; maximal size {@param maxFileSize}) and delete it
+ *
+ * @see SecureDelete#deleteFileAllInOne(String, int, long, long)
+ */
+ public static boolean deleteFileAllInOne(String filename, int iterations, long minFileSize, long maxFileSize) throws IOException, NoSuchAlgorithmException {
+ return deleteFileAllInOne(new File(filename), iterations, minFileSize, maxFileSize);
+ }
+
+ /**
+ * Overwrites the file {@param iterations} times at once with random bytes (minimal size {@param minFileSize}; maximal size {@param maxFileSize}) and delete it
+ *
+ * @param file that should be deleted
+ * @param iterations how many times the file should be overwritten before it gets deleted
+ * @param minFileSize is the minimal file size for every {@param iterations}
+ * @param maxFileSize is the maximal file size for every {@param iterations}
+ * @return if the file could be deleted
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ */
+ public static boolean deleteFileAllInOne(File file, int iterations, long minFileSize, long maxFileSize) throws IOException, NoSuchAlgorithmException {
+ for (int i = 0; i < iterations; i++) {
+ BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
+ if (maxFileSize > 1000000000) {
+ int numOfByteArrays = (int) Math.ceil((double) maxFileSize / 1000000000);
+ for (int len = 0; len < numOfByteArrays; len++) {
+ int newMaxFileSize = (int) maxFileSize / numOfByteArrays;
+ int newMinFileSize = 0;
+ if (minFileSize != 0) {
+ newMinFileSize = (int) minFileSize / numOfByteArrays;
+ }
+ byte[] randomBytes = new byte[new Random().nextInt(newMaxFileSize - newMinFileSize) + newMinFileSize];
+ SecureRandom.getInstanceStrong().nextBytes(randomBytes);
+ bufferedOutputStream.write(randomBytes);
+ }
+ } else {
+ byte[] randomBytes = new byte[new Random().nextInt((int) maxFileSize - (int) minFileSize) + (int) minFileSize];
+ SecureRandom.getInstanceStrong().nextBytes(randomBytes);
+ bufferedOutputStream.write(randomBytes);
+ }
+ bufferedOutputStream.flush();
+ bufferedOutputStream.close();
+ }
+
+ return file.delete();
+ }
+
+ /**
+ * Overwrites the file {@param iterations} times line by line with random bytes and delete it
+ *
+ * @see SecureDelete#deleteFileLineByLine(File, int)
+ */
+ public static boolean deleteFileLineByLine(String filename, int iterations) throws NoSuchAlgorithmException, IOException {
+ return deleteFileLineByLine(new File(filename), iterations);
+ }
+
+ /**
+ * Overwrites the file {@param iterations} times line by line with random bytes and delete it
+ *
+ * @param file that should be deleted
+ * @param iterations how many times the file should be overwritten before it gets deleted
+ * @return if the file could be deleted
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ */
+ public static boolean deleteFileLineByLine(File file, int iterations) throws NoSuchAlgorithmException, IOException {
+ long fileLength = file.length() + 1 ;
+ for (int i=0; i 1000000000) {
+ int numOfByteArrays = (int) Math.ceil((double) fileLength / 1000000000);
+ for (int len=0; lenOverwrites the file {@param iterations} times line by line with random bytes (minimal size {@param minFileSize}; maximal size {@param maxFileSize}) and delete it
+ */
+ public static boolean deleteFileLineByLine(String filename, int iterations, long minFileSize, long maxFileSize) throws NoSuchAlgorithmException, IOException {
+ return deleteFileLineByLine(new File(filename), iterations, minFileSize, maxFileSize);
+ }
+
+ /**
+ * Overwrites the file {@param iterations} times line by line with random bytes (minimal size {@param minFileSize}; maximal size {@param maxFileSize}) and delete it
+ *
+ * @param file that should be deleted
+ * @param iterations how many times the file should be overwritten before it gets deleted
+ * @param minFileSize is the minimal file size for every {@param iterations}
+ * @param maxFileSize is the maximal file size for every {@param iterations}
+ * @return if the file could be deleted
+ * @throws IOException
+ * @throws NoSuchAlgorithmException
+ */
+ public static boolean deleteFileLineByLine(File file, int iterations, long minFileSize, long maxFileSize) throws NoSuchAlgorithmException, IOException {
+ for (int i=0; i 1000000000) {
+ int numOfByteArrays = (int) Math.ceil((double) maxFileSize / 1000000000);
+ for (int len=0; lenChecks if any character in {@param characters} appears in {@param string}
+ *
+ * @param characters that should be searched in {@param string}
+ * @param string that should be searched for the characters
+ * @return if any character in {@param characters} appears in {@param string}
+ */
+ public static boolean hasAnyCharacter(CharSequence characters, String string) {
+ for (char c: characters.toString().toCharArray()) {
+ if (string.indexOf(c) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/org/blueshard/cryptogx/resources/addSettingsGUI.fxml b/src/org/blueshard/cryptogx/resources/addSettingsGUI.fxml
new file mode 100644
index 0000000..5d8b7d4
--- /dev/null
+++ b/src/org/blueshard/cryptogx/resources/addSettingsGUI.fxml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/blueshard/cryptogx/resources/close.png b/src/org/blueshard/cryptogx/resources/close.png
new file mode 100644
index 0000000000000000000000000000000000000000..1113eafd348d9b058f08e335d86fcea4d48e3946
GIT binary patch
literal 14702
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%0AN=LaQB=3_H?
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