/* Copyright (C) 2006 Daniel Mead (d.w.mead@gmail.com, dm367787@wcupa.edu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* File Desc: messenger is the class with serves as a go between for the swing (or possibly SWT, AWT etc) and the underlying network calls. it also serves as a handler for the encrytion mechanisms and a manager for a simple messaging protocool the only calls needed in the GUI section are: read, write, paint, encrpyion on/off and close all of which are abstracted to here. */ import java.io.*; import java.net.*; import java.security.*; import java.util.StringTokenizer; public class Messenger extends SwingWorker { //network i/o objects private Socket clientSocket; private Socket connectionSocket; private ObjectOutputStream toServer; private ObjectInputStream fromClient; //a refrence to the converstation box gui private ConversationBox convo; //default names for the chat participants private String remoteName = "Remote person says"; private String localname = "You say"; //condition booleans private boolean connected = false; boolean sendEncrypted = false; boolean reciveEncrypted = false; public boolean verifymessages = true; private String currenthash = "nothing"; private Encrypter enc = new Encrypter(); public boolean isEncrypted = false; //port to listen on for connections private int port; public void establishConnection(String destination, int port) { if(open(destination, port)) { convo.addMessage("SYSTEM: ", "successfully connected to " + destination); sendserverCommand("handshake", "cleartext"); } } public void setLocalName(String name) { localname = name; } public Messenger(ConversationBox input) { convo = input; } public void setIsEncrypted(boolean bool) { isEncrypted = bool; } //executeable commands start here. never call these outside of runcommand //or sendcommand //clientside (local) commands private void say(String message) { String remoteaddress = connectionSocket.getInetAddress().toString(); remoteaddress = remoteaddress.substring(1); String messagehash = findmessageHash(message.trim()); System.out.print("***" + message + "***\n"); System.out.print(message.length()); if (verifymessages) { System.out.print("currenthash is " + currenthash + "\n"); System.out.print("messagehash hash is " + messagehash + "\n"); if ( ! messagehash.equals(currenthash.trim())) convo.addMessage("SYSTEM", "Warning: Next message does not" + " have corrrect hash"); } convo.addMessage(remoteName + "("+ remoteaddress + ")" , message); } /* Function: findmessageHas Desc: uses the java.io libraries to do SHA (or md5, etc) hashing of outgoing and incoming chat messages */ public String findmessageHash(String message) { byte[] data; byte[] thehash; String finalhash = "hashing failed"; MessageDigest md; try { md = MessageDigest.getInstance("SHA"); try { data = message.getBytes("UTF-8"); md.update(data); thehash = md.digest(); finalhash = RSAEncryptUtil.encodeBASE64(thehash); } catch(UnsupportedEncodingException e) { e.printStackTrace(); } } catch(NoSuchAlgorithmException e) { e.printStackTrace(); } System.out.print("Current message: \n"+ message + "\n Has hash: \n" + finalhash + "\n"); return finalhash; } /* Function: sendstring Desc: writes a given string to the outgoing socket, calls the encryption routines when activated */ public void sendstring(String message) { try { if (isEncrypted) { String encryptedmessage; encryptedmessage = encryptMyMessage(message); System.out.print("Sending Encrtypedmessage: " + encryptedmessage.toString()); toServer.writeObject(encryptedmessage); toServer.flush(); } else { System.out.printf("Sending ClearText message: %s \n", message); toServer.writeObject(message); toServer.flush(); } } catch(UnknownHostException x) { System.out.println("Unable to connect to host"); }catch(Exception x) { x.printStackTrace();} } /* Function: setreceiveEncryptionStatus Desc: recives data off of the incoming socket to control encrpyter status */ private void setreceiveEncryptionStatus(String status) { if (status.equals("off")) isEncrypted = false; else if (status.equals("on")) isEncrypted = true; } /* Function: setremoteName Desc: when a client connects to us it will automatically send their set username so the chatbox reflects what we want to call ourselves */ private void setremoteName(String arg) { remoteName = arg; } /* Functions: decryptyMyMessage and encryptMyMessage Desc: wrapper to the encrypter class which gives us cleaner code */ private String decryptyMyMessage(String encryptedmessage) { String decryptedmessage = enc.decryptString(encryptedmessage); return decryptedmessage; } private String encryptMyMessage(String message) { String encryptedmessage = enc.encryptString(message); return encryptedmessage; } /* Function: listen Desc: our main thread which listens for a connection to the welcome socket this thread will block on welcomeSocket = new ServerSocket(port); until a connection is made there, when connectionSocket gets assigned it's non-null refrence then the thread in construct() will unblock and we can start a conversation */ public void listen(final int port) { new Thread() { public void run() { ServerSocket welcomeSocket; try { welcomeSocket = new ServerSocket(port); if(welcomeSocket.isBound()) System.out.print("server is bound \n"); connectionSocket = welcomeSocket.accept(); } catch (IOException e) { e.printStackTrace(); } } }.start(); //this call to start is to the unnamed thread //on the above lines setPort(port); this.start(); //this call to start spins off the thread //defined in public Object construct() below } /* Function: setport Desc: mutator for our port int */ public void setPort(final int port) { this.port = port; } /* Function: construct() Desc: this object defines the run thread that messenger needs to implement to be a swingworker. It will spun off in listen() and block until a connection is made. When a connection is made it processes everything coming off the connectionSocket as strings and calls different functions based in those strings. */ public Object construct() { String clientSentence = "Error reciving object"; StringTokenizer st; try { //sleep this thread until someone connects to us while(connectionSocket== null) { Thread.sleep(100); } } catch (Exception e) { e.printStackTrace(); } try { //establish an incoming stream from the client fromClient = new ObjectInputStream(new BufferedInputStream(connectionSocket.getInputStream())); while(true) { //peice an object off one at a time from the stream Object incoming = fromClient.readObject(); //if the encrypter is on, have it decrypt the messages // and then process it like a normal string if(isEncrypted) { System.out.printf("Incoming is %s \n", incoming); clientSentence = decryptyMyMessage((String)incoming); } else { //if we've got the recivekey command, the next //object will be the clients public key, so set that here if (incoming.equals("recivekey")) { clientSentence = (String)incoming; PublicKey recivekey = (PublicKey) fromClient.readObject(); setpublicKeyObject((PublicKey) recivekey); } //else it's a normal message else clientSentence = (String) incoming; } //mine the new string for command and arg tokens and pass to //runserverCommand st = new StringTokenizer(clientSentence, " "); String command = st.nextToken(); String arg = clientSentence.substring(command.length(), clientSentence.length()); runserverCommand(command, arg); System.out.printf("Received %s from %s", clientSentence, connectionSocket.getInetAddress() + "\n"); } } catch(Exception x) { x.printStackTrace(); } Object dummy = new Object(); return dummy; } /* Function: open Desc: call the java networking stuff to open an outgoing connection to a given host and port, report errors as needed return false if the connection fails return true if it succeeds */ public boolean open(String host, int port) { try { clientSocket = new Socket(host, port); toServer = new ObjectOutputStream(new BufferedOutputStream(clientSocket.getOutputStream())); clientSocket.setTcpNoDelay(true); } catch(UnknownHostException x) { System.out.println("Unable to connect to host"); return false; } catch(Exception x) { x.printStackTrace(); return false; } System.out.print("connected to server \n"); connected = true; return true; } /* Function: close Desc: gracefully tell the java VM to do garbage collection with the operaating system and terminate the connection */ public void close() { try { clientSocket.close(); } catch(Exception x) { x.printStackTrace(); } } /* Function: runserverCommand Desc: consumes a string from the listener thread and executes a function if it matches a case These functions should only be called from this spot */ public void runserverCommand(String command, String Arg) { if (command.equals("say")){ say(Arg); }else if (command.equals("close")){ close(); }else if (command.equals("setremoteName")){ setremoteName(Arg); }else if (command.equals("setreceiveEncryptionStatus")){ setreceiveEncryptionStatus(Arg); } else if (command.equals("setkey")){ //this is a special case, check the listener to //see how the object that comes in is passed }else if (command.equals("recivekey")){ System.out.print("Public Key recived"); }else if(command.equals("sendcurrenthash")){ currenthash = Arg; System.out.print("Current Hash after runserver command =" + currenthash + "\n"); }else { System.out.printf("Received bad command %s from %s \n", command + Arg, connectionSocket.getInetAddress().toString()); } } /* Function: sendservercommand Desc: passes strings up the datastream to be executed on the other side. if a command needs local actions to be taken as well simply name it explicitly and take care of everything in the block statement and then writes the string to the outgoing stream */ public void sendserverCommand(String command, String Arg) { //explicitly name all functions that need local stuff to get done //then pass everything else up the data stream. it will get //validated on the server if(command.equals("say")) { sendstring(command + " " + Arg); convo.addMessage(localname, Arg); //take care of the local convo }else if (command.equals("close")){ close(); } //toggle encrpytion else if(command.equals("encryption")) { if (Arg.equals("off")) isEncrypted = false; else if (Arg.equals("on")) isEncrypted = true; sendstring(command + " " + Arg); } //say what sort of conversataion we're starting else if (command.equals("handshake")) { if (Arg.equals("cleartext")) sendserverCommand("setremoteName", localname); } //send aa client our public key else if (command.equals("setkey")) { sendkey(enc.getmyPublicKey()); } else { sendstring(command + " " + Arg); } } /* Function: sendkey Desc: send a client our public RSA key */ public void sendkey(PublicKey keytosend ) { String recivecmd = "recivekey"; try { System.out.printf("Sending object: %s \n", keytosend.getClass()); toServer.writeObject(recivecmd); Thread.sleep(100); toServer.flush(); toServer.writeObject(keytosend); toServer.flush(); } catch(UnknownHostException x) { System.out.println("Unable to connect to host"); }catch(Exception x) { x.printStackTrace();} } /* Function: setpublicKeyObject Desc: give the encrpyer the public key recived from the other end */ void setpublicKeyObject(PublicKey recivekey) { enc.setRemotePublickey(recivekey); } }