import java.io.*; import java.net.*; import java.math.BigInteger; import java.util.Random; public class Server { WelcomeThread w_thread; StringQueue msg_q; ClientArray clients; public static void main(String args[]) { (new Server()).start(); } // Sætter serveren op. public void start() { msg_q = new StringQueue(10); clients = new ClientArray(10); w_thread = new WelcomeThread(this); w_thread.start(); ClientConnection client; String msg; // Denne løkke venter på at der kommer indhold i message-køen. // Indholdet distribueres derefter til de forskellige klienter. while (true) { if (!msg_q.isEmpty()) { msg = msg_q.Retrieve(); for (int i = 0 ; i < clients.getSize() ; i++) { client = clients.Get(i); if (client != null) client.Transmit(msg); } } try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Interrupted!"); return; } } } public synchronized void registerClient(ClientConnection c) { clients.Insert(c); } public void Disconnect(ClientConnection c) { clients.Remove(c); } public void Broadcast(String str) { msg_q.Insert(str); } } // En tråd der lytter efter nytilkommende klienter. class WelcomeThread extends Thread { ServerSocket welcomeSocket; Server master; public WelcomeThread (Server m) { try { welcomeSocket = new ServerSocket(6789); } catch (IOException e) { System.out.println("Couldn't initialize port listener."); System.out.println(e.getMessage()); return; } master = m; } public void run() { System.out.println("Listening for incoming connections..."); Socket sock; while (true) { try { sock = welcomeSocket.accept(); // Lav en ny klient-tråd. (new ClientConnection(master, sock)).start(); } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } } } } // Der oprettes en instans af denne klasse for hver klient der logger på. class ClientConnection extends Thread { BufferedReader inFromClient; String username; Socket connection; OutputToClient outToClient; Server master; boolean running; BigInteger AESkey; // Da AES-klassen vist ikke er trådsikker (ups!) laves to instanser: // En til kryptering og en til dekryptering. AES aes, aes2; RSA rsa; public void run() { String in; while (running) { // Prop en parser ind her. Skal parse input fra brugeren. in = Decrypt(ReadFromClient()); if (in != null) master.Broadcast('<' + username + "> " + in); else return; } } private void LoadKey() { // Big-endian order. MSB af tallet ligger i byte_key[0]. byte[] byte_key = AESkey.toByteArray(); int[] int_key = new int[16]; for (int i = 0 ; i < Math.min(byte_key.length,16) ; i++) { int_key[i] = byte_key[i]; } aes.setCipherkey(int_key); aes2.setCipherkey(int_key); } private String Decrypt(String str) { int i = 0; StringBuffer res = new StringBuffer(); byte[] cipher_text = new byte[16]; // Vi ved at strengens længde altid er et multiplum af 32. while (i != str.length()) { for (int j = 0 ; j < 16 ; j++) { cipher_text[j] = AES.ToByte(Integer.parseInt(str.substring(i,i+2), 16)); i += 2; } aes2.state = cipher_text; aes2.decrypt(); for (int j = 0 ; j < 16 ; j++) res.append( (char) aes2.state[j]); } return res.toString(); } public String Encrypt(String str) { int i = 0; StringBuffer res = new StringBuffer(); while (i + 16 < str.length() ) { aes.state = str.substring(i, i + 16).getBytes(); aes.encrypt(); for (int j = 0 ; j < 16 ; j++) { res.append( AES.Byte2Hex(aes.state[j])); } i += 16; } byte[] byte_array = new byte[16]; byte[] tmp; tmp = str.substring(i, str.length() ).getBytes(); for (int j = 0 ; j < tmp.length ; j++) { byte_array[j] = tmp[j]; } aes.state = byte_array; aes.encrypt(); for (int j = 0 ; j < 16 ; j++) { res.append( AES.Byte2Hex(aes.state[j])); } return res.toString(); } // Forsøger at rydde op efter en klient der logger af. public void Disconnect() { try { connection.close(); inFromClient.close(); } catch (IOException e) { } outToClient.running = false; master.Disconnect(this); } // Sender str til klienten. public void Transmit(String str) { System.out.println("Transmitting: " + str); outToClient.msg_q.Insert(Encrypt(str)); } public ClientConnection(Server m, Socket c) { running = true; master = m; connection = c; AESkey = new BigInteger(128, new Random(System.currentTimeMillis())); aes = new AES(); aes2 = new AES(); // Intet nøglesæt genereres til denne RSA-instans. // Serveren har ikke et nøglesæt. rsa = new RSA(); try { inFromClient = new BufferedReader(new InputStreamReader(connection.getInputStream())); outToClient = new OutputToClient(new DataOutputStream(connection.getOutputStream())); LoadKey(); ExchangeAESkey(); outToClient.start(); } catch (IOException e) { System.out.println("Could not establish connection to client."); return; } // Lav diverse setups. username = ReadFromClient(); System.out.println("User " + username + " logged in from " + c.getInetAddress().toString()); master.registerClient(this); } // Indledende handshaking med en klient. Her sendes AES-session-nøglen til klienten. // Meget usikkert hvis ikke klienten overholder protokollen nøjagtigt. private void ExchangeAESkey() { BigInteger e = new BigInteger(ReadFromClient()); BigInteger n = new BigInteger(ReadFromClient()); String[] key = rsa.Encrypt(AESkey.toString(), e, n); for (int i = 0 ; i < key.length ; i++) { outToClient.msg_q.Insert( key[i]); } outToClient.msg_q.Insert("&END"); } // Læser input fra klienten. private String ReadFromClient() { try { return inFromClient.readLine(); } catch (IOException e) { System.out.println("Communication error! " + username + " not responding!"); Disconnect(); return null; } } // Intern klasse. Håndterer output til klienten. class OutputToClient extends Thread { DataOutputStream toClient; StringQueue msg_q; // Message-queue. boolean running; public OutputToClient(DataOutputStream stream) { toClient = stream; msg_q = new StringQueue(5); running = true; } // Venter indtil der kommer indhold i message-køen. public void run() { while (running) { if (!msg_q.isEmpty()) { try { toClient.writeBytes(msg_q.Retrieve() + '\n'); continue; } catch (IOException e) { System.out.println("Error writing to client."); return; } } try { sleep(500); } catch (InterruptedException e) { return; } } } } } // Klasse til at holde styr på alle klienterne. class ClientArray { ClientConnection[] array; public ClientArray(int s) { array = new ClientConnection[s]; } public int getSize() { return array.length; } private void resize() { ClientConnection[] newarray = new ClientConnection[2 * array.length]; for (int i = 0 ; i < array.length ; i++) newarray[i] = array[i]; } public ClientConnection Remove(int i) { ClientConnection tmp = array[i]; array[i] = null; return tmp; } public void Remove(ClientConnection c) { int i = 0; while (array[i] != c) { i++; if (i == array.length) return; } array[i] = null; } public ClientConnection Get(int i) { return array[i]; } public int Insert(ClientConnection c) { int i = 0; while (array[i] != null) { i++; if (i == array.length) { resize(); break; } } array[i] = c; return i; } } class StringQueue { String[] array; int top; // Peger på det første ubrugte element. int bottom; // Peger på den forreste streng i køen. int size; public StringQueue(int s) { array = new String[s]; top = bottom = 0; } public synchronized void Insert(String str) { array[top] = str; top++; size++; // Check for overflow. if (top == array.length) top = 0; // Check for størrelse. if ( top % array.length == bottom) resize(); } public boolean isEmpty() { if (size == 0) return true; else return false; } private void resize() { String[] newarray = new String[2 * array.length]; for (int i = bottom ; i < bottom + size ; i++) { newarray[i] = array[i % array.length]; } top = bottom + size; array = newarray; } public void PrintRel() { for (int i = bottom ; i < bottom + size ; i++) { System.out.println( (i - bottom) + " - " + array[i % array.length]); } } public synchronized String Retrieve() { if (bottom == top) return null; String tmp = array[bottom]; array[bottom] = null; bottom++; size--; if (bottom == array.length) bottom = 0; return tmp; } }