diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..76191f1 --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: org.blueshard.cryptogx.Main + diff --git a/src/org/blueshard/cryptogx/Config.java b/src/org/blueshard/cryptogx/Config.java new file mode 100644 index 0000000..f11f65e --- /dev/null +++ b/src/org/blueshard/cryptogx/Config.java @@ -0,0 +1,789 @@ +package org.blueshard.cryptogx; + +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.stage.*; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.xml.stream.*; +import javax.xml.transform.*; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.*; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +import static org.blueshard.cryptogx.Main.*; + +/** + *

Class for the user configuration / settings

+ */ +public class Config { + + private static double addConfigGUIX, addConfigGUIY; + + private static final HashSet protectedConfigNames = new HashSet<>(Arrays.asList("cryptoGX", "config")); + + /** + *

Shows a GUI where the user can save settings, which can load later

+ * + * @param rootWindow from which this GUI will get called + * @param userSetting + * @throws IOException + */ + public static void addSettingGUI(Window rootWindow, Map userSetting) throws IOException { + Map newSettingItems = new HashMap<>(); + + Stage rootStage = new Stage(); + rootStage.initOwner(rootWindow); + Parent addSettingsRoot = FXMLLoader.load(Config.class.getResource("resources/addSettingsGUI.fxml")); + rootStage.initStyle(StageStyle.UNDECORATED); + rootStage.initModality(Modality.WINDOW_MODAL); + rootStage.setResizable(false); + rootStage.setTitle("cryptoGX"); + Scene scene = new Scene(addSettingsRoot, 320, 605); + + rootStage.setScene(scene); + + scene.setOnMouseDragged(event -> { + rootStage.setX(event.getScreenX() + addConfigGUIX); + rootStage.setY(event.getScreenY() + addConfigGUIY); + }); + scene.setOnMousePressed(event -> { + addConfigGUIX = scene.getX() - event.getSceneX(); + addConfigGUIY = scene.getY() - event.getSceneY(); + }); + + Thread thread = new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Platform.runLater(() -> { + MenuBar menuBar = (MenuBar) addSettingsRoot.lookup("#menuBar"); + menuBar.setOnMouseDragged(event -> { + rootStage.setX(event.getScreenX() + addConfigGUIX); + rootStage.setY(event.getScreenY() + addConfigGUIY); + }); + menuBar.setOnMousePressed(event -> { + addConfigGUIX = menuBar.getLayoutX() - event.getSceneX(); + addConfigGUIY = menuBar.getLayoutY() - event.getSceneY(); + }); + + ImageView closeButton = (ImageView) addSettingsRoot.lookup("#closeButton"); + closeButton.setOnMouseClicked(event -> rootStage.close()); + + TextField settingsNameEntry = (TextField) addSettingsRoot.lookup("#settingsNameEntry"); + + TextField textKeyEntry = (TextField) addSettingsRoot.lookup("#textKeyEntry"); + textKeyEntry.setText(userSetting.get("textKey")); + TextField textSaltEntry = (TextField) addSettingsRoot.lookup("#textSaltEntry"); + textSaltEntry.setText(userSetting.get("textSalt")); + ComboBox textAlgorithmBox = (ComboBox) addSettingsRoot.lookup("#textAlgorithmComboBox"); + textAlgorithmBox.setItems(FXCollections.observableArrayList(textAlgorithms)); + textAlgorithmBox.setValue(userSetting.get("textAlgorithm")); + + TextField fileEnDecryptKeyEntry = (TextField) addSettingsRoot.lookup("#fileEnDecryptKeyEntry"); + fileEnDecryptKeyEntry.setText(userSetting.get("fileEnDecryptKey")); + TextField fileEnDecryptSaltEntry = (TextField) addSettingsRoot.lookup("#fileEnDecryptSaltEntry"); + fileEnDecryptSaltEntry.setText(userSetting.get("fileEnDecryptSalt")); + ComboBox fileEnDecryptAlgorithmBox = (ComboBox) addSettingsRoot.lookup("#fileEnDecryptAlgorithmComboBox"); + fileEnDecryptAlgorithmBox.setItems(FXCollections.observableArrayList(fileEnDecryptAlgorithms)); + fileEnDecryptAlgorithmBox.setValue(userSetting.get("fileEnDecryptAlgorithm")); + + TextField fileDeleteIterationEntry = (TextField) addSettingsRoot.lookup("#fileDeleteIterationsEntry"); + fileDeleteIterationEntry.setText(userSetting.get("fileDeleteIterations")); + fileDeleteIterationEntry.textProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue.matches("[0-9]*")) { + fileDeleteIterationEntry.setText(oldValue); + } + }); + + TextField fileOutputPathEntry = (TextField) addSettingsRoot.lookup("#fileOutputPathEntry"); + fileOutputPathEntry.setText(userSetting.get("fileOutputPath")); + CheckBox removeFromFileBoxCheckBox = (CheckBox) addSettingsRoot.lookup("#removeFromFileBoxCheckBox"); + removeFromFileBoxCheckBox.setSelected(Boolean.parseBoolean(userSetting.get("removeFromFileBox"))); + CheckBox limitNumberOfThreadsCheckBox = (CheckBox) addSettingsRoot.lookup("#limitNumberOfThreadsCheckBox"); + limitNumberOfThreadsCheckBox.setSelected(Boolean.parseBoolean(userSetting.get("limitNumberOfThreads"))); + + PasswordField hiddenPasswordEntry = (PasswordField) addSettingsRoot.lookup("#hiddenPasswordEntry"); + TextField showedPasswordEntry = (TextField) addSettingsRoot.lookup("#showedPasswordEntry"); + CheckBox showPassword = (CheckBox) addSettingsRoot.lookup("#showPassword"); + + showPassword.setOnAction(event -> { + if (showPassword.isSelected()) { + showedPasswordEntry.setText(hiddenPasswordEntry.getText()); + showedPasswordEntry.setVisible(true); + hiddenPasswordEntry.setVisible(false); + } else { + hiddenPasswordEntry.setText(showedPasswordEntry.getText()); + hiddenPasswordEntry.setVisible(true); + showedPasswordEntry.setVisible(false); + } + }); + CheckBox encryptSettings = (CheckBox) addSettingsRoot.lookup("#encryptSettings"); + encryptSettings.setOnAction(event -> { + if (encryptSettings.isSelected()) { + hiddenPasswordEntry.setDisable(false); + showedPasswordEntry.setDisable(false); + showPassword.setDisable(false); + } else { + hiddenPasswordEntry.setDisable(true); + showedPasswordEntry.setDisable(true); + showPassword.setDisable(true); + } + }); + Button saveButton = (Button) addSettingsRoot.lookup("#saveButton"); + saveButton.setOnAction(event -> { + if (settingsNameEntry.getText().trim().isEmpty()) { + warningAlert("Add a name for the setting"); + } else if (protectedConfigNames.contains(settingsNameEntry.getText())) { + warningAlert("Please choose another name for this setting"); + } else if (encryptSettings.isSelected()){ + try { + EnDecrypt.AES encrypt; + if (!hiddenPasswordEntry.isDisabled()) { + encrypt = new EnDecrypt.AES(hiddenPasswordEntry.getText(), new byte[16]); + newSettingItems.put("encryptHash", encrypt.encrypt(hiddenPasswordEntry.getText())); + } else { + encrypt = new EnDecrypt.AES(showedPasswordEntry.getText(), new byte[16]); + newSettingItems.put("encryptHash", encrypt.encrypt(showedPasswordEntry.getText())); + } + + newSettingItems.put("textKey", encrypt.encrypt(textKeyEntry.getText())); + newSettingItems.put("textSalt", encrypt.encrypt(textSaltEntry.getText())); + newSettingItems.put("textAlgorithm", encrypt.encrypt(textAlgorithmBox.getSelectionModel().getSelectedItem().toString())); + + newSettingItems.put("fileEnDecryptKey", encrypt.encrypt(fileEnDecryptKeyEntry.getText())); + newSettingItems.put("fileEnDecryptSalt", encrypt.encrypt(fileEnDecryptSaltEntry.getText())); + newSettingItems.put("fileEnDecryptAlgorithm", encrypt.encrypt(fileEnDecryptAlgorithmBox.getSelectionModel().getSelectedItem().toString())); + + newSettingItems.put("fileDeleteIterations", encrypt.encrypt(fileDeleteIterationEntry.getText())); + + newSettingItems.put("fileOutputPath", encrypt.encrypt(fileOutputPathEntry.getText())); + newSettingItems.put("removeFromFileBox", encrypt.encrypt(String.valueOf(removeFromFileBoxCheckBox.isSelected()))); + newSettingItems.put("limitNumberOfThreads", encrypt.encrypt(String.valueOf(limitNumberOfThreadsCheckBox.isSelected()))); + + addSetting(settingsNameEntry.getText(), newSettingItems); + + rootStage.close(); + } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e) { + e.printStackTrace(); + } + } else { + newSettingItems.put("encryptHash", ""); + + newSettingItems.put("textKey", textKeyEntry.getText()); + newSettingItems.put("textSalt", textSaltEntry.getText()); + newSettingItems.put("textAlgorithm", textAlgorithmBox.getSelectionModel().getSelectedItem().toString()); + + newSettingItems.put("fileEnDecryptKey", fileEnDecryptKeyEntry.getText()); + newSettingItems.put("fileEnDecryptSalt", fileEnDecryptSaltEntry.getText()); + newSettingItems.put("fileEnDecryptAlgorithm", fileEnDecryptAlgorithmBox.getSelectionModel().getSelectedItem().toString()); + + newSettingItems.put("fileDeleteIterations", fileDeleteIterationEntry.getText()); + + newSettingItems.put("fileOutputPath", fileOutputPathEntry.getText()); + newSettingItems.put("removeFromFileBox", String.valueOf(removeFromFileBoxCheckBox.isSelected())); + newSettingItems.put("limitNumberOfThreads", String.valueOf(limitNumberOfThreadsCheckBox.isSelected())); + + addSetting(settingsNameEntry.getText(), newSettingItems); + + rootStage.close(); + } + }); + }); + }); + + thread.start(); + + rootStage.showAndWait(); + } + + /** + *

Shows a GUI where the user can export settings to a extra file

+ * + * @param rootWindow from which this GUI will get called + * @throws IOException + */ + public static void exportSettingsGUI(Window rootWindow) throws IOException { + Stage rootStage = new Stage(); + rootStage.initOwner(rootWindow); + Parent exportSettingsRoot = FXMLLoader.load(Config.class.getResource("resources/exportSettingsGUI.fxml")); + rootStage.initStyle(StageStyle.UNDECORATED); + rootStage.initModality(Modality.WINDOW_MODAL); + rootStage.setResizable(false); + rootStage.setTitle("cryptoGX"); + Scene scene = new Scene(exportSettingsRoot, 254, 253); + + rootStage.setScene(scene); + + scene.setOnMouseDragged(event -> { + rootStage.setX(event.getScreenX() + addConfigGUIX); + rootStage.setY(event.getScreenY() + addConfigGUIY); + }); + scene.setOnMousePressed(event -> { + addConfigGUIX = scene.getX() - event.getSceneX(); + addConfigGUIY = scene.getY() - event.getSceneY(); + }); + + Thread thread = new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + MenuBar menuBar = (MenuBar) exportSettingsRoot.lookup("#menuBar"); + menuBar.setOnMouseDragged(event -> { + rootStage.setX(event.getScreenX() + addConfigGUIX); + rootStage.setY(event.getScreenY() + addConfigGUIY); + }); + menuBar.setOnMousePressed(event -> { + addConfigGUIX = menuBar.getLayoutX() - event.getSceneX(); + addConfigGUIY = menuBar.getLayoutY() - event.getSceneY(); + }); + ImageView closeButton = (ImageView) exportSettingsRoot.lookup("#closeButton"); + closeButton.setOnMouseClicked(event -> rootStage.close()); + + VBox settingsBox = (VBox) exportSettingsRoot.lookup("#settingsBox"); + Platform.runLater(() -> readUserSettings().keySet().forEach(s -> { + CheckBox newCheckBox = new CheckBox(); + newCheckBox.setText(s); + settingsBox.getChildren().add(newCheckBox); + })); + + Button exportButton = (Button) exportSettingsRoot.lookup("#exportButton"); + exportButton.setOnAction(event -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Export settings"); + fileChooser.setInitialFileName("settings.config"); + fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Config files", "*.config", "*.xml"), + new FileChooser.ExtensionFilter("All files", "*.*")); + File file = fileChooser.showSaveDialog(exportSettingsRoot.getScene().getWindow()); + if (file != null) { + TreeMap> writeInfos = new TreeMap<>(); + TreeMap> settings = readUserSettings(); + for (int i=0; iShows a GUI where the user can load saved settings

+ * + * @param rootWindow from which this GUI will get called + * @return the settings that the user has chosen + * @throws IOException + */ + public static TreeMap> loadSettingsGUI(Window rootWindow) throws IOException { + Button[] outerLoadButton = new Button[1]; + HashMap setting = new HashMap<>(); + TreeMap> settingItems = readUserSettings(); + TreeMap> returnItems = new TreeMap<>(); + + Stage rootStage = new Stage(); + rootStage.initOwner(rootWindow); + AnchorPane loadSettingsRoot = FXMLLoader.load(Config.class.getResource("resources/loadSettingsGUI.fxml")); + rootStage.initStyle(StageStyle.UNDECORATED); + rootStage.initModality(Modality.WINDOW_MODAL); + rootStage.setResizable(false); + rootStage.setTitle("cryptoGX"); + rootStage.getIcons().add(new Image(Config.class.getResource("resources/cryptoGX.png").toExternalForm())); + Scene scene = new Scene(loadSettingsRoot, 242, 235); + + scene.setOnMouseDragged(event -> { + rootStage.setX(event.getScreenX() + addConfigGUIX); + rootStage.setY(event.getScreenY() + addConfigGUIY); + }); + scene.setOnMousePressed(event -> { + addConfigGUIX = scene.getX() - event.getSceneX(); + addConfigGUIY = scene.getY() - event.getSceneY(); + }); + + scene.setOnKeyReleased(event -> { + if (event.getCode() == KeyCode.ENTER) { + outerLoadButton[0].fire(); + } + }); + + Thread thread = new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Platform.runLater(() -> { + MenuBar menuBar = (MenuBar) loadSettingsRoot.lookup("#menuBar"); + menuBar.setOnMouseDragged(event -> { + rootStage.setX(event.getScreenX() + addConfigGUIX); + rootStage.setY(event.getScreenY() + addConfigGUIY); + }); + menuBar.setOnMousePressed(event -> { + addConfigGUIX = menuBar.getLayoutX() - event.getSceneX(); + addConfigGUIY = menuBar.getLayoutY() - event.getSceneY(); + }); + + ImageView closeButton = (ImageView) loadSettingsRoot.lookup("#closeButton"); + if (settingItems.isEmpty()) { + rootStage.close(); + } + + closeButton.setOnMouseClicked(event -> { + setting.put("encryptHash", configDefaultEncryptHash); + + setting.put("textKey", configDefaultTextKey); + setting.put("textSalt", configDefaultTextSalt); + setting.put("textAlgorithm", configDefaultTextAlgorithm); + + setting.put("fileEnDecryptKey", configDefaultFileEnDecryptKey); + setting.put("fileEnDecryptSalt", configDefaultFileEnDecryptSalt); + setting.put("fileEnDecryptAlgorithm", configDefaultFileEnDecryptAlgorithm); + + setting.put("fileDeleteIterations", String.valueOf(configDefaultFileDeleteIterations)); + + setting.put("fileOutputPath", configDefaultFileOutputPath); + setting.put("removeFromFileBox", String.valueOf(configDefaultRemoveFileFromFileBox)); + setting.put("limitNumberOfThreads", String.valueOf(configDefaultLimitNumberOfThreads)); + + returnItems.put("default", setting); + + rootStage.close(); + }); + + PasswordField keyHideEntry = (PasswordField) loadSettingsRoot.lookup("#passwordEntryHide"); + TextField keyShowEntry = (TextField) loadSettingsRoot.lookup("#passwordEntryShow"); + + CheckBox showPassword = (CheckBox) loadSettingsRoot.lookup("#showPassword"); + showPassword.setOnAction(event -> { + if (showPassword.isSelected()) { + keyShowEntry.setText(keyHideEntry.getText()); + keyShowEntry.setVisible(true); + keyHideEntry.setVisible(false); + } else { + keyHideEntry.setText(keyShowEntry.getText()); + keyHideEntry.setVisible(true); + keyShowEntry.setVisible(false); + } + }); + + ComboBox settingsBox = (ComboBox) loadSettingsRoot.lookup("#settingsBox"); + settingsBox.setItems(FXCollections.observableArrayList(settingItems.keySet())); + settingsBox.setValue(settingItems.firstKey()); + if (settingItems.firstEntry().getValue().get("encryptHash").trim().isEmpty()) { + keyHideEntry.clear(); + keyHideEntry.setDisable(true); + keyShowEntry.setDisable(true); + showPassword.setDisable(true); + } + settingsBox.setOnAction(event -> { + try { + if (settingItems.get(settingsBox.getSelectionModel().getSelectedItem().toString()).get("encryptHash").trim().isEmpty()) { + keyHideEntry.clear(); + keyHideEntry.setDisable(true); + keyShowEntry.clear(); + keyShowEntry.setDisable(true); + showPassword.setDisable(true); + } else { + keyHideEntry.clear(); + keyHideEntry.setDisable(false); + keyShowEntry.clear(); + keyShowEntry.setDisable(false); + showPassword.setDisable(false); + } + } catch (NullPointerException e) { + //get called when delete button is pressed + } + }); + + Button loadButton = (Button) loadSettingsRoot.lookup("#loadButton"); + loadButton.setOnAction(event -> { + String settingName = settingsBox.getSelectionModel().getSelectedItem().toString(); + Map selectedSetting = settingItems.get(settingName); + if (keyHideEntry.isDisabled() && showPassword.isDisabled() && showPassword.isDisabled()) { + setting.put("encryptHash", ""); + + setting.put("textKey", selectedSetting.get("textKey")); + setting.put("textSalt", selectedSetting.get("textSalt")); + setting.put("textAlgorithm", selectedSetting.get("textAlgorithm")); + + setting.put("fileEnDecryptKey", selectedSetting.get("fileEnDecryptKey")); + setting.put("fileEnDecryptSalt", selectedSetting.get("fileEnDecryptSalt")); + setting.put("fileEnDecryptAlgorithm", selectedSetting.get("fileEnDecryptAlgorithm")); + + setting.put("fileDeleteIterations", selectedSetting.get("fileDeleteIterations")); + + setting.put("fileOutputPath", selectedSetting.get("fileOutputPath")); + setting.put("removeFromFileBox", selectedSetting.get("removeFromFileBox")); + setting.put("limitNumberOfThreads", selectedSetting.get("limitNumberOfThreads")); + + returnItems.put(settingsBox.getSelectionModel().getSelectedItem().toString(), setting); + + rootStage.close(); + } else { + EnDecrypt.AES decryptSetting; + if (keyHideEntry.isVisible()) { + decryptSetting = new EnDecrypt.AES(keyHideEntry.getText(), new byte[16]); + } else { + decryptSetting = new EnDecrypt.AES(keyShowEntry.getText(), new byte[16]); + } + try { + if (keyHideEntry.isVisible() && !decryptSetting.encrypt(keyHideEntry.getText()).equals(settingItems.get(settingsBox.getSelectionModel().getSelectedItem().toString()).get("encryptHash").trim())) { + warningAlert("Wrong key is given"); + } else if (keyShowEntry.isVisible() && !decryptSetting.encrypt(keyShowEntry.getText()).equals(settingItems.get(settingsBox.getSelectionModel().getSelectedItem().toString()).get("encryptHash").trim())) { + warningAlert("Wrong key is given"); + } else { + Map selectedEncryptedSetting = settingItems.get(settingName); + setting.put("textKey", decryptSetting.decrypt(selectedEncryptedSetting.get("textKey"))); + setting.put("textSalt", decryptSetting.decrypt(selectedEncryptedSetting.get("textSalt"))); + setting.put("textAlgorithm", decryptSetting.decrypt(selectedEncryptedSetting.get("textAlgorithm"))); + + setting.put("fileEnDecryptKey", decryptSetting.decrypt(selectedEncryptedSetting.get("fileEnDecryptKey"))); + setting.put("fileEnDecryptSalt", decryptSetting.decrypt(selectedEncryptedSetting.get("fileEnDecryptSalt"))); + setting.put("fileEnDecryptAlgorithm", decryptSetting.decrypt(selectedEncryptedSetting.get("fileEnDecryptAlgorithm"))); + + setting.put("fileDeleteIterations", String.valueOf(Integer.parseInt(decryptSetting.decrypt(selectedEncryptedSetting.get("fileDeleteIterations"))))); + + setting.put("fileOutputPath", decryptSetting.decrypt(selectedEncryptedSetting.get("fileOutputPath"))); + setting.put("removeFromFileBox", decryptSetting.decrypt(selectedEncryptedSetting.get("removeFromFileBox"))); + setting.put("limitNumberOfThreads", decryptSetting.decrypt(selectedEncryptedSetting.get("limitNumberOfThreads"))); + + returnItems.put(settingsBox.getSelectionModel().getSelectedItem().toString(), setting); + + rootStage.close(); + } + } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e) { + e.printStackTrace(); + warningAlert("Wrong key is given"); + } + } + }); + outerLoadButton[0] = loadButton; + + Button deleteButton = (Button) loadSettingsRoot.lookup("#deleteButton"); + deleteButton.setOnAction(event -> { + AtomicReference deleteQuestionX = new AtomicReference<>((double) 0); + AtomicReference deleteQuestionY = new AtomicReference<>((double) 0); + Alert deleteQuestion = new Alert(Alert.AlertType.CONFIRMATION, "Delete " + settingsBox.getSelectionModel().getSelectedItem().toString() + "?", ButtonType.OK, ButtonType.CANCEL); + deleteQuestion.initStyle(StageStyle.UNDECORATED); + deleteQuestion.setTitle("Confirmation"); + ((Stage) deleteQuestion.getDialogPane().getScene().getWindow()).getIcons().add(new Image(Config.class.getResource("resources/cryptoGX.png").toExternalForm())); + + Scene window = deleteQuestion.getDialogPane().getScene(); + + window.setOnMouseDragged(dragEvent -> { + deleteQuestion.setX(dragEvent.getScreenX() + deleteQuestionX.get()); + deleteQuestion.setY(dragEvent.getScreenY() + deleteQuestionY.get()); + }); + window.setOnMousePressed(pressEvent -> { + deleteQuestionX.set(window.getX() - pressEvent.getSceneX()); + deleteQuestionY.set(window.getY() - pressEvent.getSceneY()); + }); + + Optional result = deleteQuestion.showAndWait(); + if (result.get() == ButtonType.OK) { + deleteUserSetting(settingsBox.getSelectionModel().getSelectedItem().toString()); + settingItems.clear(); + settingItems.putAll(readUserSettings()); + if (settingItems.size() == 0) { + for (int i=0; i<100; i++) { + if (config.isFile()) { + if (config.delete()) { + isConfig = false; + rootStage.close(); + break; + } + } + } + rootStage.close(); + return; + } + settingsBox.setItems(FXCollections.observableArrayList(settingItems.keySet())); + settingsBox.setValue(settingItems.firstKey()); + } + }); + }); + }); + + thread.start(); + + rootStage.setScene(scene); + rootStage.showAndWait(); + + return returnItems; + } + + /** + *

Shows a GUI where the user can save the current settings

+ * + * @param settingName name of the new setting + * @param userSetting the current settings + */ + public static void addSetting(String settingName, Map userSetting) { + TreeMap> newConfig = new TreeMap<>(readUserSettings()); + newConfig.put(settingName, userSetting); + writeConfig(newConfig); + } + + /** + *

Shows a GUI where the user can save the current settings

+ * + * @param settingName name of the new setting + * @param userSetting the current settings + * @param encryptPassword to encrypt the settings + */ + public static void addSetting(String settingName, Map userSetting, String encryptPassword) { + TreeMap> newConfig = new TreeMap<>(readUserSettings()); + newConfig.put(settingName, userSetting); + writeConfig(newConfig, Collections.singletonMap(settingName, encryptPassword)); + } + + /** + *

Deletes a saved setting

+ * + * @param name of the setting + * @return if the setting could be found + */ + public static boolean deleteUserSetting(String name) { + TreeMap> newSetting = new TreeMap<>(); + TreeMap> oldSetting = readUserSettings(); + boolean found = false; + + for (Map.Entry> entry: oldSetting.entrySet()) { + if (!entry.getKey().equals(name)) { + newSetting.put(entry.getKey(), entry.getValue()); + } else { + found = true; + } + } + writeConfig(newSetting); + return found; + } + + public static TreeMap> readUserSettings() { + return readUserSettings(config); + } + + /** + * @see Config#readUserSettings(String) + */ + public static TreeMap> readUserSettings(File file) { + TreeMap> rootInfos = new TreeMap<>(); + try { + XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory(); + XMLStreamReader xmlStreamReader; + try { + xmlStreamReader = xmlInputFactory.createXMLStreamReader(new FileInputStream(file)); + } catch (FileNotFoundException e) { + return rootInfos; + } + + HashMap infos = new HashMap<>(); + + String infoName = null; + StringBuilder infoCharacters = new StringBuilder(); + String rootName = null; + + while (xmlStreamReader.hasNext()) { + + int eventType = xmlStreamReader.next(); + + switch (eventType) { + case XMLStreamReader.START_ELEMENT: + String startTag = xmlStreamReader.getLocalName().trim(); + if (startTag != null) { + if (protectedConfigNames.contains(startTag)) { + continue; + } else if (rootName == null) { + rootName = startTag; + } else { + infoName = startTag; + } + } + break; + case XMLStreamReader.CHARACTERS: + if (infoName != null) { + if (!xmlStreamReader.getText().trim().equals("")) { + infoCharacters.append(xmlStreamReader.getText()); + } + } + break; + case XMLStreamReader.END_ELEMENT: + String endTag = xmlStreamReader.getLocalName().trim(); + if (endTag != null) { + if (protectedConfigNames.contains(endTag)) { + continue; + } else if (endTag.equals(rootName)) { + rootInfos.put(rootName, infos); + rootName = null; + infos = new HashMap<>(); + infoCharacters = new StringBuilder(); + } else { + infos.put(infoName, infoCharacters.toString()); + infoName = null; + infoCharacters = new StringBuilder(); + } + } + break; + } + } + xmlStreamReader.close(); + } catch (XMLStreamException e) { + e.printStackTrace(); + } + System.out.println(rootInfos); + + return rootInfos; + } + + /** + *

Shows a GUI where the user can choose and load saved settings

+ * + * @param filename of the file with the settings + * @return the setting that the user has chosen + */ + public static TreeMap> readUserSettings(String filename) { + return readUserSettings(new File(filename)); + } + + /** + *

Writes settings (could be more than one) to the pre-defined config file

+ * + * @see Config#writeConfig(File, TreeMap, Map) + */ + public static void writeConfig(TreeMap> userSettings) { + writeConfig(config, userSettings, null); + } + + /** + *

Writes settings (could be more than one) to the pre-defined config file

+ * + * @see Config#writeConfig(File, TreeMap, Map) + */ + public static void writeConfig(TreeMap> userSettings, Map encryptedSettings) { + writeConfig(config, userSettings, encryptedSettings); + } + + /** + *

Writes settings (could be more than one) to the pre-defined config file

+ * + * @see Config#writeConfig(String, TreeMap, Map) + */ + public static void writeConfig(String filename, TreeMap> userSettings) { + writeConfig(filename, userSettings, null); + } + + /** + *

Writes settings (could be more than one) to the pre-defined config file

+ * + * @see Config#writeConfig(File, TreeMap, Map) + */ + public static void writeConfig(File file, TreeMap> userSettings) { + writeConfig(file, userSettings, null); + } + + /** + *

Writes settings (could be more than one) to a file

+ * + * @see Config#writeConfig(String, TreeMap, Map) + */ + public static void writeConfig(String filename, TreeMap> userSettings, Map encryptedSettings) { + writeConfig(new File(filename), userSettings, encryptedSettings); + } + + /** + *

Writes settings (could be more than one) to a file

+ * + * @param file where the settings should be written in + * @param userSettings of the user + * @param encryptedSettings says which settings from {@param userSettings} should be encrypted with a password + */ + public static void writeConfig(File file, TreeMap> userSettings, Map encryptedSettings) { + EnDecrypt.AES encryptSetting; + StringWriter stringWriter = new StringWriter(); + + if (encryptedSettings == null) { + encryptedSettings = new HashMap<>(); + } + + try { + XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); + XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(stringWriter); + + xmlStreamWriter.writeStartDocument(); + xmlStreamWriter.writeStartElement("cryptoGX"); + for (Map.Entry> settingElement: userSettings.entrySet()) { + xmlStreamWriter.writeStartElement(settingElement.getKey()); + if (encryptedSettings.containsKey(settingElement.getKey())) { + encryptSetting = new EnDecrypt.AES(settingElement.getKey(), new byte[16]); + for (Map.Entry entry: settingElement.getValue().entrySet()) { + xmlStreamWriter.writeStartElement(entry.getKey()); + xmlStreamWriter.writeCharacters(encryptSetting.encrypt(entry.getValue())); + xmlStreamWriter.writeEndElement(); + } + } else { + for (Map.Entry entry: settingElement.getValue().entrySet()) { + xmlStreamWriter.writeStartElement(entry.getKey()); + xmlStreamWriter.writeCharacters(entry.getValue()); + xmlStreamWriter.writeEndElement(); + } + } + xmlStreamWriter.writeEndElement(); + } + xmlStreamWriter.writeEndElement(); + xmlStreamWriter.writeEndDocument(); + + //prettify + + Source xmlInput = new StreamSource(new StringReader(stringWriter.toString())); + StringWriter prettifyStringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(prettifyStringWriter); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", 2); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(xmlInput, xmlOutput); + + BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); + for (String s: prettifyStringWriter.getBuffer().toString().split(System.lineSeparator())) { + bufferedWriter.write(s); + bufferedWriter.newLine(); + } + bufferedWriter.close(); + } catch (XMLStreamException | IOException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException | TransformerException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/org/blueshard/cryptogx/Controller.java b/src/org/blueshard/cryptogx/Controller.java new file mode 100644 index 0000000..2b6c493 --- /dev/null +++ b/src/org/blueshard/cryptogx/Controller.java @@ -0,0 +1,1581 @@ +package org.blueshard.cryptogx; + +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.event.Event; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuBar; +import javafx.scene.control.MenuItem; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.*; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.stage.*; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.*; +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.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.blueshard.cryptogx.Config.*; +import static org.blueshard.cryptogx.Main.*; + +public class Controller implements Initializable { + + private Event fileEnDecryptLabelEvent; + + private double menubarX, menubarY; + private boolean textLoading = false; + private boolean fileEnDecryptLoading = false; + private boolean fileDeleteLoading = false; + private AtomicInteger textThreads = new AtomicInteger(0); + private AtomicInteger totalThreads = new AtomicInteger(0); + private int tooltipShow = 15; + private final int DATAFILEURL = 2; + private final int FILEFILEURL = 1; + private final int NONSPECIFICFILEURL = 0; + private final int IMAGE = 78345; + private final int FILE = 23902; + private final int UNKNOWN = 12345; + private final KeyCombination paste = new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_DOWN); + private final Image loadingImage = new Image(getClass().getResource("resources/loading.gif").toExternalForm()); + + private HashMap currentConfigSettings = new HashMap<>(); + + private HashMap> enDecryptInputOutputFiles = new HashMap<>(); + private HashMap> enDecryptInputOutputInternetFiles = new HashMap<>(); + private HashMap enDecryptInputOutputClipboardImages = new HashMap<>(); + private HashMap deleteInputFiles = new HashMap<>(); + private List fileEnDecryptThreads = Collections.synchronizedList(new ArrayList<>()); + private List fileDeleteThreads = Collections.synchronizedList(new ArrayList<>()); + + private ContextMenu fileEnDecryptInputContextMenu = new ContextMenu(); + private ContextMenu fileDeleteInputContextMenu = new ContextMenu(); + private Label choosedLabel = null; + private String choosedLabelType = null; + private MenuItem fileOutputFileChangeDest = new MenuItem("Change output file"); + private MenuItem getChoosedLabelInputFileFolder = new MenuItem("Open source directory"); + private MenuItem getChoosedLabelOutputFileFolder = new MenuItem("Open source directory"); + private Tooltip tooltip = new Tooltip(); + + public AnchorPane rootWindow; + + public Button fileEnDecryptFilesButton; + public Button fileDecrypt; + public Button fileEncrypt; + public Button fileEnDecryptStop; + + public ComboBox textAlgorithmBox; + public ComboBox fileEnDecryptAlgorithmBox; + + public ImageView minimizeWindow; + public ImageView closeWindow; + public ImageView textLoadingImage; + public ImageView fileEnDecryptLoadingImage; + public ImageView fileDeleteLoadingImage; + + public Menu settingsMenu; + + public MenuBar menubar; + + public MenuItem setDefaultOutputPath; + public MenuItem saveSettings; + public MenuItem loadSettings; + public MenuItem exportSettings; + public MenuItem importSettings; + + public RadioMenuItem removeFileFromFileBox; + public RadioMenuItem limitNumberOfThreads; + + public ScrollPane fileEnDecryptInputScroll; + + public TextArea textDecryptedEntry; + public TextArea textEncryptedEntry; + + public TextField textKeyEntry; + public TextField textSaltEntry; + public TextField fileEnDecryptKeyEntry; + public TextField fileDecryptOutputFile; + public TextField fileEncryptOutputFile; + public TextField fileEnDecryptSaltEntry; + public TextField fileDeleteIterationsEntry; + + public VBox fileEnDecryptInputFiles; + public VBox fileDeleteInputFiles; + + //-----general-----// + + /** + *

Shows a tooltip when the user type in some text in a text field, text area, etc. and the mouse is over this entry

+ * + * @param event from which this method is called + */ + public void keyTypedTooltip(KeyEvent event) { + String id = null; + String text = ""; + try { + id = ((TextField) event.getSource()).getId(); + text = ((TextField) event.getSource()).getText() + event.getCharacter(); + tooltip.setText(text); + } catch (ClassCastException e) { + tooltip.setText(((TextArea) event.getSource()).getText() + event.getCharacter()); + } + if (id != null) { + switch (id) { + case ("textKeyEntry"): + currentConfigSettings.replace("textKey", text); + break; + case ("textSaltEntry"): + currentConfigSettings.replace("textSalt", text); + break; + case ("fileEnDecryptKeyEntry"): + currentConfigSettings.replace("fileEnDecryptKey", text); + break; + case ("fileEnDecryptSaltEntry"): + currentConfigSettings.replace("fileEnDecryptSalt", text); + break; + case ("fileDeleteIterationsEntry"): + currentConfigSettings.replace("fileDeleteIterations", String.valueOf(Integer.parseInt(text))); + break; + } + } + } + + /** + *

Shows a tooltip when to mouse is over a text field, text area, etc.

+ * + * @param event from which this method is called + */ + public void mouseOverEntryTooltip(MouseEvent event) { + try { + tooltip.setText(((TextField) event.getSource()).getText()); + } catch (ClassCastException e) { + try { + tooltip.setText(((TextArea) event.getSource()).getText()); + } catch (ClassCastException ex) { + tooltip.setText(((Label) event.getSource()).getText()); + } + } + if (!tooltip.getText().trim().isEmpty()) { + tooltip.show(rootWindow.getScene().getWindow(), event.getScreenX(), event.getScreenY() + tooltipShow); + } + } + + /** + *

Hides the tooltip if the mouse exit a text field, text area, etc.

+ */ + public void mouseExitEntryTooltip() { + tooltip.hide(); + } + + //-----root-----// + + /** + *

Closed the application. + * Get called if red close button is pressed

+ */ + public void closeApplication() { + Stage rootStage = (Stage) rootWindow.getScene().getWindow(); + rootStage.close(); + System.exit(0); + } + + /** + *

Hides the application. + * Get called if the green minimize button is pressed

+ */ + public void minimizeApplication() { + Stage rootStage = (Stage) rootWindow.getScene().getWindow(); + rootStage.setIconified(true); + } + + //-----text-----// + + /** + *

Encrypt text in {@link Controller#textDecryptedEntry}. + * Get called if the text 'Encrypt' button is pressed

+ */ + public void textEncryptButton() { + final byte[] salt; + if (!textSaltEntry.getText().isEmpty()) { + salt = textSaltEntry.getText().getBytes(StandardCharsets.UTF_8); + } else { + salt = new byte[16]; + } + if (!textLoading) { + textLoadingImage.setImage(loadingImage); + } + Thread textEncrypt = new Thread(() -> { + textThreads.getAndIncrement(); + if (limitNumberOfThreads.isSelected()) { + while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().stop(); + } + } + } + totalThreads.getAndIncrement(); + EnDecrypt.AES encrypt = new EnDecrypt.AES(textKeyEntry.getText(), salt); + try { + String encryptedText = encrypt.encrypt(textDecryptedEntry.getText()); + Platform.runLater(() -> { + textEncryptedEntry.setText(encryptedText); + }); + } catch (NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException e) { + e.printStackTrace(); + } catch (IllegalArgumentException | IllegalBlockSizeException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("Wrong text for encryption is given", e.getMessage())); + } + if ((textThreads.get() - 1) <= 0) { + textLoadingImage.setImage(null); + textLoading = false; + } + textThreads.getAndDecrement(); + totalThreads.getAndDecrement(); + }); + textEncrypt.setDaemon(false); + textEncrypt.start(); + textLoading = true; + } + + /** + *

Decrypt text in {@link Controller#textEncryptedEntry}. + * Get called if the text 'Decrypt' button is pressed

+ */ + public void textDecryptButton() { + final byte[] salt; + if (!textSaltEntry.getText().isEmpty()) { + salt = textSaltEntry.getText().getBytes(StandardCharsets.UTF_8); + } else { + salt = new byte[16]; + } + if (!textLoading) { + textLoadingImage.setImage(loadingImage); + } + Thread textDecrypt = new Thread(() -> { + textThreads.getAndIncrement(); + if (limitNumberOfThreads.isSelected()) { + while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().stop(); + } + } + } + totalThreads.getAndIncrement(); + EnDecrypt.AES decrypt = new EnDecrypt.AES(textKeyEntry.getText(), salt); + try { + String DecryptedText = decrypt.decrypt(textEncryptedEntry.getText()); + Platform.runLater(() -> { + textDecryptedEntry.setText(DecryptedText); + }); + } catch (NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("Wrong key and / or salt is given", e.getMessage())); + } catch (IllegalArgumentException | IllegalBlockSizeException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("Wrong text for decryption is given", e.getMessage())); + } + if ((textThreads.get() - 1) <= 0) { + textLoading = false; + Platform.runLater(() -> textLoadingImage.setImage(null)); + } + textThreads.getAndDecrement(); + totalThreads.getAndDecrement(); + }); + textDecrypt.setDaemon(false); + textDecrypt.start(); + textLoading = true; + } + + //-----fileEnDecrypt-----// + + /** + *

Synchronized method to get the list of threads which en- / decrypt files

+ * + * @return list of en- / decryption threads + */ + private synchronized List getFileEnDecryptThreads() { + return fileEnDecryptThreads; + } + + /** + *

Synchronized method to get the number of threads which en- / decrypt files

+ * + * @return number of en- / decryption threads + */ + private synchronized int getFileEnDecryptThreadsSize() { + return fileEnDecryptThreads.size(); + } + + /** + *

Synchronized method to add a thread to the file en- / decryption list of current running file en- / decryption threads

+ * + * @param thread that should be added + */ + private synchronized void addFileEnDecryptThread(Thread thread) { + fileEnDecryptThreads.add(thread); + } + + /** + *

Synchronized method to remove a thread from the file en- / decryption list of current running file en- / decryption threads

+ * + * @param thread that should be removed + */ + private synchronized void removeFileEnDecryptThread(Thread thread) { + fileEnDecryptThreads.remove(thread); + } + + /** + *

Adds a file for en- / decryption

+ * + * @param file that should be added + */ + private void fileEnDecryptAddFile(File file) { + for (Label l: enDecryptInputOutputFiles.keySet()) { + if (l.getText().equals(file.getAbsolutePath())) { + return; + } + } + Label newLabel = new Label(file.getAbsolutePath()); + newLabel.setOnKeyTyped(this::keyTypedTooltip); + newLabel.setOnMouseMoved(this::mouseOverEntryTooltip); + newLabel.setOnMouseExited(event -> mouseExitEntryTooltip()); + newLabel.setOnMouseClicked(event -> { + fileEnDecryptSelected(newLabel); + fileOutputFilesChangeText(newLabel, null, null); + fileEnDecryptLabelEvent = event; + }); + newLabel.setContextMenu(fileEnDecryptInputContextMenu); + String fileAbsolutePath = file.getAbsolutePath(); + String fileName = file.getName(); + + File encryptFile; + File decryptFile; + ArrayList inputOutputList = new ArrayList<>(); + if (currentConfigSettings.get("fileOutputPath").trim().isEmpty()) { + encryptFile = new File(fileAbsolutePath + ".cryptoGX"); + while (encryptFile.isFile()) { + encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX"); + } + if (fileAbsolutePath.endsWith(".cryptoGX")) { + decryptFile = new File(fileAbsolutePath.substring(0, fileAbsolutePath.length() - 9)); + if (decryptFile.isFile()) { + while (decryptFile.isFile()) { + decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX"); + } + } + } else { + decryptFile = new File(fileAbsolutePath + ".cryptoGX"); + while (decryptFile.isFile()) { + decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX"); + } + } + } else { + encryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + fileName + ".cryptoGX"); + while (encryptFile.isFile()) { + encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX"); + } + if (fileAbsolutePath.endsWith(".cryptoGX")) { + decryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + fileName.substring(0, fileAbsolutePath.length() - 9)); + if (decryptFile.isFile()) { + while (decryptFile.isFile()) { + decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX"); + } + } + } else { + decryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + fileName + ".cryptoGX"); + while (decryptFile.isFile()) { + decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX"); + } + } + } + System.out.println(encryptFile.getAbsolutePath() + ";" + decryptFile.getAbsolutePath()); + inputOutputList.add(0, encryptFile); + inputOutputList.add(1, decryptFile); + fileEnDecryptInputFiles.getChildren().add(newLabel); + enDecryptInputOutputFiles.put(newLabel, inputOutputList); + } + + /** + *

Adds an file from the internet for en- / decryption

+ * + * @param url of the file + * @param fileType of the file + * @throws URISyntaxException + */ + private void fileEnDecryptAddInternetFile(String url, int fileType) throws URISyntaxException { + String filename; + if (fileType == FILEFILEURL) { + filename = url.substring(url.lastIndexOf("/") + 1); + } else if (fileType == DATAFILEURL) { + filename = url.substring(5, url.indexOf("/")) + "." + url.substring(url.indexOf("/") + 1, url.indexOf(";")); + } else if (fileType == NONSPECIFICFILEURL) { + filename = "unknown" + System.nanoTime(); + } else { + warningAlert("Cannot read given url '" + url + "'"); + return; + } + for (Label l: enDecryptInputOutputInternetFiles.keySet()) { + if (l.getText().equals(filename)) { + return; + } + } + Label newLabel = new Label(filename); + newLabel.setOnKeyTyped(this::keyTypedTooltip); + newLabel.setOnMouseMoved(this::mouseOverEntryTooltip); + newLabel.setOnMouseExited(event -> mouseExitEntryTooltip()); + newLabel.setOnMouseClicked(event -> { + fileEnDecryptSelected(newLabel); + fileOutputFilesChangeText(newLabel, null, null); + fileEnDecryptLabelEvent = event; + }); + newLabel.setContextMenu(fileEnDecryptInputContextMenu); + + File encryptFile; + File decryptFile; + ArrayList fileSpecs = new ArrayList<>(); + ArrayList inputOutputFiles = new ArrayList<>(); + fileSpecs.add(0, fileType); + fileSpecs.add(1, url); + String currentDir = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + + if (currentConfigSettings.get("fileOutputPath").trim().isEmpty()) { + encryptFile = new File(currentDir + "/" + filename + ".cryptoGX"); + while (encryptFile.isFile()) { + encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX"); + } + if (url.endsWith(".cryptoGX") && filename.endsWith(".cryptoGX")) { + decryptFile = new File(currentDir + "/" + filename.substring(0, filename.length() - 9)); + } else { + decryptFile = new File(currentDir + "/" + filename); + } + } else { + encryptFile = new File(currentConfigSettings.get("fileOutputPath") + "/" + filename + ".cryptoGX"); + while (encryptFile.isFile()) { + encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX"); + } + if (url.endsWith(".cryptoGX") && filename.endsWith(".cryptoGX")) { + decryptFile = new File(currentConfigSettings.get("fileOutputPath") + "/" + filename.substring(0, filename.length() - 9)); + } else { + decryptFile = new File(currentConfigSettings.get("fileOutputPath") + "/" + filename); + } + } + while (decryptFile.isFile()) { + decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX"); + } + inputOutputFiles.add(0, encryptFile); + inputOutputFiles.add(1, decryptFile); + + fileEnDecryptInputFiles.getChildren().add(newLabel); + enDecryptInputOutputInternetFiles.put(newLabel, fileSpecs); + enDecryptInputOutputFiles.put(newLabel, inputOutputFiles); + } + + /** + *

Adds an clipboard image for en- / decryption. + * This can be a normal image and an image stream

+ * + * @param image that should be added + * @throws URISyntaxException + */ + private void fileEnDecryptAddClipboardImage(BufferedImage image) throws URISyntaxException { + String filename = "clipboardImage" + System.nanoTime() + ".png"; + for (Label l: enDecryptInputOutputClipboardImages.keySet()) { + if (l.getText().equals(filename)) { + return; + } + } + Label newLabel = new Label(filename); + newLabel.setOnKeyTyped(this::keyTypedTooltip); + newLabel.setOnMouseMoved(this::mouseOverEntryTooltip); + newLabel.setOnMouseExited(event -> mouseExitEntryTooltip()); + newLabel.setOnMouseClicked(event -> { + fileEnDecryptSelected(newLabel); + fileOutputFilesChangeText(newLabel, null, null); + fileEnDecryptLabelEvent = event; + }); + newLabel.setContextMenu(fileEnDecryptInputContextMenu); + + File encryptFile; + File decryptFile; + String currentDir = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + ArrayList inputOutputFiles = new ArrayList<>(); + + if (currentConfigSettings.get("fileOutputPath").trim().isEmpty()) { + encryptFile = new File(currentDir + "/" + filename + ".cryptoGX"); + decryptFile = new File(currentDir + "/" + filename); + } else { + encryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + filename + ".cryptoGX"); + decryptFile = new File(currentConfigSettings.get("fileOutputPath").trim() + "/" + filename); + } + while (encryptFile.isFile()) { + encryptFile = new File(encryptFile.getAbsolutePath() + ".cryptoGX"); + } + while (decryptFile.isFile()) { + decryptFile = new File(decryptFile.getAbsolutePath() + ".cryptoGX"); + } + inputOutputFiles.add(0, encryptFile); + inputOutputFiles.add(1, decryptFile); + + fileEnDecryptInputFiles.getChildren().add(newLabel); + enDecryptInputOutputClipboardImages.put(newLabel, image); + enDecryptInputOutputFiles.put(newLabel, inputOutputFiles); + } + + /** + *

Changes the text in the file en- / decryption output file text fields

+ * + * @param label + * @param encryptOutputFile is the filename of the file it gets encrypted + * @param decryptOutputFile is the filename of the file it gets decrypted + */ + private void fileOutputFilesChangeText(Label label, String encryptOutputFile, String decryptOutputFile) { + File encryptFile; + File decryptFile; + ArrayList change = new ArrayList<>(); + if (encryptOutputFile == null) { + encryptFile = enDecryptInputOutputFiles.get(label).get(0); + } else { + encryptFile = new File(encryptOutputFile); + } + if (decryptOutputFile == null) { + decryptFile = enDecryptInputOutputFiles.get(label).get(1); + } else { + decryptFile = new File(decryptOutputFile); + } + change.add(0, encryptFile); + change.add(1, decryptFile); + if (encryptFile.toString().trim().isEmpty()) { + fileEncryptOutputFile.setText(""); + } else { + fileEncryptOutputFile.setText(encryptFile.getAbsolutePath()); + } + if (decryptFile.toString().trim().isEmpty()) { + fileDecryptOutputFile.setText(""); + } else { + fileDecryptOutputFile.setText(decryptFile.getAbsolutePath()); + } + enDecryptInputOutputFiles.replace(label, change); + } + + /** + *

Deletes an entry for en- / decryption. + * Get called if the user presses 'del' or delete the entry in the en- / decryption box via the right click tooltip

+ * + * @see Controller#fileEnDecryptDeleteEntry(Label) + */ + private void fileEnDecryptDeleteEntry() { + fileEnDecryptDeleteEntry(choosedLabel); + } + + /** + *

Deletes an entry for en- / decryption. + * Get called if the user presses 'del' or delete the entry in the en- / decryption box via the right click tooltip

+ * + * @param label that should be deleted + */ + private void fileEnDecryptDeleteEntry(Label label) { + enDecryptInputOutputFiles.remove(label); + if (fileEnDecryptInputFiles.getChildren().size() - 1 >= 1) { + for (int i = 0; i < fileEnDecryptInputFiles.getChildren().size(); i++) { + if (fileEnDecryptInputFiles.getChildren().get(i) == label) { + fileEnDecryptInputFiles.getChildren().remove(label); + if (label == choosedLabel) { + try { + choosedLabel = (Label) fileEnDecryptInputFiles.getChildren().get(i - 1); + choosedLabelType = "ENDECRYPT"; + fileOutputFilesChangeText(choosedLabel, null, null); + fileEnDecryptSelected(choosedLabel); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + fileOutputFileChangeDest.setDisable(true); + getChoosedLabelOutputFileFolder.setDisable(true); + fileEncryptOutputFile.setEditable(false); + fileDecryptOutputFile.setEditable(false); + fileOutputFilesChangeText(choosedLabel, "", ""); + choosedLabel = null; + choosedLabelType = null; + } + break; + } + } + } + } else { + fileEnDecryptInputFiles.getChildren().remove(label); + fileOutputFileChangeDest.setDisable(true); + getChoosedLabelOutputFileFolder.setDisable(true); + fileEncryptOutputFile.setEditable(false); + fileDecryptOutputFile.setEditable(false); + if (label == choosedLabel) { + fileOutputFilesChangeText(choosedLabel, "", ""); + choosedLabel = null; + choosedLabelType = null; + } + } + } + + /** + *

Changes the highlight of the clicked item in the en- / decryption box. + * Get called if the user click an non-highlighted item in the en- / decryption box

+ * + * @param changeLabel is the label that the user has clicked + */ + private void fileEnDecryptSelected(Label changeLabel) { + if (changeLabel != null) { + fileDeleteSelected(null); + enDecryptInputOutputFiles.keySet().forEach(label -> label.setStyle(null)); + changeLabel.setStyle("-fx-background-color: lightblue; -fx-border-color: #292929"); + fileDecryptOutputFile.setEditable(true); + fileEncryptOutputFile.setEditable(true); + fileOutputFileChangeDest.setDisable(false); + getChoosedLabelOutputFileFolder.setDisable(false); + choosedLabel = changeLabel; + choosedLabelType = "ENDECRYPT"; + } else { + enDecryptInputOutputFiles.keySet().forEach(label -> label.setStyle(null)); + fileDecryptOutputFile.setEditable(false); + fileEncryptOutputFile.setEditable(false); + fileOutputFileChangeDest.setDisable(true); + getChoosedLabelOutputFileFolder.setDisable(true); + } + } + + /** + *

Opens a file chooser GUI where the user can select the files that should be en- / decrypted. + * Get called if the 'Choose files...' in the file en- / decrypt section button is pressed

+ */ + public void fileEnDecryptChoose() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Choose files"); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*")); + List files = fileChooser.showOpenMultipleDialog(rootWindow.getScene().getWindow()); + try { + if (files.size() >= 1) { + files.forEach(this::fileEnDecryptAddFile); + } + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + + /** + *

Get called if user drags a (normal or internet) file over the en- / decrypt file box

+ * + * @param event source + */ + public void onFileEnDecryptDragOver(DragEvent event) { + Dragboard dragboard = event.getDragboard(); + if (event.getGestureSource() != fileEnDecryptInputFiles) { + if (dragboard.hasFiles()) { + if (dragboard.getFiles().size() == 1 && dragboard.getFiles().get(0).isDirectory()) { + return; + } else { + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } + } else if (dragboard.hasUrl()) { + String url = dragboard.getUrl(); + String urlFilename = dragboard.getUrl().split("/")[dragboard.getUrl().split("/").length - 1]; + if (url.startsWith("data:")) { + try { + final int dataStartIndex = url.indexOf(",") + 1; + final String data = url.substring(dataStartIndex); + java.util.Base64.getDecoder().decode(data); + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } catch (Exception e) { + e.printStackTrace(); + } + } else if (urlFilename.contains(".") && !Utils.hasAnyCharacter("\\/:*?|<>\"", urlFilename)) { + try { + new URL(url); + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } catch (Exception e) { + e.printStackTrace(); + return; + } + } else { + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } + } + } + } + + /** + *

Get called if the user drops the dragged (normal or internet) file over the en- / decrypt file box

+ * + * @param event source + * @throws URISyntaxException + */ + public void onFileEnDecryptDragNDrop(DragEvent event) throws URISyntaxException { + Dragboard dragboard = event.getDragboard(); + if (dragboard.hasFiles()) { + dragboard.getFiles().forEach(file -> { + if (file.isFile()) { + fileEnDecryptAddFile(file); + } + }); + } else if (dragboard.hasUrl()) { + String url = dragboard.getUrl(); + String urlFilename = dragboard.getUrl().split("/")[dragboard.getUrl().split("/").length - 1]; + if (url.startsWith("data:")) { + fileEnDecryptAddInternetFile(url, DATAFILEURL); + } else if (urlFilename.contains(".") && !Utils.hasAnyCharacter("\\/:*?|<>\"", urlFilename)) { + fileEnDecryptAddInternetFile(url, FILEFILEURL); + } else { + fileEnDecryptAddInternetFile(url, NONSPECIFICFILEURL); + } + } + } + + /** + *

If the user presses Ctrl + V: Adds the last object in clipboard (if file) for en- / decryption. + * Get called if the user presses a key while selected file en- / decryption box

+ * + * @param event source + * @throws URISyntaxException + */ + public void onFileEnDecryptPaste(KeyEvent event) throws URISyntaxException { + if (paste.match(event)) { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + Transferable transferable = clipboard.getContents(null); + try { + if (transferable != null) { + if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + Object objectFileList = transferable.getTransferData(DataFlavor.javaFileListFlavor); + List files = (List) objectFileList; + files.forEach(o -> fileEnDecryptAddFile((File) o)); + } else if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) { + Object objectImage = transferable.getTransferData(DataFlavor.imageFlavor); + fileEnDecryptAddClipboardImage((BufferedImage) objectImage); + } + } + } catch (UnsupportedFlavorException | IOException e) { + e.printStackTrace(); + } + } + } + + /** + *

Encrypt all files given files. + * Get called if file 'Encrypt' button is pressed

+ */ + public void fileEncryptButton() { + final byte[] salt; + if (!fileEnDecryptSaltEntry.getText().isEmpty()) { + salt = fileEnDecryptSaltEntry.getText().getBytes(StandardCharsets.UTF_8); + } else { + salt = new byte[16]; + } + if (!enDecryptInputOutputFiles.isEmpty()) { + removeFileFromFileBox.setDisable(true); + limitNumberOfThreads.setDisable(true); + for(Map.Entry> entry: enDecryptInputOutputFiles.entrySet()) { + Thread thread = new Thread(() -> { + addFileEnDecryptThread(Thread.currentThread()); + if (limitNumberOfThreads.isSelected()) { + while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().stop(); + } + } + } + totalThreads.getAndIncrement(); + Label inputFileLabel = entry.getKey(); + ArrayList outputFileList = entry.getValue(); + EnDecrypt.AES fileEncrypt = new EnDecrypt.AES(fileEnDecryptKeyEntry.getText(), salt); + if (enDecryptInputOutputInternetFiles.containsKey(inputFileLabel)) { + ArrayList fileSpecs = enDecryptInputOutputInternetFiles.get(inputFileLabel); + int urlType = (int) fileSpecs.get(0); + String url = (String) fileSpecs.get(1); + try { + if (urlType == FILEFILEURL) { + URLConnection openURL = new URL(url).openConnection(); + openURL.addRequestProperty("User-Agent", "Mozilla/5.0"); + fileEncrypt.encryptFileLineByLine(openURL.getInputStream(), new FileOutputStream((File) fileSpecs.get(2))); + } else if (urlType == DATAFILEURL) { + final int dataStartIndex = url.indexOf(",") + 1; + final String data = url.substring(dataStartIndex); + byte[] decoded = java.util.Base64.getDecoder().decode(data); + fileEncrypt.encryptFileAllInOne(decoded, new FileOutputStream((File) fileSpecs.get(2))); + } else if (urlType == NONSPECIFICFILEURL) { + URLConnection openURL = new URL(url).openConnection(); + openURL.addRequestProperty("User-Agent", "Mozilla/5.0"); + fileEncrypt.encryptFileLineByLine(openURL.getInputStream(), new FileOutputStream((File) fileSpecs.get(2))); + } + } catch (FileNotFoundException | InvalidKeySpecException | NoSuchAlgorithmException | MalformedURLException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage())); + } + } else if (enDecryptInputOutputClipboardImages.containsKey(inputFileLabel)) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + BufferedImage bufferedImage = enDecryptInputOutputClipboardImages.get(inputFileLabel); + try { + ImageIO.write(bufferedImage, "png", byteArrayOutputStream); + fileEncrypt.encryptFileAllInOne(byteArrayOutputStream.toByteArray(), new FileOutputStream(outputFileList.get(0).getAbsoluteFile())); + } catch (IOException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage())); + } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | BadPaddingException | InvalidKeySpecException | IllegalBlockSizeException e) { + e.printStackTrace(); + } + } else { + try { + fileEncrypt.encryptFileLineByLine(inputFileLabel.getText(), outputFileList.get(0).getAbsolutePath()); + } catch (NoSuchPaddingException | InvalidAlgorithmParameterException | NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage())); + } + } + if (removeFileFromFileBox.isSelected()) { + Platform.runLater(() -> fileEnDecryptDeleteEntry(entry.getKey())); + } + if ((getFileEnDecryptThreadsSize() - 1) <= 0) { + fileEnDecryptLoading = false; + Platform.runLater(() -> { + fileEnDecryptLoadingImage.setImage(null); + removeFileFromFileBox.setDisable(false); + limitNumberOfThreads.setDisable(false); + }); + } + removeFileEnDecryptThread(Thread.currentThread()); + totalThreads.getAndDecrement(); + }); + thread.setDaemon(false); + thread.start(); + if (!fileEnDecryptLoading) { + fileEnDecryptLoadingImage.setImage(loadingImage); + } + fileEnDecryptLoading = true; + } + } + } + + /** + *

Decrypt all files given files. + * Get called if file 'Decrypt' button is pressed

+ */ + public void fileDecryptButton() { + final byte[] salt; + if (!fileEnDecryptSaltEntry.getText().isEmpty()) { + salt = fileEnDecryptSaltEntry.getText().getBytes(StandardCharsets.UTF_8); + } else { + salt = new byte[16]; + } + if (!enDecryptInputOutputFiles.isEmpty()) { + removeFileFromFileBox.setDisable(true); + limitNumberOfThreads.setDisable(true); + for(Map.Entry> entry: enDecryptInputOutputFiles.entrySet()) { + Thread thread = new Thread(() -> { + addFileEnDecryptThread(Thread.currentThread()); + if (limitNumberOfThreads.isSelected()) { + while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().stop(); + } + } + } + totalThreads.getAndIncrement(); + Label inputFileLabel = entry.getKey(); + ArrayList outputFileList = entry.getValue(); + EnDecrypt.AES fileDecrypt = new EnDecrypt.AES(fileEnDecryptKeyEntry.getText(), salt); + if (enDecryptInputOutputInternetFiles.containsKey(entry.getKey())) { + ArrayList imageSpecs = enDecryptInputOutputInternetFiles.get(entry.getKey()); + int urlType = (int) imageSpecs.get(0); + String url = (String) imageSpecs.get(1); + try { + if (urlType == FILEFILEURL) { + URLConnection openURL = new URL(url).openConnection(); + openURL.addRequestProperty("User-Agent", "Mozilla/5.0"); + fileDecrypt.decryptFileLineByLine(openURL.getInputStream(), new FileOutputStream((File) imageSpecs.get(2))); + } else if (urlType == DATAFILEURL) { + final int dataStartIndex = url.indexOf(",") + 1; + final String data = url.substring(dataStartIndex); + byte[] decoded = java.util.Base64.getDecoder().decode(data); + fileDecrypt.decryptFileAllInOne(decoded, new FileOutputStream((File) imageSpecs.get(2))); + } else if (urlType == NONSPECIFICFILEURL) { + URLConnection openURL = new URL(url).openConnection(); + openURL.addRequestProperty("User-Agent", "Mozilla/5.0"); + fileDecrypt.decryptFileLineByLine(openURL.getInputStream(), new FileOutputStream((File) imageSpecs.get(2))); + } + } catch (FileNotFoundException | InvalidKeySpecException | NoSuchAlgorithmException | MalformedURLException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage())); + } + } else if (enDecryptInputOutputClipboardImages.containsKey(inputFileLabel)) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + BufferedImage bufferedImage = enDecryptInputOutputClipboardImages.get(inputFileLabel); + try { + ImageIO.write(bufferedImage, "png", byteArrayOutputStream); + fileDecrypt.decryptFileAllInOne(byteArrayOutputStream.toByteArray(), new FileOutputStream(outputFileList.get(1).getAbsolutePath())); + } catch (IOException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage())); + } catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | BadPaddingException | InvalidKeySpecException | IllegalBlockSizeException e) { + e.printStackTrace(); + } + } else { + try { + fileDecrypt.decryptFileLineByLine(inputFileLabel.getText(), outputFileList.get(1).getAbsolutePath()); + } catch (NoSuchPaddingException | InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("IO Exception occurred", e.getMessage())); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + Platform.runLater(() -> errorAlert("Wrong text for encryption is given", e.getMessage())); + } + } + if (removeFileFromFileBox.isSelected()) { + Platform.runLater(() -> fileEnDecryptDeleteEntry(entry.getKey())); + } + if ((getFileEnDecryptThreadsSize() - 1) <= 0) { + fileEnDecryptLoading = false; + Platform.runLater(() -> { + fileEnDecryptLoadingImage.setImage(null); + removeFileFromFileBox.setDisable(false); + limitNumberOfThreads.setDisable(false); + }); + } + removeFileEnDecryptThread(Thread.currentThread()); + totalThreads.getAndDecrement(); + }); + thread.setDaemon(false); + thread.start(); + if (!fileEnDecryptLoading) { + fileEnDecryptLoadingImage.setImage(loadingImage); + } + fileEnDecryptLoading = true; + } + } + } + + /** + *

Cancels the file en- / decryption. + * Get called if the file en- / decrypt 'Cancel' button is pressed

+ */ + public void fileEnDecryptCancelButton() { + for (Iterator iterator = getFileEnDecryptThreads().iterator(); iterator.hasNext();) { + Thread thread = iterator.next(); + while (thread.isAlive() && !thread.isInterrupted()) { + thread.stop(); + thread.interrupt(); + } + iterator.remove(); + } + fileEnDecryptLoading = false; + fileEnDecryptLoadingImage.setImage(null); + removeFileFromFileBox.setDisable(false); + limitNumberOfThreads.setDisable(false); + } + + //-----fileDelete-----// + + /** + *

Synchronized method to get the list of threads which delete files

+ * + * @return list of threads which delete files + */ + private synchronized List getFileDeleteThreads() { + return fileDeleteThreads; + } + + /** + *

Synchronized method to get the number of threads which delete files

+ * + * @return number of threads which delete files + */ + private synchronized int getFileDeleteThreadsSize() { + return fileDeleteThreads.size(); + } + + /** + *

Synchronized method to add a thread to the file delete list of current running file delete threads

+ * + * @param thread that should be added + */ + private synchronized void addFileDeleteThread(Thread thread) { + fileDeleteThreads.add(thread); + } + + /** + *

Synchronized method to remove a thread from the file delete list of current file delete threads

+ * + * @param thread that should be removed + */ + private synchronized void removeFileDeleteThread(Thread thread) { + fileDeleteThreads.remove(thread); + } + + /** + *

Adds a file that should be deleted

+ * + * @param file that should be added + */ + private void fileDeleteAddFile(File file) { + for (File f: deleteInputFiles.values()) { + if (f.getAbsolutePath().equals(file.getAbsolutePath())) { + return; + } + } + Label newLabel = new Label(file.getAbsolutePath()); + newLabel.setOnKeyTyped(this::keyTypedTooltip); + newLabel.setOnMouseMoved(this::mouseOverEntryTooltip); + newLabel.setOnMouseExited(event -> mouseExitEntryTooltip()); + newLabel.setOnMouseClicked(event -> { + fileDeleteSelected(newLabel); + if (event.getButton() == MouseButton.SECONDARY) { + fileDeleteInputContextMenu.show(newLabel, event.getScreenX(), event.getScreenY()); + } + }); + fileDeleteInputFiles.getChildren().add(newLabel); + deleteInputFiles.put(newLabel, file.getAbsoluteFile()); + } + + /** + *

Changes the highlight of the clicked item in the file delete box. + * Get called if the user click an non-highlighted item in the file delete box

+ * + * @param changeLabel is the label that the user has clicked + */ + private void fileDeleteSelected(Label changeLabel) { + if (changeLabel != null) { + fileEnDecryptSelected(null); + deleteInputFiles.keySet().forEach(label -> label.setStyle(null)); + changeLabel.setStyle("-fx-background-color: lightblue; -fx-border-color: #292929"); + choosedLabel = changeLabel; + choosedLabelType = "DELETE"; + } else { + deleteInputFiles.keySet().forEach(label -> label.setStyle(null)); + } + } + + /** + *

Deletes an entry for file delete. + * Get called if the user presses 'del' or delete the entry in the file delete box via the right click tooltip

+ * + * @see Controller#fileEnDecryptDeleteEntry(Label) + */ + private void fileDeleteDeleteEntry() { + fileEnDecryptDeleteEntry(choosedLabel); + } + + /** + *

Deletes an entry for file delete. + * Get called if the user presses 'del' or delete the entry in the file delete box via the right click tooltip

+ * + * @param label that should be deleted + */ + private void fileDeleteDeleteEntry(Label label) { + deleteInputFiles.remove(choosedLabel); + if (fileDeleteInputFiles.getChildren().size() - 1 >= 1) { + for (int i=0; iOpens a file chooser GUI where the user can select the files that should be en- / decrypted. + * Get called if the 'Choose files...' in the delete section button is pressed

+ */ + public void fileDeleteChoose() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Choose files"); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All Files", "*.*")); + List files = fileChooser.showOpenMultipleDialog(rootWindow.getScene().getWindow()); + try { + if (files.size() >= 1) { + files.forEach(file -> fileDeleteAddFile(file)); + } + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + + /** + *

Get called if user drags a file over the delete file box

+ * + * @param event source + */ + public void onFileDeleteDragOver(DragEvent event) { + Dragboard dragboard = event.getDragboard(); + if (event.getGestureSource() != fileDeleteInputFiles && dragboard.hasFiles()) { + if (dragboard.getFiles().size() == 1 && dragboard.getFiles().get(0).isDirectory()) { + return; + } else { + event.acceptTransferModes(TransferMode.COPY_OR_MOVE); + } + } + } + + /** + *

Get called if the user drops the dragged file over the delete file box

+ * + * @param event source + */ + public void onFileDeleteDragNDrop(DragEvent event) { + Dragboard dragboard = event.getDragboard(); + if (dragboard.hasFiles()) { + dragboard.getFiles().forEach(file -> { + if (file.isFile()) { + fileDeleteAddFile(file); + } + }); + } + } + + /** + *

Delete all given files. + * Get called if 'Delete' button is pressed

+ */ + public void fileDelete() { + if (!fileDeleteLoading && !deleteInputFiles.isEmpty()) { + fileDeleteLoadingImage.setImage(loadingImage); + } + Iterator> deleteIterator = deleteInputFiles.entrySet().iterator(); + while(deleteIterator.hasNext()) { + Map.Entry map = deleteIterator.next(); + Label label = map.getKey(); + File file = map.getValue(); + Thread thread = new Thread(() -> { + addFileDeleteThread(Thread.currentThread()); + if (limitNumberOfThreads.isSelected()) { + while (totalThreads.get() >= Runtime.getRuntime().availableProcessors()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().stop(); + } + } + } + totalThreads.getAndIncrement(); + String deleteFile = file.getAbsolutePath(); + try { + SecureDelete.deleteFileLineByLine(deleteFile, Integer.parseInt(fileDeleteIterationsEntry.getText())); + } catch (NoSuchAlgorithmException | IOException e) { + e.printStackTrace(); + } + if ((getFileDeleteThreadsSize() - 1) <= 0) { + fileDeleteLoading = false; + Platform.runLater(() -> fileDeleteLoadingImage.setImage(null)); + } + if (label == choosedLabel) { + choosedLabel = null; + choosedLabelType = null; + } + Platform.runLater(() -> fileDeleteInputFiles.getChildren().remove(label)); + removeFileDeleteThread(Thread.currentThread()); + totalThreads.getAndDecrement(); + }); + thread.setDaemon(false); + thread.start(); + fileDeleteLoading = true; + } + } + + /** + *

Cancels the file en- / decryption. + * Get called if the file delete 'Cancel' button is pressed

+ */ + public void fileDeleteCancelButton() { + for (Iterator iterator = getFileDeleteThreads().iterator(); iterator.hasNext();) { + Thread thread = iterator.next(); + while (thread.isAlive() & !thread.isInterrupted()) { + thread.stop(); + thread.interrupt(); + } + iterator.remove(); + } + fileDeleteLoading = false; + fileDeleteLoadingImage.setImage(null); + } + + /** + * Called to initialize a controller after its root element has been + * completely processed. + * + * @param location + * The location used to resolve relative paths for the root object, or + * null if the location is not known. + * + * @param resources + * The resources used to localize the root object, or null if + * the root object was not localized. + */ + @Override + public void initialize(URL location, ResourceBundle resources) { + + //-----general-----// + + currentConfigSettings.put("encryptHash", configDefaultEncryptHash); + + currentConfigSettings.put("textKey", configDefaultTextKey); + currentConfigSettings.put("textSalt", configDefaultTextSalt); + currentConfigSettings.put("textAlgorithm", configDefaultTextAlgorithm); + + currentConfigSettings.put("fileEnDecryptKey", configDefaultFileEnDecryptKey); + currentConfigSettings.put("fileEnDecryptSalt", configDefaultFileEnDecryptSalt); + currentConfigSettings.put("fileEnDecryptAlgorithm", configDefaultFileEnDecryptAlgorithm); + + currentConfigSettings.put("fileDeleteIterations", String.valueOf(configDefaultFileDeleteIterations)); + + currentConfigSettings.put("fileOutputPath", configDefaultFileOutputPath); + currentConfigSettings.put("removeFromFileBox", String.valueOf(configDefaultRemoveFileFromFileBox)); + currentConfigSettings.put("limitNumberOfThreads", String.valueOf(configDefaultLimitNumberOfThreads)); + + textAlgorithms.add("AES"); + fileEnDecryptAlgorithms.add("AES"); + + menubar.setOnMouseDragged(event -> { + Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); + stage.setX(event.getScreenX() + menubarX); + stage.setY(event.getScreenY() + menubarY); + }); + menubar.setOnMousePressed(event -> { + Scene scene = ((Node) event.getSource()).getScene(); + menubarX = scene.getX() - event.getSceneX(); + menubarY = scene.getY() - event.getSceneY(); + }); + + rootWindow.setOnKeyReleased(event -> { + if (event.getCode() == KeyCode.DELETE && choosedLabelType != null) { + if (choosedLabelType.equals("ENDECRYPT")) { + fileEnDecryptDeleteEntry(); + } else if (choosedLabelType.equals("DELETE")) { + fileDeleteDeleteEntry(); + } + } + }); + + getChoosedLabelInputFileFolder.setOnAction(event -> { + Desktop desktop = Desktop.getDesktop(); + String filePath = choosedLabel.getText(); + try { + desktop.open(new File(filePath.substring(0, filePath.lastIndexOf(System.getProperty("file.separator"))))); + } catch (IOException e) { + errorAlert("An unexpected IO Exception occurred", e.getMessage()); + } + }); + + getChoosedLabelOutputFileFolder.setOnAction(event -> { + Desktop desktop; + String filePath; + if (enDecryptInputOutputFiles.containsKey(choosedLabel)) { + desktop = Desktop.getDesktop(); + filePath = enDecryptInputOutputFiles.get(choosedLabel).get(0).getAbsolutePath(); + } else { + return; + } + try { + desktop.open(new File(filePath.substring(0, filePath.lastIndexOf(System.getProperty("file.separator"))))); + } catch (IOException e) { + errorAlert("An unexpected IO Exception occurred", e.getMessage()); + } + }); + + setDefaultOutputPath.setOnAction(event -> { + DirectoryChooser directoryChooser = new DirectoryChooser(); + File directory = directoryChooser.showDialog(rootWindow.getScene().getWindow()); + try { + currentConfigSettings.replace("fileOutputPath", directory.getAbsolutePath()); + } catch (NullPointerException e) { + e.printStackTrace(); + } + }); + + settingsMenu.setOnShowing(event -> { + loadSettings.setDisable(!isConfig); + exportSettings.setDisable(!isConfig); + }); + + removeFileFromFileBox.setOnAction(event -> currentConfigSettings.replace("removeFromFileBox", String.valueOf(removeFileFromFileBox.isSelected()))); + limitNumberOfThreads.setOnAction(event -> currentConfigSettings.replace("limitNumberOfThreads", String.valueOf(limitNumberOfThreads.isSelected()))); + saveSettings.setOnAction(event -> { + try { + addSettingGUI(rootWindow.getScene().getWindow(), currentConfigSettings); + if (config.isFile()) { + isConfig = true; + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + loadSettings.setOnAction(event -> { + try { + currentConfigSettings = (HashMap) 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(); + } + } + }); + exportSettings.setOnAction(event -> { + try { + exportSettingsGUI(rootWindow.getScene().getWindow()); + } catch (IOException e) { + e.printStackTrace(); + errorAlert("IO Exception occurred", e.getMessage()); + } + }); + importSettings.setOnAction(event -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Import settings"); + fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("Config files", "*.config*", "*.xml"), new FileChooser.ExtensionFilter("All files", "*.*")); + File file = fileChooser.showOpenDialog(rootWindow.getScene().getWindow()); + if (file != null) { + if (isConfig) { + readUserSettings(file).forEach((Config::addSetting)); + } else { + writeConfig(readUserSettings(file)); + isConfig = true; + } + } + }); + + //-----text------// + + textAlgorithmBox.setItems(FXCollections.observableArrayList(textAlgorithms)); + textAlgorithmBox.setValue(textAlgorithms.get(0)); + + //-----fileEnDecrypt-----// + + fileEnDecryptAlgorithmBox.setItems(FXCollections.observableArrayList(fileEnDecryptAlgorithms)); + fileEnDecryptAlgorithmBox.setValue(fileEnDecryptAlgorithms.get(0)); + + MenuItem enDecryptRemove = new MenuItem(); + enDecryptRemove.setText("Remove"); + enDecryptRemove.setOnAction(removeEvent -> fileEnDecryptDeleteEntry()); + MenuItem enDecryptChangeDest = new MenuItem(); + enDecryptChangeDest.setText("Change output file"); + enDecryptChangeDest.setOnAction(outputFileChangeEvent -> { + FileChooser fileDestChooser = new FileChooser(); + fileDestChooser.setTitle("Choose or create new file"); + fileDestChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*")); + File file = fileDestChooser.showSaveDialog(rootWindow.getScene().getWindow()); + if (file != null) { + for (Map.Entry> entry : enDecryptInputOutputFiles.entrySet()) { + if (entry.getKey().getText().equals(choosedLabel.getText())) { + ArrayList changedFile = new ArrayList<>(); + changedFile.add(0, file); + changedFile.add(1, file); + enDecryptInputOutputFiles.replace(entry.getKey(), entry.getValue(), changedFile); + fileOutputFilesChangeText((Label) fileEnDecryptLabelEvent.getSource(), file.getAbsolutePath(), file.getAbsolutePath()); + break; + } + } + } + }); + fileEnDecryptInputContextMenu.getItems().addAll(enDecryptRemove, enDecryptChangeDest); + + ContextMenu fileEnDecryptInputFilesMenu = new ContextMenu(); + MenuItem enDecryptPaste = new MenuItem(); + enDecryptPaste.setText("Paste"); + enDecryptPaste.setOnAction(pasteEvent -> { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + Transferable transferable = clipboard.getContents(null); + try { + if (transferable != null) { + if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + Object objectFileList = transferable.getTransferData(DataFlavor.javaFileListFlavor); + List files = (List) objectFileList; + files.forEach(o -> fileEnDecryptAddFile((File) o)); + } else if (transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) { + Object objectImage = transferable.getTransferData(DataFlavor.imageFlavor); + fileEnDecryptAddClipboardImage((BufferedImage) objectImage); + } + } + } catch (UnsupportedFlavorException | IOException | URISyntaxException e) { + e.printStackTrace(); + } + }); + fileEnDecryptInputFilesMenu.getItems().add(enDecryptPaste); + + fileEnDecryptInputFiles.setOnContextMenuRequested(event -> { + if (!fileEnDecryptInputContextMenu.isShowing()) { + fileEnDecryptInputFilesMenu.show(((VBox) event.getSource()).getParent().getScene().getWindow(), event.getScreenX(), event.getScreenY()); + } + }); + + fileOutputFileChangeDest.setOnAction(event -> { + FileChooser fileDestChooser = new FileChooser(); + fileDestChooser.setTitle("Choose or create new file"); + fileDestChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("All files", "*.*")); + File file = fileDestChooser.showSaveDialog(rootWindow.getScene().getWindow()); + if (file != null) { + for (Map.Entry> entry : enDecryptInputOutputFiles.entrySet()) { + if (entry.getKey().getText().equals(choosedLabel.getText())) { + ArrayList changedFile = new ArrayList<>(); + changedFile.add(0, file); + changedFile.add(1, file); + enDecryptInputOutputFiles.replace(entry.getKey(), entry.getValue(), changedFile); + fileOutputFilesChangeText((Label) fileEnDecryptLabelEvent.getSource(), file.getAbsolutePath(), file.getAbsolutePath()); + break; + } + } + } + }); + + fileOutputFileChangeDest.setDisable(true); + getChoosedLabelOutputFileFolder.setDisable(true); + + fileEncryptOutputFile.textProperty().addListener((observable, oldValue, newValue) -> { + fileOutputFilesChangeText(choosedLabel, newValue, fileDecryptOutputFile.getText()); + }); + fileDecryptOutputFile.textProperty().addListener((observable, oldValue, newValue) -> { + fileOutputFilesChangeText(choosedLabel, fileEncryptOutputFile.getText(), newValue); + }); + + //-----fileDelete-----// + + MenuItem deleteRemove = new MenuItem(); + deleteRemove.setText("Remove"); + deleteRemove.setOnAction(removeEvent -> fileDeleteDeleteEntry()); + fileDeleteInputContextMenu.getItems().addAll(deleteRemove); + + ContextMenu fileDeleteInputFilesMenu = new ContextMenu(); + MenuItem deletePaste = new MenuItem(); + deletePaste.setText("Paste"); + deletePaste.setOnAction(pasteEvent -> { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + Transferable transferable = clipboard.getContents(null); + try { + if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { + Object objectFileList = transferable.getTransferData(DataFlavor.javaFileListFlavor); + List files = (List) objectFileList; + files.forEach(o -> fileDeleteAddFile((File) o)); + } + } catch (UnsupportedFlavorException | IOException e) { + e.printStackTrace(); + } + }); + fileDeleteInputFilesMenu.getItems().add(deletePaste); + + fileDeleteInputFiles.setOnContextMenuRequested(event -> { + if (!fileDeleteInputContextMenu.isShowing()) { + fileDeleteInputFilesMenu.show(((VBox) event.getSource()).getParent().getScene().getWindow(), event.getScreenX(), event.getScreenY()); + } + }); + + fileDeleteIterationsEntry.textProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue.matches("[0-9]*")) { + fileDeleteIterationsEntry.setText(oldValue); + } + }); + + Thread t = new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (isConfig) { + Platform.runLater(() -> { + try { + currentConfigSettings = (HashMap) 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +