From 251532493f360dced91a1eea9e6c7588ee5a20fa Mon Sep 17 00:00:00 2001 From: CubeBit Date: Wed, 2 Aug 2023 12:14:50 +0200 Subject: [PATCH] * added desktop client Signed-off-by: CubeBit --- .../org/orinprojects/{ => client}/Client.java | 6 +- .../{ => client}/ClientThread.java | 8 +- desktop-client/pom.xml | 62 ++++++++++ desktop-client/src/main/java/module-info.java | 9 ++ .../orinprojects/desktop/ChatController.java | 89 +++++++++++++++ .../orinprojects/desktop/ClientThread.java | 101 ++++++++++++++++ .../java/org/orinprojects/desktop/Main.java | 46 ++++++++ desktop-client/src/main/resources/chat.fxml | 108 ++++++++++++++++++ pom.xml | 1 + server/src/main/java/module-info.java | 3 + .../java/org/orinprojects/ClientHandler.java | 15 ++- 11 files changed, 438 insertions(+), 10 deletions(-) rename client/src/main/java/org/orinprojects/{ => client}/Client.java (96%) rename client/src/main/java/org/orinprojects/{ => client}/ClientThread.java (94%) create mode 100644 desktop-client/pom.xml create mode 100644 desktop-client/src/main/java/module-info.java create mode 100644 desktop-client/src/main/java/org/orinprojects/desktop/ChatController.java create mode 100644 desktop-client/src/main/java/org/orinprojects/desktop/ClientThread.java create mode 100644 desktop-client/src/main/java/org/orinprojects/desktop/Main.java create mode 100644 desktop-client/src/main/resources/chat.fxml create mode 100644 server/src/main/java/module-info.java diff --git a/client/src/main/java/org/orinprojects/Client.java b/client/src/main/java/org/orinprojects/client/Client.java similarity index 96% rename from client/src/main/java/org/orinprojects/Client.java rename to client/src/main/java/org/orinprojects/client/Client.java index 21101bd..ca2eeaa 100644 --- a/client/src/main/java/org/orinprojects/Client.java +++ b/client/src/main/java/org/orinprojects/client/Client.java @@ -1,4 +1,4 @@ -package org.orinprojects; +package org.orinprojects.client; import org.orinprojects.encryption.EncryptionUtil; import org.orinprojects.exceptions.ArgumentsException; @@ -7,7 +7,9 @@ import javax.crypto.SecretKey; import java.net.Socket; import java.security.KeyPair; import java.security.PublicKey; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; public class Client { diff --git a/client/src/main/java/org/orinprojects/ClientThread.java b/client/src/main/java/org/orinprojects/client/ClientThread.java similarity index 94% rename from client/src/main/java/org/orinprojects/ClientThread.java rename to client/src/main/java/org/orinprojects/client/ClientThread.java index 1080e78..2b4f14e 100644 --- a/client/src/main/java/org/orinprojects/ClientThread.java +++ b/client/src/main/java/org/orinprojects/client/ClientThread.java @@ -1,15 +1,17 @@ -package org.orinprojects; +package org.orinprojects.client; import org.orinprojects.encryption.EncryptionUtil; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.Socket; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; public class ClientThread implements Runnable { diff --git a/desktop-client/pom.xml b/desktop-client/pom.xml new file mode 100644 index 0000000..832dab4 --- /dev/null +++ b/desktop-client/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + org.orinprojects + SecuredChat + 1.0-SNAPSHOT + + + desktop-client + + + 20 + 20 + UTF-8 + + + + + org.openjfx + javafx-controls + 19.0.2.1 + + + org.openjfx + javafx-fxml + 19.0.2.1 + + + org.orinprojects + server + 1.0-SNAPSHOT + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 19 + 19 + + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + hellofx + securedChat + org.orinprojects.desktop.Main + + + + + + \ No newline at end of file diff --git a/desktop-client/src/main/java/module-info.java b/desktop-client/src/main/java/module-info.java new file mode 100644 index 0000000..dbeaaff --- /dev/null +++ b/desktop-client/src/main/java/module-info.java @@ -0,0 +1,9 @@ +module org.orinprojects.desktop { + requires javafx.controls; + requires javafx.fxml; + requires javafx.base; + requires org.orinprojects.server; + + opens org.orinprojects.desktop to javafx.fxml; + exports org.orinprojects.desktop; +} \ No newline at end of file diff --git a/desktop-client/src/main/java/org/orinprojects/desktop/ChatController.java b/desktop-client/src/main/java/org/orinprojects/desktop/ChatController.java new file mode 100644 index 0000000..a375181 --- /dev/null +++ b/desktop-client/src/main/java/org/orinprojects/desktop/ChatController.java @@ -0,0 +1,89 @@ +package org.orinprojects.desktop; + +import javafx.event.ActionEvent; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import org.orinprojects.encryption.EncryptionUtil; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.net.Socket; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import static org.orinprojects.desktop.Main.aesKey; +import static org.orinprojects.desktop.Main.keys; + +public class ChatController { + + + public TextField messageTextInput; + public Button sendMsgBtn; + public Text welcomeMessage; + public Button connectBtn; + public Button disconnectBtn; + public TextField serverIpInput; + public TextField serverPortInput; + public TextField usernameInp; + public VBox messagesBox; + + private Socket socket; + + private ClientThread clientThread; + + public void connect(ActionEvent actionEvent) throws IOException { + if (!serverIpInput.getText().equals("") && !serverPortInput.getText().equals("")) { + socket = new Socket(serverIpInput.getText(), Integer.parseInt(serverPortInput.getText())); + + connectBtn.setDisable(true); + disconnectBtn.setDisable(false); + sendMsgBtn.setDisable(false); + + clientThread = new ClientThread(socket, welcomeMessage, messagesBox); + + Thread thr = new Thread(clientThread); + thr.start(); + + clientThread.out.println("WLC" + usernameInp.getText()); + clientThread.out.flush(); + + clientThread.out.println("RSA" + EncryptionUtil.publicKeyToString(keys.getPublic())); + clientThread.out.flush(); + } + } + + public void disconnect(ActionEvent actionEvent) throws IOException { + clientThread.out.close(); + clientThread.in.close(); + socket.close(); + + connectBtn.setDisable(false); + disconnectBtn.setDisable(true); + sendMsgBtn.setDisable(true); + } + + public void sendMessage(ActionEvent actionEvent) throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, InvalidKeyException { + if (!clientThread.aesReceived && !clientThread.rsaReceived) + System.out.println("Wait for complete initialisation!"); + + if (clientThread.rsaReceived && clientThread.aesReceived) { + String encryptedText = EncryptionUtil.encryptWithAES(messageTextInput.getText(), aesKey); + clientThread.out.println("TXT" + encryptedText); + clientThread.out.flush(); + } + + Label text = new Label(messageTextInput.getText()); + text.setFont(new Font(14)); + text.setPadding(new Insets(0, 0, 5, 5)); + messagesBox.getChildren().add(text); + + messageTextInput.setText(""); + } +} diff --git a/desktop-client/src/main/java/org/orinprojects/desktop/ClientThread.java b/desktop-client/src/main/java/org/orinprojects/desktop/ClientThread.java new file mode 100644 index 0000000..e6c6b67 --- /dev/null +++ b/desktop-client/src/main/java/org/orinprojects/desktop/ClientThread.java @@ -0,0 +1,101 @@ +package org.orinprojects.desktop; + +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import org.orinprojects.encryption.EncryptionUtil; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +public class ClientThread implements Runnable { + + private final Socket clientSocket; + + final BufferedReader in; + + final PrintWriter out; + + public boolean rsaReceived = false; + + public boolean aesReceived = false; + + public Text messageExample; + + public VBox messagesBox; + + public ClientThread(Socket socket, Text messageExample, VBox messagesBox) throws IOException { + this.clientSocket = socket; + this.messageExample = messageExample; + this.messagesBox = messagesBox; + this.in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + this.out = new PrintWriter(clientSocket.getOutputStream()); + } + + @Override + public void run() { + while (clientSocket.isConnected()) { + try { + String receivedMessage = in.readLine(); + String prefix = receivedMessage.substring(0, 3); + String restMessage = receivedMessage.substring(3); + + if (prefix.equals("RSA") && !rsaReceived) { + Main.serverPublicRSA = EncryptionUtil.stringToPublicKey(restMessage); + rsaReceived = true; + continue; + } + + if (prefix.equals("AES") && !aesReceived) { + String decryptedAES = EncryptionUtil.decryptWithRSA(restMessage, Main.keys.getPrivate()); + Main.aesKey = EncryptionUtil.aesKeyFromString(decryptedAES); + aesReceived = true; + continue; + } + + if (prefix.equals("TXT") && aesReceived && rsaReceived) { + String decryptedMessage = EncryptionUtil.decryptWithAES(restMessage, Main.aesKey); + + Label text = new Label(decryptedMessage); + text.setFont(new Font(14)); + text.setPadding(new Insets(0, 0, 5, 5)); + + Platform.runLater(() -> messagesBox.getChildren().add(text)); + } + } catch (NoSuchPaddingException | IllegalBlockSizeException | IOException | NoSuchAlgorithmException | + InvalidKeySpecException | BadPaddingException |InvalidKeyException e) { + System.err.println("Disconnected from server!"); + System.exit(-1); + closeAllConnections(clientSocket, in, out); + } + } + } + + private void closeAllConnections(Socket socket, BufferedReader in, PrintWriter out) { + try { + if (socket != null) + socket.close(); + + if (in != null) + in.close(); + + if (out != null) + out.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/desktop-client/src/main/java/org/orinprojects/desktop/Main.java b/desktop-client/src/main/java/org/orinprojects/desktop/Main.java new file mode 100644 index 0000000..741626b --- /dev/null +++ b/desktop-client/src/main/java/org/orinprojects/desktop/Main.java @@ -0,0 +1,46 @@ +package org.orinprojects.desktop; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.orinprojects.encryption.EncryptionUtil; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; + +/** + * JavaFX App + */ +public class Main extends Application { + + public static KeyPair keys; + + public static PublicKey serverPublicRSA; + + public static SecretKey aesKey; + + @Override + public void start(Stage primaryStage) throws IOException, NoSuchAlgorithmException { + keys = EncryptionUtil.generateRSAKeys(); + + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(getClass().getResource("/chat.fxml")); + + Parent root = loader.load(); + + primaryStage.setTitle("Chat App"); + primaryStage.setResizable(false); + primaryStage.setScene(new Scene(root)); + primaryStage.show(); + } + + public static void main(String[] args) { + launch(); + } + +} \ No newline at end of file diff --git a/desktop-client/src/main/resources/chat.fxml b/desktop-client/src/main/resources/chat.fxml new file mode 100644 index 0000000..0337c61 --- /dev/null +++ b/desktop-client/src/main/resources/chat.fxml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+
+ + diff --git a/pom.xml b/pom.xml index 27e1179..75634ff 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ server client + desktop-client diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java new file mode 100644 index 0000000..e2579e4 --- /dev/null +++ b/server/src/main/java/module-info.java @@ -0,0 +1,3 @@ +module org.orinprojects.server { + exports org.orinprojects.encryption; +} \ No newline at end of file diff --git a/server/src/main/java/org/orinprojects/ClientHandler.java b/server/src/main/java/org/orinprojects/ClientHandler.java index e475ddd..dd2eaf6 100644 --- a/server/src/main/java/org/orinprojects/ClientHandler.java +++ b/server/src/main/java/org/orinprojects/ClientHandler.java @@ -5,15 +5,14 @@ import org.orinprojects.encryption.EncryptionUtil; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; import java.net.Socket; -import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; -import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; public class ClientHandler implements Runnable { @@ -64,6 +63,12 @@ public class ClientHandler implements Runnable { if (prefix.equals("WLC")) { this.username = restMessage; + //TODO: add better logic + if (Server.clientKeys.get(username) != null) { + in.close(); + out.close(); + clientSocket.close(); + } continue; }