) 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 0000000..1113eaf
Binary files /dev/null and b/src/org/blueshard/cryptogx/resources/close.png differ
diff --git a/src/org/blueshard/cryptogx/resources/cryptoGX.png b/src/org/blueshard/cryptogx/resources/cryptoGX.png
new file mode 100644
index 0000000..f84ad55
Binary files /dev/null and b/src/org/blueshard/cryptogx/resources/cryptoGX.png differ
diff --git a/src/org/blueshard/cryptogx/resources/exportSettingsGUI.fxml b/src/org/blueshard/cryptogx/resources/exportSettingsGUI.fxml
new file mode 100644
index 0000000..f40bf75
--- /dev/null
+++ b/src/org/blueshard/cryptogx/resources/exportSettingsGUI.fxml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/blueshard/cryptogx/resources/loadSettingsGUI.fxml b/src/org/blueshard/cryptogx/resources/loadSettingsGUI.fxml
new file mode 100644
index 0000000..df7f741
--- /dev/null
+++ b/src/org/blueshard/cryptogx/resources/loadSettingsGUI.fxml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/blueshard/cryptogx/resources/loading.gif b/src/org/blueshard/cryptogx/resources/loading.gif
new file mode 100644
index 0000000..a3ba16a
Binary files /dev/null and b/src/org/blueshard/cryptogx/resources/loading.gif differ
diff --git a/src/org/blueshard/cryptogx/resources/mainGUI.fxml b/src/org/blueshard/cryptogx/resources/mainGUI.fxml
new file mode 100644
index 0000000..39dbbe8
--- /dev/null
+++ b/src/org/blueshard/cryptogx/resources/mainGUI.fxml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/blueshard/cryptogx/resources/minimize.png b/src/org/blueshard/cryptogx/resources/minimize.png
new file mode 100644
index 0000000..928735e
Binary files /dev/null and b/src/org/blueshard/cryptogx/resources/minimize.png differ