* added desktop client

Signed-off-by: CubeBit <denis-seredenko@ukr.net>
This commit is contained in:
2023-08-02 12:14:50 +02:00
parent 7508801049
commit 251532493f
11 changed files with 438 additions and 10 deletions

View File

@@ -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 {

View File

@@ -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 {

62
desktop-client/pom.xml Normal file
View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.orinprojects</groupId>
<artifactId>SecuredChat</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>desktop-client</artifactId>
<properties>
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>19.0.2.1</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>19.0.2.1</version>
</dependency>
<dependency>
<groupId>org.orinprojects</groupId>
<artifactId>server</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>19</source>
<target>19</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<jlinkImageName>hellofx</jlinkImageName>
<launcher>securedChat</launcher>
<mainClass>org.orinprojects.desktop.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -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;
}

View File

@@ -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("");
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.orinprojects.desktop.ChatController">
<right>
<VBox style="-fx-background-color: #393E46;" BorderPane.alignment="CENTER">
<children>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="79.0" minWidth="10.0" prefWidth="61.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="145.0" minWidth="10.0" prefWidth="124.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Text fill="#eeeeee" strokeType="OUTSIDE" strokeWidth="0.0" text="IP:" GridPane.rowIndex="2" />
<TextField fx:id="serverIpInput" promptText="127.0.0.1" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Text fill="#eeeeee" strokeType="OUTSIDE" strokeWidth="0.0" text="Port:" GridPane.rowIndex="3" />
<TextField fx:id="serverPortInput" promptText="6666" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Text fill="#eeeeee" strokeType="OUTSIDE" strokeWidth="0.0" text="Username:" GridPane.rowIndex="1" />
<TextField fx:id="usernameInp" text="anonym" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
<Button fx:id="connectBtn" mnemonicParsing="false" onAction="#connect" prefHeight="40.0" prefWidth="185.0" style="-fx-background-color: #00ADB5;" text="Connect" textFill="WHITE">
<VBox.margin>
<Insets bottom="10.0" top="10.0" />
</VBox.margin>
</Button>
<Button fx:id="disconnectBtn" disable="true" mnemonicParsing="false" onAction="#disconnect" prefHeight="42.0" prefWidth="185.0" style="-fx-background-color: #d9534f;" text="Disconnect" textFill="WHITE" />
</children>
<BorderPane.margin>
<Insets />
</BorderPane.margin>
<padding>
<Insets right="10.0" />
</padding>
</VBox>
</right>
<bottom>
<GridPane prefHeight="58.0" prefWidth="601.0" style="-fx-background-color: #222831;" BorderPane.alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Text fill="#eeeeee" strokeType="OUTSIDE" strokeWidth="0.0" text="Made by Denys Seredenko" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
<font>
<Font size="14.0" />
</font>
</Text>
</children>
</GridPane>
</bottom>
<center>
<BorderPane prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: #393E46;" BorderPane.alignment="CENTER">
<bottom>
<HBox BorderPane.alignment="CENTER">
<children>
<TextField fx:id="messageTextInput" prefHeight="32.0" prefWidth="303.0" promptText="Start typing" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets bottom="20.0" left="10.0" />
</HBox.margin>
</TextField>
<Button fx:id="sendMsgBtn" disable="true" mnemonicParsing="false" onAction="#sendMessage" prefHeight="32.0" prefWidth="112.0" text="Send">
<HBox.margin>
<Insets left="10.0" right="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<ScrollPane prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: #eeeeee;" vvalue="1.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</BorderPane.margin>
<content>
<VBox fx:id="messagesBox">
<children>
<Text fx:id="welcomeMessage" strokeType="OUTSIDE" strokeWidth="0.0" text="Welcome to chatting application!">
<font>
<Font size="14.0" />
</font>
<VBox.margin>
<Insets bottom="5.0" left="5.0" />
</VBox.margin>
</Text>
</children>
</VBox>
</content>
</ScrollPane>
</center>
</BorderPane>
</center>
</BorderPane>

View File

@@ -11,6 +11,7 @@
<modules>
<module>server</module>
<module>client</module>
<module>desktop-client</module>
</modules>
<repositories>

View File

@@ -0,0 +1,3 @@
module org.orinprojects.server {
exports org.orinprojects.encryption;
}

View File

@@ -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;
}