ソースを参照

Save 06-10-2025 Working Chat, next step is implementing encryption, direct communication and tests

Jan 4 ヶ月 前
コミット
2f960cec80

+ 50 - 0
src/main/java/config/Config.java

@@ -0,0 +1,50 @@
+package config;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class Config {
+    public static boolean ENCRYPT_ON_SAVE = true;
+    public static boolean USE_RELAY = true;
+    public static String RELAY_SERVER_IP = "192.168.2.177"; // Default IP
+    public static int RELAY_SERVER_PORT = 12345;
+    public static String APPEARANCE_NAME = "Jan";
+    // Method to save configuration to file (optional)
+    public static void saveConfig() {
+        // Implementation to save settings to a properties file or database
+        Properties props = new Properties();
+        props.setProperty("RELAY_SERVER_IP", RELAY_SERVER_IP);
+        props.setProperty("RELAY_SERVER_PORT", String.valueOf(RELAY_SERVER_PORT));
+        props.setProperty("ENCRYPT_ON_SAVE", String.valueOf(ENCRYPT_ON_SAVE));
+        props.setProperty("APPEARANCE_NAME", APPEARANCE_NAME);
+        props.setProperty("USE_RELAY", String.valueOf(USE_RELAY));
+
+        try (FileOutputStream out = new FileOutputStream("config.properties")) {
+            props.store(out, "Chat Application Configuration");
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // Method to load configuration from file (optional)
+    public static void loadConfig() {
+        try {
+            Properties props = new Properties();
+            try (FileInputStream in = new FileInputStream("config.properties")) {
+                props.load(in);
+                RELAY_SERVER_IP = props.getProperty("RELAY_SERVER_IP", "192.168.2.177");
+                RELAY_SERVER_PORT = Integer.parseInt(props.getProperty("RELAY_SERVER_PORT", "12345"));
+                ENCRYPT_ON_SAVE = Boolean.parseBoolean(props.getProperty("ENCRYPT_ON_SAVE", "true"));
+                APPEARANCE_NAME = props.getProperty("APPEARANCE_NAME", "Jan");
+                USE_RELAY = Boolean.parseBoolean(props.getProperty("USE_RELAY", "true"));
+            }
+        } catch (IOException e) {
+            System.err.println("Could not load configuration, using defaults: " + e.getMessage());
+        }
+    }
+}

+ 25 - 0
src/main/java/crypto/CryptoUtils.java

@@ -0,0 +1,25 @@
+package crypto;
+
+import javax.crypto.*;
+import java.security.*;
+import java.util.Base64;
+
+public class CryptoUtils {
+    public static KeyPair generateKeyPair() throws Exception {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        return keyGen.generateKeyPair();
+    }
+
+    public static String encrypt(String data, PublicKey key) throws Exception {
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
+    }
+
+    public static String decrypt(String data, PrivateKey key) throws Exception {
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, key);
+        return new String(cipher.doFinal(Base64.getDecoder().decode(data)));
+    }
+}

+ 25 - 0
src/main/java/crypto/KeyManager.java

@@ -0,0 +1,25 @@
+package crypto;
+
+import java.security.*;
+import java.util.UUID;
+
+public class KeyManager {
+    private static KeyPair keyPair;
+    private static UUID myUUID;
+    public static void init() throws Exception {
+        keyPair = CryptoUtils.generateKeyPair();
+        myUUID = UUID.randomUUID();
+    }
+
+    public static PublicKey getPublicKey() {
+        return keyPair.getPublic();
+    }
+
+    public static PrivateKey getPrivateKey() {
+        return keyPair.getPrivate();
+    }
+
+    public static UUID getMyUUID() {
+        return myUUID;
+    }
+}

+ 9 - 0
src/main/java/main/Main.java

@@ -0,0 +1,9 @@
+package main;
+
+public class Main {
+    public static void main(String[] args) {
+        javax.swing.SwingUtilities.invokeLater(() -> {
+            RoleSelector.selectRole();
+        });
+    }
+}

+ 26 - 0
src/main/java/main/RoleSelector.java

@@ -0,0 +1,26 @@
+package main;
+
+import javax.swing.*;
+
+public class RoleSelector {
+    public static void selectRole() {
+        String[] options = {"Relay Server starten", "Als Benutzer starten"};
+        int choice = JOptionPane.showOptionDialog(
+                null,
+                "Welche Rolle willst du einnehmen?",
+                "Chat App Start",
+                JOptionPane.DEFAULT_OPTION,
+                JOptionPane.QUESTION_MESSAGE,
+                null,
+                options,
+                options[1]
+        );
+
+        if (choice == 0) {
+            new net.RelayServer().start();
+            JOptionPane.showMessageDialog(null, "Relay Server läuft jetzt auf Port 12345.");
+        } else {
+            new ui.MainWindow();
+        }
+    }
+}

+ 21 - 0
src/main/java/model/AuthMessage.java

@@ -0,0 +1,21 @@
+package model;
+
+import java.io.Serializable;
+
+public class AuthMessage implements Serializable {
+    private final String name;
+    private final String uuid;
+
+    public AuthMessage(String UUID, String appearanceName){
+        this.uuid = UUID;
+        this.name = appearanceName;
+    }
+
+    public String getUuid() {
+        return uuid;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 37 - 0
src/main/java/model/Chat.java

@@ -0,0 +1,37 @@
+package model;
+
+import crypto.KeyManager;
+
+import java.util.*;
+
+public class Chat {
+    private String targetUUID;
+    private List<Message> messages = new ArrayList<>();
+    private boolean isUsingRelay;
+    private String relayAddress;
+
+    public Chat(boolean useRelay, String addressOrIp, String targetUUID) {
+        this.targetUUID = targetUUID;
+        this.isUsingRelay = useRelay;
+        this.relayAddress = addressOrIp;
+    }
+
+    public List<Message> getMessages(){
+        return messages;
+    }
+    public void addMessage(Message msg) {
+        messages.add(msg);
+    }
+
+    public boolean isUsingRelay() {
+        return isUsingRelay;
+    }
+
+    public String getRelayAddress() {
+        return relayAddress;
+    }
+
+    public String getTargetUUID() {
+        return targetUUID.toString();
+    }
+}

+ 60 - 0
src/main/java/model/Message.java

@@ -0,0 +1,60 @@
+package model;
+
+import crypto.CryptoUtils;
+
+import javax.crypto.Cipher;
+import java.io.Serializable;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Base64;
+
+public class Message implements Serializable {
+    private String receiverUUID;
+    private PublicKey receiverKey;
+
+    private String senderId;
+    private PublicKey senderKey;
+
+    private long timestamp;
+    private String content;
+    private boolean encrypted = false;
+
+
+    public Message(String senderUUID, String receiverUUID, String content) {
+        this.senderId = senderUUID;
+        this.content = content;
+        this.receiverUUID = receiverUUID;
+        this.timestamp = System.currentTimeMillis();
+    }
+    public Message(PublicKey senderKey, PublicKey receiverKey, String content) {
+        this.senderKey = senderKey;
+        this.content = content;
+        this.receiverKey = receiverKey;
+        this.timestamp = System.currentTimeMillis();
+    }
+
+    public void encrypt(PublicKey key) throws Exception {
+        if(!encrypted) {
+            this.content = CryptoUtils.encrypt(content, key);
+            encrypted = true;
+        }
+    }
+
+    public void decrypt(PrivateKey key) throws Exception {
+        if(encrypted){
+            this.content = CryptoUtils.decrypt(content, key);
+            encrypted = false;
+        }
+    }
+
+    public String getReceiverUUID(){
+        return receiverUUID;
+    }
+
+    public String getSenderUUID(){
+        return senderId;
+    }
+    public String getContent(){
+        return content;
+    }
+}

+ 54 - 0
src/main/java/net/RelayClient.java

@@ -0,0 +1,54 @@
+package net;
+
+import config.Config;
+import model.AuthMessage;
+import model.Message;
+
+import java.io.*;
+import java.net.*;
+
+public class RelayClient {
+    private Socket socket;
+    private ObjectOutputStream out; // Changed from PrintWriter
+    private ObjectInputStream in;
+
+    public RelayClient(String serverIp, String myId, MessageReceiver receiver) throws IOException {
+        socket = new Socket(serverIp, 12345);
+        out = new ObjectOutputStream(socket.getOutputStream()); // Direct ObjectOutputStream
+        in = new ObjectInputStream(socket.getInputStream());
+
+        AuthMessage auth = new AuthMessage(myId, Config.APPEARANCE_NAME);
+        System.out.println("Sending auth: " + auth);
+        send(auth);
+
+        new Thread(() -> {
+            try {
+                Object msg;
+                while ((msg = in.readObject()) != null) {
+                    if(msg instanceof Message){
+                        receiver.onMessage((Message) msg);
+                    }
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+        }).start();
+    }
+
+    public void send(Object msg) {
+        try {
+            out.writeObject(msg);
+            out.flush(); // Important: flush after writing
+            System.out.println("Sent: " + msg);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public interface MessageReceiver {
+        void onMessage(Message msg);
+    }
+}
+

+ 34 - 0
src/main/java/net/RelayHandler.java

@@ -0,0 +1,34 @@
+package net;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class RelayHandler implements Runnable {
+    private final Socket socket;
+    private final Map<String, Socket> clients;
+
+    public RelayHandler(Socket socket, Map<String, Socket> clients) {
+        this.socket = socket;
+        this.clients = clients;
+    }
+
+    public void run() {
+        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
+            String userId = in.readLine();
+            clients.put(userId, socket);
+
+            String targetId;
+            while ((targetId = in.readLine()) != null) {
+                String msg = in.readLine();
+                Socket target = clients.get(targetId);
+                if (target != null) {
+                    PrintWriter out = new PrintWriter(target.getOutputStream(), true);
+                    out.println(msg);
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 63 - 0
src/main/java/net/RelayServer.java

@@ -0,0 +1,63 @@
+package net;
+
+import model.AuthMessage;
+import model.Message;
+
+import java.net.*;
+import java.io.*;
+import java.util.concurrent.*;
+
+public class RelayServer extends Thread {
+    private final ConcurrentHashMap<String, ObjectOutputStream> clients = new ConcurrentHashMap<>(); // Store ObjectOutputStream instead of Socket
+    public void run() {
+        try (ServerSocket server = new ServerSocket(12345)) {
+            System.out.println("Relay Server läuft auf Port 12345...");
+            while (true) {
+                Socket socket = server.accept();
+                new Thread(() -> handleClient(socket)).start();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void handleClient(Socket socket) {
+        try {
+            System.out.println("New connection from: " + socket.getInetAddress());
+            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
+            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); // Create ObjectOutputStream for responses
+
+            Object input = in.readObject();
+
+
+            if(input instanceof AuthMessage){
+                AuthMessage auth = (AuthMessage) input;
+                String clientId = auth.getUuid();
+                String clientName = auth.getName();
+                clients.put(clientId, out); // Store the output stream
+                System.out.println("Received auth for: " + clientName + ",\n with UUID: " + clientId);
+
+                Object msg;
+                while ((msg = in.readObject()) != null) {
+                    System.out.println("Received message: " + msg);
+                    if(msg instanceof Message){
+                        String receiverId = ((Message) msg).getReceiverUUID();
+                        ObjectOutputStream targetOut = clients.get(receiverId);
+                        if (targetOut != null) {
+                            targetOut.writeObject(msg);
+                            targetOut.flush();
+                            System.out.println("Relayed message from " + clientId + " to " + receiverId + ": " + ((Message) msg).getContent());
+                        } else {
+                            System.out.println("Target client not found: " + receiverId);
+                        }
+                    }
+                }
+            }
+        } catch (IOException e) {
+            System.out.println("Disconnected client!");
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+}
+

+ 19 - 0
src/main/java/net/direct/DirectClient.java

@@ -0,0 +1,19 @@
+package net.direct;
+
+
+import java.io.*;
+import java.net.*;
+
+public class DirectClient {
+    private final Socket socket;
+    private final PrintWriter out;
+
+    public DirectClient(String ip) throws IOException {
+        socket = new Socket(ip, 9876);
+        out = new PrintWriter(socket.getOutputStream(), true);
+    }
+
+    public void send(String msg) {
+        out.println(msg);
+    }
+}

+ 35 - 0
src/main/java/net/direct/DirectServer.java

@@ -0,0 +1,35 @@
+package net.direct;
+
+import java.io.*;
+import java.net.*;
+import ui.ChatWindow;
+
+public class DirectServer extends Thread {
+    private final ChatWindow chatWindow;
+
+    public DirectServer(ChatWindow window) {
+        this.chatWindow = window;
+    }
+
+    public void run() {
+        try (ServerSocket server = new ServerSocket(9876)) {
+            while (true) {
+                Socket client = server.accept();
+                new Thread(() -> handleClient(client)).start();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void handleClient(Socket socket) {
+        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
+            String msg;
+            while ((msg = in.readLine()) != null) {
+                //chatWindow.receiveMessage("Peer: " + msg);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 28 - 0
src/main/java/storage/SecureStorage.java

@@ -0,0 +1,28 @@
+package storage;
+
+import model.Chat;
+import java.io.*;
+import java.nio.file.*;
+import java.security.*;
+import javax.crypto.*;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.*;
+
+public class SecureStorage {
+    public static void saveChats(List<Chat> chats, String password) throws Exception {
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        ObjectOutputStream objOut = new ObjectOutputStream(byteStream);
+        objOut.writeObject(chats);
+        objOut.close();
+
+        byte[] data = byteStream.toByteArray();
+        byte[] key = Arrays.copyOf(password.getBytes(), 16); // AES 128-bit
+        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
+
+        Cipher cipher = Cipher.getInstance("AES");
+        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+        byte[] encrypted = cipher.doFinal(data);
+
+        Files.write(Paths.get("chats.dat"), encrypted);
+    }
+}

+ 112 - 0
src/main/java/ui/ChatWindow.java

@@ -0,0 +1,112 @@
+package ui;
+
+import crypto.KeyManager;
+import model.AuthMessage;
+import model.Chat;
+import model.Message;
+import net.RelayClient;
+import net.direct.DirectClient;
+import net.direct.DirectServer;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+public class ChatWindow extends JFrame implements RelayClient.MessageReceiver {
+    private String targetName;
+    private JTextArea chatArea;
+    private JTextField inputField;
+    private boolean useRelay;
+    private String targetUUID;
+    private DirectClient directClient;
+    private RelayClient relayClient;
+
+    public ChatWindow(String targetName, Chat chat) {
+        this.useRelay = chat.isUsingRelay();
+        this.targetUUID = chat.getTargetUUID();
+        this.targetName = targetName;
+        setTitle("Chat mit " + targetName);
+        setSize(400, 500);
+        setLocationRelativeTo(null);
+        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+
+        chatArea = new JTextArea();
+        chatArea.setEditable(false);
+        inputField = new JTextField();
+        JButton sendBtn = new JButton("Senden");
+        JPanel inputPanel = new JPanel(new BorderLayout());
+        inputPanel.add(inputField, BorderLayout.CENTER);
+        inputPanel.add(sendBtn, BorderLayout.EAST);
+        add(new JScrollPane(chatArea), BorderLayout.CENTER);
+        add(inputPanel, BorderLayout.SOUTH);
+        setVisible(true);
+
+        setupMessages(chat);
+        // Create the send action that can be reused
+        Runnable sendAction = () -> {
+            String msg = inputField.getText();
+            if (!msg.isEmpty()) {
+                inputField.setText("");
+                chatArea.append("Du: " + msg + "\n");
+                Message toSend = new Message(KeyManager.getMyUUID().toString(), targetUUID, msg);
+
+                chat.addMessage(toSend);
+                if (useRelay) {
+                    relayClient.send(toSend);
+                } else {
+                    directClient.send(msg);
+                }
+            }
+        };
+
+        sendBtn.addActionListener(e -> sendAction.run());
+
+        inputField.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed(KeyEvent e) {
+                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+                    sendAction.run();
+                }
+            }
+        });
+
+        if (useRelay) {
+            try {
+                relayClient = new RelayClient(chat.getRelayAddress(), KeyManager.getMyUUID().toString(), this);
+            } catch (Exception e) {
+                e.printStackTrace();
+                JOptionPane.showMessageDialog(this, "Verbindung zum Relay-Server fehlgeschlagen: " + e.getMessage(),
+                        "Verbindungsfehler", JOptionPane.ERROR_MESSAGE);
+            }
+        } else {
+            try {
+                directClient = new DirectClient(chat.getRelayAddress());
+                new DirectServer(this).start(); // Für eingehende Nachrichten
+            } catch (Exception e) {
+                e.printStackTrace();
+                JOptionPane.showMessageDialog(this, "Direkte Verbindung fehlgeschlagen: " + e.getMessage(),
+                        "Verbindungsfehler", JOptionPane.ERROR_MESSAGE);
+            }
+        }
+    }
+
+    private void setupMessages(Chat chat) {
+        for(Message msg : chat.getMessages()){
+            if(msg.getSenderUUID().equals(targetUUID)){
+                chatArea.append(targetName + ": " + msg.getContent() + "\n");
+            }else if(msg.getReceiverUUID().equals(targetUUID)){
+                chatArea.append("Du: " + msg.getContent() + "\n");
+            }else{
+                chatArea.append("Something went wrong whilst loading this message! \n");
+            }
+        }
+    }
+
+    @Override
+    public void onMessage(Message msg) {
+        //System.out.println("Empfangen: " + msg.getContent());
+        //String username = msg.getSenderUUID();
+        SwingUtilities.invokeLater(() -> chatArea.append(targetName + ": " + msg.getContent() + "\n"));
+    }
+}

+ 115 - 0
src/main/java/ui/EditChatWindow.java

@@ -0,0 +1,115 @@
+package ui;
+
+import model.Chat;
+import model.Message;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.function.BiConsumer;
+
+public class EditChatWindow extends JDialog {
+    private String originalName;
+    private Chat originalChat;
+    private BiConsumer<String, Chat> onSave;
+
+    public EditChatWindow(JFrame parent, String currentName, Chat currentChat, BiConsumer<String, Chat> onSave) {
+        super(parent, "Chat bearbeiten", true);
+        this.originalName = currentName;
+        this.originalChat = currentChat;
+        this.onSave = onSave;
+
+        setSize(400, 300);
+        setLocationRelativeTo(parent);
+        setLayout(new GridLayout(7, 2, 5, 5));
+
+        // Create form fields with current values
+        JTextField nameField = new JTextField(currentName);
+        JTextField uuidField = new JTextField(currentChat.getTargetUUID());
+        JTextField relayAddressField = new JTextField(currentChat.getRelayAddress());
+        JCheckBox useRelayCheckbox = new JCheckBox("Relay verwenden", currentChat.isUsingRelay());
+
+        // Enable/disable relay address field based on checkbox
+        relayAddressField.setEnabled(useRelayCheckbox.isSelected());
+        useRelayCheckbox.addActionListener(e -> {
+            relayAddressField.setEnabled(useRelayCheckbox.isSelected());
+        });
+
+        JButton saveBtn = new JButton("Speichern");
+        JButton cancelBtn = new JButton("Abbrechen");
+        JButton deleteBtn = new JButton("Chat löschen");
+
+        // Add components to dialog
+        add(new JLabel("Chat-Name:"));
+        add(nameField);
+
+        add(new JLabel("Ziel-UUID:"));
+        add(uuidField);
+
+        add(new JLabel("Relay verwenden:"));
+        add(useRelayCheckbox);
+
+        add(new JLabel("Relay Server Adresse:"));
+        add(relayAddressField);
+
+        add(saveBtn);
+        add(cancelBtn);
+
+        add(deleteBtn);
+        add(new JLabel()); // Empty cell
+
+        // Save button action
+        saveBtn.addActionListener(e -> {
+            String newName = nameField.getText().trim();
+            String uuid = uuidField.getText().trim();
+            String relayAddress = relayAddressField.getText().trim();
+            boolean useRelay = useRelayCheckbox.isSelected();
+
+            if (newName.isEmpty() || uuid.isEmpty()) {
+                JOptionPane.showMessageDialog(this, "Chat-Name und UUID dürfen nicht leer sein!", "Fehler", JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+
+            if (useRelay && relayAddress.isEmpty()) {
+                JOptionPane.showMessageDialog(this, "Bitte geben Sie eine Relay Server Adresse ein!", "Fehler", JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+
+            // Create updated chat object
+            Chat updatedChat = new Chat(useRelay, relayAddress, uuid);
+
+            for(Message msg : originalChat.getMessages()){
+                updatedChat.addMessage(msg);
+            }
+
+            // Call the save callback
+            onSave.accept(newName, updatedChat);
+            dispose();
+        });
+
+        // Cancel button action
+        cancelBtn.addActionListener(e -> {
+            dispose();
+        });
+
+        // Delete button action
+        deleteBtn.addActionListener(e -> {
+            int result = JOptionPane.showConfirmDialog(
+                    this,
+                    "Möchten Sie den Chat '" + originalName + "' wirklich löschen?",
+                    "Chat löschen",
+                    JOptionPane.YES_NO_OPTION,
+                    JOptionPane.WARNING_MESSAGE
+            );
+
+            if (result == JOptionPane.YES_OPTION) {
+                // Pass null to indicate deletion
+                onSave.accept(originalName, null);
+                dispose();
+            }
+        });
+
+        // Add some padding
+        ((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+        setVisible(true);
+    }
+}

+ 219 - 0
src/main/java/ui/MainWindow.java

@@ -0,0 +1,219 @@
+package ui;
+
+import config.Config;
+import crypto.KeyManager;
+import model.AuthMessage;
+import model.Chat;
+import model.Message;
+import net.RelayClient;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.util.HashMap;
+
+public class MainWindow extends JFrame implements RelayClient.MessageReceiver {
+    private HashMap<String, Chat> chatHashMap = new HashMap<>(); //Name und Chat
+    private DefaultListModel<String> chatListModel = new DefaultListModel<>();
+    private JPanel mainPanel = new JPanel(new BorderLayout());
+    private JList<String> chatList = new JList<>(chatListModel);
+    private JScrollPane chatScrollPane = new JScrollPane(chatList);
+    private RelayClient relayClient;
+
+    public MainWindow() {
+        try {
+            KeyManager.init();
+        } catch (Exception e) {
+            System.out.println("KeyManager error! Please try again.");
+            e.printStackTrace();
+        }
+        Config.loadConfig();
+
+        initializeWindow();
+        setupUI();
+        setupEventListeners();
+
+        setupRelayConnection();
+
+        setVisible(true);
+    }
+
+    private void setupRelayConnection() {
+        if(Config.USE_RELAY){
+            try {
+                this.relayClient = new RelayClient(Config.RELAY_SERVER_IP, KeyManager.getMyUUID().toString(), this);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void initializeWindow() {
+        setTitle("Chat App");
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setSize(500, 600); // Increased width to accommodate buttons
+        setLocationRelativeTo(null);
+        add(mainPanel);
+    }
+
+    private void setupUI() {
+        // Clear and setup main panel
+        mainPanel.removeAll();
+
+        // Create a panel for the chat list with buttons
+        JPanel chatListPanel = new JPanel(new BorderLayout());
+
+        // Add chat list with scroll pane
+        chatListPanel.add(chatScrollPane, BorderLayout.CENTER);
+
+        // Add instruction label
+        JLabel instructionLabel = new JLabel("Doppelklick zum Oeffnen, Rechtsklick fuer Optionen");
+        instructionLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        instructionLabel.setForeground(Color.GRAY);
+        chatListPanel.add(instructionLabel, BorderLayout.NORTH);
+
+        mainPanel.add(chatListPanel, BorderLayout.CENTER);
+
+        // Create and add button panel
+        JPanel buttonPanel = createButtonPanel();
+        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+
+        // Refresh UI
+        mainPanel.revalidate();
+        mainPanel.repaint();
+    }
+
+    private JPanel createButtonPanel() {
+        JPanel buttonPanel = new JPanel();
+        JButton newChatBtn = new JButton("Neuer Chat");
+        JButton settingsBtn = new JButton("Einstellungen");
+
+        buttonPanel.add(newChatBtn);
+        buttonPanel.add(settingsBtn);
+
+        return buttonPanel;
+    }
+
+    private void setupEventListeners() {
+        // New Chat Button
+        JButton newChatBtn = (JButton) ((JPanel) mainPanel.getComponent(1)).getComponent(0);
+        newChatBtn.addActionListener(e -> {
+            NewChatDialog dialog = new NewChatDialog(this, (name, chat) -> {
+                chatHashMap.put(name, chat);
+                updateChatList();
+                new ChatWindow(name, chat);
+            });
+            dialog.setVisible(true);
+        });
+
+        // Settings Button
+        JButton settingsBtn = (JButton) ((JPanel) mainPanel.getComponent(1)).getComponent(1);
+        settingsBtn.addActionListener(e -> new SettingsDialog(this).setVisible(true));
+
+        // Chat List Double-Click
+        chatList.addMouseListener(new MouseAdapter() {
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) {
+                    String selected = chatList.getSelectedValue();
+                    if (selected != null) {
+                        Chat selectedChat = chatHashMap.get(selected);
+                        new ChatWindow(selected, selectedChat);
+                    }
+                }
+            }
+        });
+
+        // Add right-click context menu for chat list
+        JPopupMenu contextMenu = createChatContextMenu();
+        chatList.setComponentPopupMenu(contextMenu);
+    }
+
+    private JPopupMenu createChatContextMenu() {
+        JPopupMenu menu = new JPopupMenu();
+
+        JMenuItem openItem = new JMenuItem("Chat öffnen");
+        JMenuItem editItem = new JMenuItem("Eigenschaften bearbeiten");
+        JMenuItem deleteItem = new JMenuItem("Chat löschen");
+
+        openItem.addActionListener(e -> {
+            String selected = chatList.getSelectedValue();
+            if (selected != null) {
+                Chat selectedChat = chatHashMap.get(selected);
+                new ChatWindow(selected, selectedChat);
+            }
+        });
+
+        editItem.addActionListener(e -> {
+            String selected = chatList.getSelectedValue();
+            if (selected != null) {
+                Chat selectedChat = chatHashMap.get(selected);
+                new EditChatWindow(this, selected, selectedChat, (newName, updatedChat) -> {
+                    // Remove old entry and add updated one
+                    chatHashMap.remove(selected);
+                    chatHashMap.put(newName, updatedChat);
+                    updateChatList();
+                });
+            }
+        });
+
+        deleteItem.addActionListener(e -> {
+            String selected = chatList.getSelectedValue();
+            if (selected != null) {
+                int result = JOptionPane.showConfirmDialog(
+                        this,
+                        "Möchten Sie den Chat '" + selected + "' wirklich löschen?",
+                        "Chat löschen",
+                        JOptionPane.YES_NO_OPTION
+                );
+                if (result == JOptionPane.YES_OPTION) {
+                    chatHashMap.remove(selected);
+                    updateChatList();
+                }
+            }
+        });
+
+        menu.add(openItem);
+        menu.add(editItem);
+        menu.add(deleteItem);
+
+        return menu;
+    }
+
+    private void updateChatList() {
+        // Clear and repopulate the list model
+        chatListModel.clear();
+
+        for (String chatName : chatHashMap.keySet()) {
+            chatListModel.addElement(chatName);
+        }
+
+        // The JList will automatically update since it uses the same model
+        // Force UI refresh
+        chatList.revalidate();
+        chatList.repaint();
+        printChatList();
+    }
+
+    // Optional: Method to get the current chat list for debugging
+    public void printChatList() {
+        System.out.println("Current chats: " + chatHashMap.keySet());
+    }
+
+    @Override
+    public void onMessage(Message msg) {
+        String senderUUID = msg.getSenderUUID();
+
+        if(chatHashMap.get(senderUUID) != null){
+
+            System.out.println("Known UUID");
+            chatHashMap.get(senderUUID).addMessage(msg);
+        }else{
+            Chat newChat = new Chat(true, Config.RELAY_SERVER_IP, msg.getSenderUUID());
+            newChat.addMessage(msg);
+            chatHashMap.put(senderUUID, newChat);
+            updateChatList();
+        }
+    }
+}

+ 53 - 0
src/main/java/ui/NewChatDialog.java

@@ -0,0 +1,53 @@
+package ui;
+
+import crypto.KeyManager;
+import model.Chat;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.function.BiConsumer;
+
+public class NewChatDialog extends JDialog {
+    public NewChatDialog(JFrame parent, BiConsumer<String, Chat> onCreate) {
+        super(parent, "Neuen Chat starten", true);
+        setSize(300, 200);
+        setLocationRelativeTo(parent);
+        JTextField uuidField = new JTextField();
+        //JTextField myIdField = new JTextField();
+        JTextField receiverNameField = new JTextField();
+        JRadioButton relayBtn = new JRadioButton("Relay", true);
+        JRadioButton directBtn = new JRadioButton("Direkt");
+        JTextField relayAddress = new JTextField();
+        relayAddress.setText("192.168.2.177");
+        ButtonGroup group = new ButtonGroup();
+        group.add(relayBtn);
+        group.add(directBtn);
+        JButton startBtn = new JButton("Starten");
+
+        setLayout(new GridLayout(6, 1));
+        add(new JLabel("Empfänger-UUID"));
+        add(uuidField);
+        add(new JLabel("Empfängername"));
+        add(receiverNameField);
+        //add(new JLabel("Deine ID (für Relay):"));
+        //add(myIdField);
+        add(relayBtn);
+        add(directBtn);
+        add(new JLabel("Die Ip des Relay-Servers (Nur wenn Relay ausgewählt)"));
+        add(relayAddress);
+        add(startBtn);
+
+        startBtn.addActionListener(e -> {
+            String uuid = uuidField.getText();
+            String relayAddressText = relayAddress.getText();
+            String name = receiverNameField.getText();
+            if (!relayAddressText.isEmpty() && !uuid.isEmpty() && !name.isEmpty()) {
+                boolean useRelay = relayBtn.isSelected();
+                Chat chat = new Chat(useRelay, relayAddressText, uuid);
+                dispose();
+                onCreate.accept(name, chat);
+
+            }
+        });
+    }
+}

+ 119 - 0
src/main/java/ui/SettingsDialog.java

@@ -0,0 +1,119 @@
+package ui;
+
+import javax.swing.*;
+import config.Config;
+import crypto.KeyManager;
+
+import java.awt.*;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+public class SettingsDialog extends JDialog {
+    public SettingsDialog(JFrame parent) {
+        super(parent, "Einstellungen", true);
+        setSize(350, 250); // Increased size to accommodate new fields
+        setLocationRelativeTo(parent);
+
+        setLayout(new GridLayout(8, 2)); // Increased rows for new fields
+        JTextField UUIDBox = new JTextField();
+        UUIDBox.setText(KeyManager.getMyUUID().toString());
+        UUIDBox.setEditable(false); // UUID should not be editable
+
+        JCheckBox encryptBox = new JCheckBox("Verschlüsselung beim Speichern", Config.ENCRYPT_ON_SAVE);
+        encryptBox.addActionListener(e -> Config.ENCRYPT_ON_SAVE = encryptBox.isSelected());
+
+        // New fields for Relay Server configuration
+        JTextField relayServerIpField = new JTextField(Config.RELAY_SERVER_IP);
+        JTextField relayServerPortField = new JTextField(String.valueOf(Config.RELAY_SERVER_PORT));
+
+        JButton testConnectionBtn = new JButton("Verbindung testen");
+        JLabel connectionStatusLabel = new JLabel("Nicht getestet");
+
+        JButton saveBtn = new JButton("Speichern");
+        JButton cancelBtn = new JButton("Abbrechen");
+
+        // Add components to dialog
+        add(new JLabel("Deine UUID: "));
+        add(UUIDBox);
+
+        add(new JLabel("Relay Server IP:"));
+        add(relayServerIpField);
+
+        add(new JLabel("Relay Server Port:"));
+        add(relayServerPortField);
+
+        add(testConnectionBtn);
+        add(connectionStatusLabel);
+
+        add(encryptBox);
+        add(new JLabel()); // Empty cell for layout
+
+        add(saveBtn);
+        add(cancelBtn);
+
+        // Test connection button action
+        testConnectionBtn.addActionListener(e -> {
+            String ip = relayServerIpField.getText();
+            int port;
+            try {
+                port = Integer.parseInt(relayServerPortField.getText());
+            } catch (NumberFormatException ex) {
+                connectionStatusLabel.setText("Ungültiger Port");
+                connectionStatusLabel.setForeground(Color.RED);
+                return;
+            }
+
+            testRelayConnection(ip, port, connectionStatusLabel);
+        });
+
+        // Save button action
+        saveBtn.addActionListener(e -> {
+            // Save Relay Server configuration
+            Config.RELAY_SERVER_IP = relayServerIpField.getText();
+            try {
+                Config.RELAY_SERVER_PORT = Integer.parseInt(relayServerPortField.getText());
+            } catch (NumberFormatException ex) {
+                JOptionPane.showMessageDialog(this, "Ungültiger Port!", "Fehler", JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+
+            // Save other settings
+            Config.ENCRYPT_ON_SAVE = encryptBox.isSelected();
+
+            // Save to persistent storage if you have that functionality
+            Config.saveConfig();
+
+            JOptionPane.showMessageDialog(this, "Einstellungen gespeichert!", "Erfolg", JOptionPane.INFORMATION_MESSAGE);
+            dispose();
+        });
+
+        // Cancel button action
+        cancelBtn.addActionListener(e -> {
+            dispose();
+        });
+    }
+
+    private void testRelayConnection(String ip, int port, JLabel statusLabel) {
+        statusLabel.setText("Teste...");
+        statusLabel.setForeground(Color.BLUE);
+
+        new Thread(() -> {
+            try (Socket testSocket = new Socket()) {
+                testSocket.connect(new InetSocketAddress(ip, port), 5000); // 5 second timeout
+
+                // If we get here, connection was successful
+                SwingUtilities.invokeLater(() -> {
+                    statusLabel.setText("Verbunden ✓");
+                    statusLabel.setForeground(Color.GREEN);
+                });
+
+            } catch (IOException ex) {
+                SwingUtilities.invokeLater(() -> {
+                    statusLabel.setText("Verbindung fehlgeschlagen");
+                    statusLabel.setForeground(Color.RED);
+                });
+            }
+        }).start();
+    }
+}