/* Athena/Aegis Encrypted Chat Platform * Athena.java: Client backend operations for logging in and routing incoming/outgoing messages * * Copyright (C) 2010 OlympuSoft * 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 2 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ //TODO Code review the three major files. // Athena.java, CommunicationInterface.java, ServerThread.java import java.awt.AWTException; import java.awt.Color; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.net.Socket; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Enumeration; import java.io.StringWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Hashtable; import javax.crypto.spec.*; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.text.BadLocationException; import javax.swing.text.MutableAttributeSet; import sun.audio.AudioPlayer; import sun.audio.AudioStream; /** * Provides the backend components for the Athena chat client */ public class Athena { /** * Print debug messages during runtime (0=off,1=on,2=verbose) */ public static int debug = 2; //Show debug messages? //public static String[] inviteInformationArray; /** * The current user's username. Used globally */ public static String username = "null"; /** * The server's public key. Used for server communication */ public static RSAPublicKeySpec serverPublic; public static RSAPublicKeySpec toUserPublic;// = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); public static RSAPrivateKeySpec userPrivate;// /** * The main window. We use this object to manipulate the window. */ protected static CommunicationInterface clientResource; /** * The login window */ protected static AuthenticationInterface loginGUI; /** * A MapTextArea object used all over the place to manipulate IM/Chat tabs */ protected static MapTextArea print; //End protected variables /** * Begin private variables */ //private static DataInputStream dpInputStream; static String serverIP = "aegis.athenachat.org"; //IP of the server static int connected = 0; //If the client is connect to the server private static int away = 0; //Is the user away? private static LoginProgress loginBar; private static SecretKeySpec chatSessionKey, dpSessionKey; //Secret key for group chat session key static DESCrypto descrypto; //DESCrpyto Object for encrypting with user's password private static String toUser; //Recipient for message private static String awayText; //Away message text private static String currentMsgSound; private static String currentInSound; private static String currentOutSound; static Socket c2ssocket; // The socket connecting us to the server for client communication static Socket c2csocket; // The socket connecting us to the server for server communication static DataOutputStream c2sdout; // Client to Server DataOutputStream static DataInputStream c2sdin; //Client to Server DataInputStream static DataOutputStream c2cdout; //Client to Client DataOutputStream static DataInputStream c2cdin; //Clien to Client DataInputStream static Thread listeningProcedureClientToClient;//, listeningProcedureDirectProtect, listeningProcedureConnectDirectProtect; //Thread that will be used to listen for incoming messages private static boolean enableSounds; //Flag to control sound notifications private static BigInteger modOfBuddy = null; private static BigInteger expOfBuddy = null; private static String[] aliasArray; static File debugLog; //private static ServerSocket directProtectSocket; //Direct protect socket! //private static Socket dpSocket; //private static RSAPrivateKeySpec usersPrivate; //User's public key public static Hashtable<String, SecretKeySpec> sessionKeys = new Hashtable<String, SecretKeySpec>(); static BufferedWriter debugWriter; public static Hashtable<String, String> contactsTable = new Hashtable<String, String>(); //End private variables /** * Method that connects the user with Aegis * @param usernameToConnect the username that is entered in the login window * @param hashedPassword the hashed password that is entered in the login window * @throws AWTException * @throws InterruptedException * @throws Exception */ public static void connect(String usernameToConnect, String hashedPassword) throws InterruptedException, AWTException, Exception { ConnectThread connect = new ConnectThread(usernameToConnect, hashedPassword); connect.start(); //System.out.println("THE USERNAME IS: "+username) // toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + username + ".pub"); // userPrivate = RSACrypto.readPrivKeyFromFile("users/" + username + "/keys/" + username + ".priv", descrypto); } public static void openLog(File fileName) { try{ int numLogs = new File("users/"+username+"/logs").list().length; if(numLogs > 15) { File directory = new File("users/"+username+"/logs"); // Get all files in directory File[] files = directory.listFiles(); for (File file : files) { // Delete each file if (!file.delete()) { // Failed to delete file System.out.println("Failed to delete "+file); } } } debugLog = fileName; if (!(debugLog.exists())) { if(Athena.debug >= 1)Athena.writeLog("ERROR: Logs folder is not there. Will attempt to create."); boolean success = new File("users/" + username+"/logs/").mkdirs(); if (success) { debugLog.createNewFile(); } else { debugLog.createNewFile(); } } debugWriter = new BufferedWriter(new FileWriter(debugLog)); } catch(Exception e) { System.out.println("Unable to open log file"); } } public static String getDateTime() { DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); Date date = new Date(); return dateFormat.format(date); } public static String getCleanDateTime() { DateFormat dateFormat = new SimpleDateFormat("yyyyddMM-HHmm"); Date date = new Date(); return dateFormat.format(date); } public static void writeLog(String debugText) { try{ debugWriter.write(getDateTime() + ": "+debugText+"\r\n"); debugWriter.flush(); } catch(Exception e){ //e.printStackTrace(); System.out.println("Unable to write to log file. Make sure the file is open for writing."); } } public static void closeLog() throws IOException { debugWriter.close(); } /** * Overloaded connect method for adding a user * @overloaded */ public static void connect() { //Try to connect to and authenticate with the socket try { try { //Connect to auth server at defined port over socket c2ssocket = new Socket(serverIP, 7777); c2csocket = new Socket(serverIP, 7778); } catch (Exception e) { //We can't connect to the server at the specified port for some reason JOptionPane.showMessageDialog(null, "Could not connect to the server.\nPlease check your Internet connection." + "\n\n", "Connection Error", JOptionPane.ERROR_MESSAGE); return; } //Connection established debug code. if (debug == 1) { writeLog("Connected to " + c2ssocket); } //Bind the datastreams to the socket in order to send/receive c2sdin = new DataInputStream(c2ssocket.getInputStream()); c2sdout = new DataOutputStream(c2ssocket.getOutputStream()); //Read in server's public key for encryption of headers serverPublic = RSACrypto.readPubKeyFromFile("users/Aegis/keys/Aegis.pub"); System.gc(); } catch (IOException ie) { sendBugReport(getStackTraceAsString(ie),null); } } public static void blockUser(String userToBlock){ try{ systemMessage("23"); c2sdout.writeUTF(encryptServerPublic(userToBlock)); } catch(Exception e){e.printStackTrace();} } public static void unblockUser(String userToUnblock){ try{ systemMessage("24"); c2sdout.writeUTF(encryptServerPublic(userToUnblock)); } catch(Exception e){e.printStackTrace();} } /** * Method instantiate the buddy list * @throws Exception */ public static void instantiateBuddyList(LoginProgress myloginBar) throws Exception { loginBar = myloginBar; //First we need to compare the hash of the buddy list we have to the one on the server to make sure nothing has been changed. String hashOfLocalBuddyList = returnHashOfLocalBuddyList(username); //Now we need to get the hash of the user's buddy list on the server String[] remoteVals = returnHashOfRemoteBuddyList(username); long remoteBuddyListModDate = Long.parseLong(remoteVals[1].trim()); if (debug == 2) { writeLog("INFO: Hash of local buddylist file: " + hashOfLocalBuddyList + "\nINFO: Hash of buddylist on the server: " + remoteVals[0]); } //Now let's compare this hash with the hash on the server if ((!(hashOfLocalBuddyList.equals(remoteVals[0])))) { long localBuddyListModDate = returnLocalModDateOfBuddyList(username); if ((hashOfLocalBuddyList.equals("d41d8cd98f00b204e9800998ecf8427e"))) { receiveBuddyListFromServer(); } else if (localBuddyListModDate > remoteBuddyListModDate) { //Send buddylist to server! if (debug >= 1) { writeLog("ALERT: Sending new copy of buddylist to server. "); } sendBuddyListToServer(); } else if (localBuddyListModDate == remoteBuddyListModDate) { //DO NOTHING } else { //Get buddylist from server if (debug >= 1) { writeLog("ALERT: Your buddylist is old. Getting a new copy from the server."); } receiveBuddyListFromServer(); } } else { if (debug >= 1) { writeLog("Hashes match!"); } } //Grab string array of the buddylist.csv file returnBuddyListArray(); String[] usernames = getContactsArrayFromTable(); loginBar.iterate(1500,"Querying User Status"); int moveIt=0; if(usernames.length !=0){ moveIt = (1900-1500)/usernames.length; } else{ moveIt = 1900-1500;} int current = 1500; //Check entire buddylist and fill hashtable with user online statuses for (int i = 0; i < usernames.length; i++) { if (debug == 1) { writeLog("INFO: Checking status for username = " + usernames[i]); } //Check to see if the user's public key is there File pubKey = new File("users/" + username + "/keys/" + usernames[i] + ".pub"); if (!(pubKey.exists())) { boolean temp = getUsersPublicKeyFromAegis(usernames[i]); } checkUserStatus(usernames[i]); current+=moveIt; loginBar.iterate(current,"Querying User Status"); } //Counter int y = 0; //Loop through the HashTable of available users and place them in the JList for (Enumeration<?> e = clientResource.userStatus.keys(), f = clientResource.userStatus.elements(); y < clientResource.userStatus.size(); y++) { try { String currentE = e.nextElement().toString(); String currentF = f.nextElement().toString(); //If the user is online, add them to your buddylist if (currentF.equals("1")) { clientResource.newBuddyListItems(currentE); clientResource.newAliasListItems(contactsTable.get(currentE)); } } catch (java.util.NoSuchElementException ie) { sendBugReport(getStackTraceAsString(ie),null); ie.printStackTrace(); } catch (Exception ie) { sendBugReport(getStackTraceAsString(ie),null); ie.printStackTrace(); } } //Send Message to Aegis letting it know we're logged in systemMessage("002"); if(Athena.debug>=1)Athena.writeLog("INFO: Login successful. Moving on."); //Garbage collect! System.gc(); } /** * @Overloaded * This method is called when adding a user to ones buddy list, this immediately checks to see if the inputted user is online * @param usernameToInstantiate Username of buddy to add to the buddylist * @throws IOException */ public static void instantiateBuddyList(String usernameToInstantiate) throws IOException, Exception { boolean isExist = getUsersPublicKeyFromAegis(usernameToInstantiate); if(isExist) { buddyList(usernameToInstantiate); checkUserStatus(usernameToInstantiate, "PauseThread!"); } } /** * This method checks to see if the current user is online * @param findUserName The username of the user to check */ public static void checkUserStatus(String findUserName) { try { if (debug >= 1) { writeLog("INFO: Checking status for user: " + findUserName); } //Initalize Result int result = -1; //Run the systemMessage Method to let Aegis know what we're about to do //First contact with Aegis! systemMessage("001"); //Go ahead and send Aegis the user name we want to find c2sdout.writeUTF(encryptServerPublic(findUserName)); if (debug >= 1) { writeLog("INFO: Username sent to the server. Listening for their status..."); } //Grab result and turn it into an integer result = Integer.parseInt(decryptServerPublic(c2sdin.readUTF())); //Print result if (debug >= 1) { writeLog("INFO: " + findUserName + ": " + result); } //Call the mapUserStatus method in ClientApplet to fill the Hashtable of user's statuses clientResource.mapUserStatus(findUserName, result); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); if (debug >= 1) { e.printStackTrace(); } } } /** This method checks to see on a one user basis if the inputted user is online * @param usernameToCheck The user to check the status * @param checkStatusFlag Boolean flag to designate that the method is overloaded */ public static void checkUserStatus(String usernameToCheck, String checkStatusFlag) { try { if (debug >= 1) { writeLog("INFO: Checking status for user: " + usernameToCheck); } //Initialize Result to -1 int result = -1; //Run the systemMessage Method to let Aegis know what we're about to do systemMessage("003"); c2sdout.writeUTF(encryptServerPublic(usernameToCheck)); if (debug >= 1) { writeLog("INFO: Username sent to the server. Listening for their status..."); } result = Integer.parseInt(decryptServerPublic(c2sdin.readUTF())); if (debug >= 1) { writeLog("INFO: " + usernameToCheck + ": " + result); } clientResource.mapUserStatus(usernameToCheck, result); if (result == 1) { clientResource.newBuddyListItems(usernameToCheck); clientResource.newAliasListItems(contactsTable.get(usernameToCheck)); } } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); if (debug >= 1) { e.printStackTrace(); } } } /** This method is run in a thread and will receive and process an incoming message * @param din This DataInputStream is where the messages will come from */ public static void recvMesg(DataInputStream din) { try { // Who is the message from? String fromUserCipher = din.readUTF(); // What is the message? String encryptedMessage = din.readUTF(); RSAPrivateKeySpec usersPrivateKey = RSACrypto.readPrivKeyFromFile("users/" + username + "/keys/" + username + ".priv", descrypto); //Decrypt the fromUser to see what user this message came from! String fromUserDecrypted = decryptServerPublic(fromUserCipher); //Get the message ready for encryption String decryptedMessage; byte[] messageBytes = (new BigInteger(encryptedMessage)).toByteArray(); //If the message is an unavailable user response if (fromUserDecrypted.equals("UnavailableUser")) { decryptedMessage = decryptServerPublic(encryptedMessage); print = (MapTextArea) clientResource.tabPanels.get(decryptedMessage); print.writeToTextArea(fromUserDecrypted + ": ", print.getSetHeaderFont(Color.red)); print.writeToTextArea(decryptedMessage + "\n", print.getTextFont()); return; } else if (fromUserDecrypted.equals("ServerShutDown")) { decryptedMessage = decryptServerPublic(encryptedMessage); JOptionPane.showMessageDialog(null,"Aegis is shutting down in 30 seconds.\nReason:\n"+decryptedMessage+"\n\nPlease wrap up your business and log off."); if(debug>=1)Athena.writeLog("WARN: Received server shutdown message. Will be disconnected in 30 seconds."); }//Remove user from Buddylist else if (fromUserDecrypted.equals("ServerLogOff")) { decryptedMessage = decryptServerPublic(encryptedMessage); //Check to see if the user is in your buddy list, if not, don't care if (contactsTable.containsKey(decryptedMessage)) { //We know that the buddy is in his/her buddy list! clientResource.buddySignOff(decryptedMessage); clientResource.aliasSignOff(contactsTable.get(decryptedMessage)); // If enabled, open an input stream to the audio file. if (getEnableSounds()) { InputStream in = new FileInputStream(currentOutSound); // Create an AudioStream object from the input stream. AudioStream as = new AudioStream(in); // Use the static class member "player" from class AudioPlayer to play // clip. AudioPlayer.player.start(as); } } if ((contactsTable.containsKey(decryptedMessage)) && (clientResource.tabPanels.containsKey(decryptedMessage))) { print = (MapTextArea) clientResource.tabPanels.get(decryptedMessage); print.writeToTextArea(decryptedMessage + " has signed off.\n", print.getSetHeaderFont(Color.gray)); if(sessionKeys.containsKey(decryptedMessage)){ print.writeToTextArea("DirectProtect session aborted!\n", print.getSetHeaderFont(Color.gray)); print.encType.setText("Encryption Type: RSA - DirectProtect Inactive"); print.encType.setIcon(new ImageIcon("images/unlockDP.png")); sessionKeys.remove(decryptedMessage); } } return; } //Create buddy list entry for user sign on else if (fromUserDecrypted.equals("ServerLogOn")) { //Decrypt Message decryptedMessage = decryptServerPublic(encryptedMessage); if (!(decryptedMessage.equals(username))) { //Check to see if the user is in your buddylist, if not, don't care if (contactsTable.containsKey(decryptedMessage)) { //We know that the buddy is in his/her buddy list! clientResource.newBuddyListItems(decryptedMessage); clientResource.newAliasListItems(contactsTable.get(decryptedMessage)); //** add this into your application code as appropriate if (getEnableSounds()) { // If enabled, open an input stream to the audio file. InputStream in = new FileInputStream(currentInSound); // Create an AudioStream object from the input stream. AudioStream as = new AudioStream(in); // Use the static class member "player" from class AudioPlayer to play AudioPlayer.player.start(as); } } if ((contactsTable.containsKey(decryptedMessage)) && (clientResource.tabPanels.containsKey(decryptedMessage))) { print = (MapTextArea) clientResource.tabPanels.get(decryptedMessage); print.writeToTextArea(decryptedMessage + " has signed on.\n", print.getSetHeaderFont(Color.gray)); } return; } } //Pop up chat invite else if (fromUserDecrypted.equals("ChatInvite")) { decryptedMessage = decryptServerPublic(encryptedMessage); String[] chatName = decryptedMessage.split(","); int toJoin = JOptionPane.showConfirmDialog(null, "You have been invited by " + chatName[1] + " to join\nthe group chat: " + chatName[0] + "..."); if (toJoin == JOptionPane.YES_OPTION) { //Send server a confirm message systemMessage("14"); c2sdout.writeUTF(encryptServerPublic(chatName[2])); //Get the user list for the chat from the server getUserList(Integer.parseInt(chatName[2])); //Open a tab for the chat clientResource.makeChatTab(chatName[0], chatName[2]); //Put a dummy entry in the hashtable until we get the real session key SecretKeySpec nothing = new SecretKeySpec("lol".getBytes(), "AES"); sessionKeys.put(chatName[2], nothing); } return; } else if (fromUserDecrypted.equals("SessionKey")) { //Take in the encrypted session key decryptedMessage = RSACrypto.rsaDecryptPrivate(messageBytes, usersPrivateKey.getModulus(), usersPrivateKey.getPrivateExponent()); //Split the chatUID from the session key String[] chatInfo = decryptedMessage.split(","); //Make sure we are actually in the chat this key is for if (sessionKeys.containsKey(chatInfo[0])) { byte[] encoded = new BigInteger(chatInfo[1], 16).toByteArray(); SecretKeySpec aesKey; //If a leading zero-byte shows up, strip it if (encoded[0] == 0) { byte[] encoded2 = new byte[16]; for (int x = 0, y = -1; x < encoded.length; x++, y++) { if (x >= 1) { encoded2[y] = encoded[x]; } } aesKey = new SecretKeySpec(encoded2, "AES"); } else { aesKey = new SecretKeySpec(encoded, "AES"); } //Replace the dummy session key with the real one sessionKeys.remove(chatInfo[0]); sessionKeys.put(chatInfo[0], aesKey); //Chat join announcement BigInteger messageBigInt = new BigInteger(AESCrypto.encryptMessage(aesKey, "ChatJoin," + username)); //Alert the other users! systemMessage("17"); c2sdout.writeUTF(encryptServerPublic(chatInfo[0])); c2sdout.writeUTF(messageBigInt.toString()); return; } } else if (fromUserDecrypted.equals("DPSessionKey")) { //Take in the encrypted session key decryptedMessage = RSACrypto.rsaDecryptPrivate(messageBytes, usersPrivateKey.getModulus(), usersPrivateKey.getPrivateExponent()); //Split the chatUID from the session key String[] chatInfo = decryptedMessage.split(","); //Make sure we are actually in the chat this key is for if (sessionKeys.containsKey(chatInfo[0])) { byte[] encoded = new BigInteger(chatInfo[1], 16).toByteArray(); SecretKeySpec aesKey; //If a leading zero-byte shows up, strip it if (encoded[0] == 0) { byte[] encoded2 = new byte[16]; for (int x = 0, y = -1; x < encoded.length; x++, y++) { if (x >= 1) { encoded2[y] = encoded[x]; } } aesKey = new SecretKeySpec(encoded2, "AES"); } else { aesKey = new SecretKeySpec(encoded, "AES"); } //Replace the dummy session key with the real one sessionKeys.remove(chatInfo[0]); sessionKeys.put(chatInfo[0], aesKey); return; } } else if (fromUserDecrypted.equals("DPInvite")) { //Read in the information String inviteInformation = decryptServerPublic(encryptedMessage); //inviteInformationArray = inviteInformation.split(","); //Open up an alert! if ((clientResource.tabPanels.containsKey(inviteInformation))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation); print.writeToTextArea(inviteInformation + " has initiated a Direct Protect session...\n", print.getSetHeaderFont(Color.gray)); } int toJoin = JOptionPane.showConfirmDialog(null, "You have been invited to a Direct Protect session with: " + inviteInformation + "..."); if (toJoin == JOptionPane.YES_OPTION) { //Send server a confirm message systemMessage("20"); c2sdout.writeUTF(encryptServerPublic(inviteInformation)); c2sdout.writeUTF(encryptServerPublic("yes")); //Put a dummy entry in the hashtable until we get the real session key SecretKeySpec nothing = new SecretKeySpec("lol".getBytes(), "AES"); sessionKeys.put(inviteInformation, nothing); //dpSocket = new Socket(inviteInformationArray[1], 7779); //dpInputStream = new DataInputStream(dpSocket.getInputStream()); if ((clientResource.tabPanels.containsKey(inviteInformation))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation); print.writeToTextArea("Joining Direct Protect session with "+inviteInformation+"...\n", print.getSetHeaderFont(Color.gray)); print.encType.setText("Encryption Type: AES - DirectProtect Active"); print.encType.setIcon(new ImageIcon("images/lockDP.png")); } } else { if ((clientResource.tabPanels.containsKey(inviteInformation))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation); print.writeToTextArea("Aborting Direct Protect session with "+inviteInformation+"...\n", print.getSetHeaderFont(Color.gray)); print.encType.setText("Encryption Type: RSA - DirectProtect Inactive"); print.encType.setIcon(new ImageIcon("images/unlockDP.png")); } //Send server a confirm message systemMessage("20"); c2sdout.writeUTF(encryptServerPublic(inviteInformation)); c2sdout.writeUTF(encryptServerPublic("no")); } } else if (fromUserDecrypted.equals("DPResult")) { decryptedMessage = decryptServerPublic(encryptedMessage); String[] inviteInformation = decryptedMessage.split(","); if(inviteInformation[1].equals("yes")){ if ((clientResource.tabPanels.containsKey(inviteInformation[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation[0]); print.writeToTextArea("Direct Protect session started!\n", print.getSetHeaderFont(Color.gray)); print.encType.setText("Encryption Type: AES - DirectProtect Active"); print.encType.setIcon(new ImageIcon("images/lockDP.png")); } } else{ if ((clientResource.tabPanels.containsKey(inviteInformation[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation[0]); print.writeToTextArea("DirectProtect session aborted!\n", print.getSetHeaderFont(Color.gray)); print.encType.setText("Encryption Type: RSA - DirectProtect Inactive"); print.encType.setIcon(new ImageIcon("images/unlockDP.png")); } if(sessionKeys.containsKey(inviteInformation[0])){ sessionKeys.remove(inviteInformation[0]); } } } else if (fromUserDecrypted.equals("FileInvite")) { toUser = clientResource.imTabbedPane.getTitleAt(clientResource.imTabbedPane.getSelectedIndex()); //Read in the information String inviteInformation = RSACrypto.rsaDecryptPrivate(messageBytes, usersPrivateKey.getModulus(), usersPrivateKey.getPrivateExponent()); String[] inviteInformationArray = inviteInformation.split(","); String filePathReplace = inviteInformationArray[1].replace("\\", ","); String[] filePathArray = filePathReplace.split(","); //Open up an alert! if ((clientResource.tabPanels.containsKey(inviteInformationArray[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformationArray[0]); print.writeToTextArea(inviteInformationArray[0] + " would like to transfer a file...\n", print.getSetHeaderFont(Color.gray)); } int toJoin = JOptionPane.showConfirmDialog(null, inviteInformationArray[0] + " would like to transfer a file:\nFile name: " + filePathArray[filePathArray.length-1] + "\nFile Size: "+Integer.parseInt(inviteInformationArray[2])/1000+"kb"); if (toJoin == JOptionPane.YES_OPTION) { //Send server a confirm message systemMessage("22"); c2sdout.writeUTF(encryptServerPublic(inviteInformationArray[0])); String inviteString = username+","+inviteInformationArray[1]+","+"yes"; //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the toUser with the Server's public key and send it to the server //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(inviteString, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(messageCipher.toString()); if ((clientResource.tabPanels.containsKey(inviteInformationArray[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformationArray[0]); print.writeToTextArea("File transfer session started!\n", print.getSetHeaderFont(Color.gray)); //print.encType.setText("Encryption Type: AES - DirectProtect Active"); } receiveFile(inviteInformationArray[0],inviteInformationArray[1],inviteInformationArray[2], inviteInformationArray[3], inviteInformationArray[4]); //Receieve the file! } else { //Send server a confirm message systemMessage("22"); c2sdout.writeUTF(encryptServerPublic(inviteInformationArray[0])); String inviteString = username+","+inviteInformationArray[1]+","+"no"; //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the toUser with the Server's public key and send it to the server //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(inviteString, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(messageCipher.toString()); if ((clientResource.tabPanels.containsKey(inviteInformationArray[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformationArray[0]); print.writeToTextArea("File transfer session aborted!\n", print.getSetHeaderFont(Color.gray)); //print.encType.setText("Encryption Type: AES - DirectProtect Active"); } } } else if (fromUserDecrypted.equals("FileResult")) { decryptedMessage = RSACrypto.rsaDecryptPrivate(messageBytes, usersPrivateKey.getModulus(), usersPrivateKey.getPrivateExponent()); String[] inviteInformation = decryptedMessage.split(","); if(inviteInformation[2].equals("yes")){ if ((clientResource.tabPanels.containsKey(inviteInformation[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation[0]); print.writeToTextArea("File transfer session started!\n", print.getSetHeaderFont(Color.gray)); //print.encType.setText("Encryption Type: AES - DirectProtect Active"); transferFile(new File(inviteInformation[1]),inviteInformation[0]); } } else{ if ((clientResource.tabPanels.containsKey(inviteInformation[0]))) { print = (MapTextArea) clientResource.tabPanels.get(inviteInformation[0]); print.writeToTextArea("File transfer session aborted!\n", print.getSetHeaderFont(Color.gray)); //print.encType.setText("Encryption Type: RSA - DirectProtect Inactive"); } } } else if(fromUserDecrypted.equals("KickMessage")) { decryptedMessage = decryptServerPublic(encryptedMessage); //Output the kick message to a JOption Pane JOptionPane.showMessageDialog(null, "You have been kicked by the server. Please re-login."); clientResource.contactListModel.clear(); Athena.disconnect(); //Get rid of this window and open a new Login Window clientResource.imContentFrame.dispose(); new AuthenticationInterface(); } else { // Need this else in order to hide the system messages coming from Aegis //TODO Implement digital signatures decryptedMessage = RSACrypto.rsaDecryptPrivate(messageBytes, usersPrivateKey.getModulus(), usersPrivateKey.getPrivateExponent()); //If there isn't already a tab for the conversation, make one if (!(clientResource.tabPanels.containsKey(fromUserDecrypted)) && !(sessionKeys.containsKey(fromUserDecrypted))) { clientResource.makeTab(fromUserDecrypted, false); //We have to actually print the message now because we have a sane if/else setup //Write message to the correct tab print = (MapTextArea) clientResource.tabPanels.get(fromUserDecrypted); print.writeToTextArea(fromUserDecrypted + ": ", print.getSetHeaderFont(new Color(0, 0, 130))); parseMarkdown(decryptedMessage, print); print.moveToEnd(); //If we are away send the user our away message if (away == 1) { processMessage(fromUserDecrypted, "Auto reply from user: " + awayText); } //Play laugh if sound is enabled if (decryptedMessage.equalsIgnoreCase("lmao")) { if (getEnableSounds()) { // If enabled, open an input stream to the audio file. InputStream in = new FileInputStream("sounds/lmaoMesg.wav"); // Create an AudioStream object from the input stream. AudioStream as = new AudioStream(in); // Use the static class member "player" from class AudioPlayer to play AudioPlayer.player.start(as); } } else if (getEnableSounds()) { InputStream in = new FileInputStream(currentMsgSound); // Create an AudioStream object from the input stream. AudioStream as = new AudioStream(in); // Use the static class member "player" from class AudioPlayer to play AudioPlayer.player.start(as); } System.gc(); } //Write to an open chat tab else if (sessionKeys.containsKey(fromUserDecrypted) && !(clientResource.tabPanels.containsKey(fromUserDecrypted))) { //Find the chat tab for (int z = 0; z < clientResource.imTabbedPane.getTabCount(); z++) { JPanel tabToCheck = (JPanel) clientResource.imTabbedPane.getComponentAt(z); if (tabToCheck.getName().equals(fromUserDecrypted)) { //Decrypt the message with the session key decryptedMessage = decryptAES(fromUserDecrypted, encryptedMessage); //Split the username from the message String[] chatMessage = decryptedMessage.split(",", 2); //Get the MapTextArea so we can write the message to it print = (MapTextArea) clientResource.tabPanels.get(clientResource.imTabbedPane.getTitleAt(z)); if ((chatMessage[0].equals("ChatLeave"))) { //Format a chat leave message print.writeToTextArea(chatMessage[1] + " has left the chat.\n", print.getSetHeaderFont(Color.gray)); clientResource.chatSignOff(chatMessage[1], tabToCheck.getName()); } else if (chatMessage[0].equals("ChatJoin")) { //Format a chat join message print.writeToTextArea(chatMessage[1] + " has joined the chat.\n", print.getSetHeaderFont(Color.gray)); clientResource.newChatListItem(chatMessage[1], tabToCheck.getName()); } else { print.writeToTextArea(chatMessage[0] + ": ", print.getSetHeaderFont(new Color(0, 0, 130))); parseMarkdown(chatMessage[1], print); } print.moveToEnd(); break; } } } //Write to an open IM tab else { if(sessionKeys.containsKey(fromUserDecrypted)){ decryptedMessage = decryptAES(fromUserDecrypted, encryptedMessage); } //Write message to the correct tab print = (MapTextArea) clientResource.tabPanels.get(fromUserDecrypted); print.writeToTextArea(fromUserDecrypted + ": ", print.getSetHeaderFont(new Color(0, 0, 130))); parseMarkdown(decryptedMessage, print); print.moveToEnd(); //If we are away send the user our away message if (away == 1) { processMessage(fromUserDecrypted, "Auto reply from user: " + awayText); } //Play the lmao sound if (decryptedMessage.equalsIgnoreCase("lmao")) { if (getEnableSounds()) { // If enabled, open an input stream to the audio file. InputStream in = new FileInputStream("sounds/lmaoMesg.wav"); // Create an AudioStream object from the input stream. AudioStream as = new AudioStream(in); // Use the static class member "player" from class AudioPlayer to play AudioPlayer.player.start(as); } } // If enabled, open an input stream to the audio file. else if (getEnableSounds()) { InputStream in = new FileInputStream(currentMsgSound); // Create an AudioStream object from the input stream. AudioStream as = new AudioStream(in); // Use the static class member "player" from class AudioPlayer to play AudioPlayer.player.start(as); } System.gc(); } } } catch (IOException ie) { connected = 0; } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); if (debug >= 1) { e.printStackTrace(); } } } /** * Get the userlist of a chat * @param chatUID The UID of the chat of which you want the userlist */ public static void getUserList(int chatUID) { try { systemMessage("18"); c2sdout.writeUTF(encryptServerPublic(String.valueOf(chatUID))); String userList = decryptServerPublic(c2sdin.readUTF()); //Split the userlist and add it to the GUI String[] users = userList.split(","); clientResource.newChatListItems(users, String.valueOf(chatUID)); } catch (Exception e) { e.printStackTrace(); } } /** * Parse messages for markdown commands before printing them * @param mesg The message you want to parse for markdown * @param print The tab to write the parsed message to */ public static void parseMarkdown(String mesg, MapTextArea print) { String message = mesg; int bold = 0; int italic = 0; int underline = 0; int x = 0; int changed = 0; char current = ' '; char previous = ' '; char next = ' '; MutableAttributeSet currentAttr = print.getTextFont(); //Go through the message, character by character for (x = 0; x < message.length(); x++) { current = message.charAt(x); //Only get the previous character if we aren't on the first iteration if (x > 0) { previous = message.charAt(x - 1); } //Only get the next character if we aren't on the last if (x != message.length() - 1) { next = message.charAt(x + 1); } if (current == '*') { if (previous == '\\') { try { //Print an escaped asterisk print.writeToTextArea(String.valueOf(current), print.getTextFont()); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); e.printStackTrace(); } } //Or toggle bold/italic based on the number else if (next == '*') { if (bold == 1) { bold = 0; changed = 1; //System.out.print("</b>"); } else { bold = 1; changed = 1; //System.out.print("<b>"); } x++; } else { if (italic == 1) { italic = 0; changed = 1; //System.out.print("</i>"); } else { italic = 1; changed = 1; //System.out.print("<i>"); } } } //Check for underline commands else if (current == '_') { if (previous == '\\') { try { print.writeToTextArea(String.valueOf(current), print.getTextFont()); } catch (Exception e) { e.printStackTrace(); } } else { if (underline == 1) { underline = 0; } else { underline = 1; } changed = 1; } } //Otherwise just print text else { //update font if need be if (changed == 1) { boolean b = (bold != 0); boolean i = (italic != 0); boolean u = (underline != 0); print.setTextFont(b, i, u); changed = 0; } //Don't print escape characters if (current == '\\' && next == '*') { } else if (current == '\\' && next == '_') { } //Print text in current formatting else { try { print.writeToTextArea(String.valueOf(current), print.getTextFont()); } catch (Exception e) { e.printStackTrace(); } } } } try { //Newline after parsing the message print.writeToTextArea("\n", print.getTextFont()); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); e.printStackTrace(); } //Revert to default font print.setTextFont(currentAttr); } /** * Method for creating a group chat * @param chatName The name of the chat to create * @return The UID of the created chat, or -1 for failure */ public static String createChat(String chatName) { try { systemMessage("12"); try { c2sdout.writeUTF(encryptServerPublic(chatName)); String chatUID = decryptServerPublic(c2sdin.readUTF()); chatSessionKey = AESCrypto.generateKey(); //Save the session key to a Hashtable sessionKeys.put(chatUID, chatSessionKey); return chatUID; } catch (IOException e) { e.printStackTrace(); return "-1"; } } catch (NullPointerException npe) { return "-1"; } } public static void leaveDP(String user) { try{ if(sessionKeys.containsKey(user)){ sessionKeys.remove(user); systemMessage("20"); c2sdout.writeUTF(encryptServerPublic(user)); c2sdout.writeUTF(encryptServerPublic("no")); if ((clientResource.tabPanels.containsKey(user))) { print = (MapTextArea) clientResource.tabPanels.get(user); print.writeToTextArea("DirectProtect session has been terminated.\n", print.getSetHeaderFont(Color.gray)); print.encType.setText("Encryption Type: RSA - DirectProtect Inactive"); print.encType.setIcon(new ImageIcon("images/unlockDP.png")); } }}catch(Exception e){} } /** * */ public static void directProtect(String inviteUser) { try { //Create the session key dpSessionKey = AESCrypto.generateKey(); //Alert Aegis of our invite! systemMessage("19"); if(debug>=1)writeLog("INFO: Sent DirectProtect invite."); //Send the user we're connecting to c2sdout.writeUTF(encryptServerPublic(inviteUser)); //Send the user our session key String keyString = AESCrypto.asHex(dpSessionKey.getEncoded()); RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + inviteUser + ".pub"); BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(username + "," + keyString, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(messageCipher.toString()); sessionKeys.put(inviteUser, dpSessionKey); if ((clientResource.tabPanels.containsKey(inviteUser))) { print = (MapTextArea) clientResource.tabPanels.get(inviteUser); print.writeToTextArea("Inviting "+inviteUser+" to a Direct Protect session...\n", print.getSetHeaderFont(Color.gray)); } } catch (Exception ie) { } } /** * Invite buddies to a group chat * @param inviteUsers An array of usernames to invite * @param myChatUID The chatUID to invite the users to * @param chatName The name of the chat associated with the chatUID * @throws IOException */ public static void inviteUsers(String[] inviteUsers, String myChatUID, String chatName) throws IOException { //Get the session key for this chat String keyString = AESCrypto.asHex(sessionKeys.get(myChatUID).getEncoded()); //Send Aegis the information systemMessage("16"); c2sdout.writeUTF(encryptServerPublic(myChatUID)); c2sdout.writeUTF(encryptServerPublic(chatName)); c2sdout.writeUTF(encryptServerPublic(String.valueOf(inviteUsers.length))); for (int x = 0; x < inviteUsers.length; x++) { c2sdout.writeUTF(encryptServerPublic(inviteUsers[x])); RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + inviteUsers[x] + ".pub"); BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(myChatUID + "," + keyString, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(messageCipher.toString()); if (debug >= 1) { writeLog("INFO: Invited user: " + inviteUsers[x]); } } } /** * Leave a group chat * @param myChatUID The chatUID to depart from */ public static void leaveChat(String myChatUID) { SecretKeySpec skeySpec = sessionKeys.get(myChatUID); BigInteger messageBigInt = new BigInteger(AESCrypto.encryptMessage(skeySpec, "ChatLeave," + username)); //Alert the other users! systemMessage("17"); try { c2sdout.writeUTF(encryptServerPublic(myChatUID)); c2sdout.writeUTF(messageBigInt.toString()); } catch (Exception e) { e.printStackTrace(); } //Let Aegis know that we're leaving the chat systemMessage("15"); //Send Aegis the chatUID try { c2sdout.writeUTF(encryptServerPublic(myChatUID)); sessionKeys.remove(myChatUID); System.gc(); } catch (IOException e) { e.printStackTrace(); } } /** * This method sends the file to the other user (must initialize direct-connect first!) * @param myFile The filename to send * @throws IOException */ public static void sendFile(File myFile) throws IOException { toUser = clientResource.imTabbedPane.getTitleAt(clientResource.imTabbedPane.getSelectedIndex()); //TODO Send the file! //Grab the file size int fileSize = (int) myFile.length(); byte[] mybytearray = new byte[(int) myFile.length()]; FileInputStream fis = new FileInputStream(myFile); BufferedInputStream bis = new BufferedInputStream(fis); bis.read(mybytearray, 0, mybytearray.length); byte[] encryptedFile = encryptAES(toUser,mybytearray); fileSize = (int)encryptedFile.length; //Use process message to initiate the file transfer //Get my external IP systemMessage("13"); String myExternalIP = decryptServerPublic(c2sdin.readUTF()); systemMessage("21"); //Send the server the user to invite, the filename and then the file size InetAddress myLocalIP = InetAddress.getLocalHost(); // Get IP Address String inviteString = username + "," + myFile.getPath() + "," + String.valueOf(fileSize) + "," + myExternalIP + "," + myLocalIP.getHostAddress(); writeLog("INFO: Local external address: " + myExternalIP); writeLog("INFO: Local local address: " + myLocalIP.getHostAddress()); //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the toUser with the Server's public key and send it to the server //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(inviteString, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(encryptServerPublic(toUser)); c2sdout.writeUTF(messageCipher.toString()); } public static void transferFile(File myFile, String userTo) throws IOException { Thread sendFile = new fileSendThread(myFile,userTo); sendFile.start(); } /** * This method receives a file from a user (must initialize a direct-connect first!) * @throws IOException */ public static void receiveFile(String fromUser, String filePath, String fileSize, String sendersExternal, String sendersLocal) throws IOException { //Find the external IP of me systemMessage("13"); String myExternalIP = decryptServerPublic(c2sdin.readUTF()); if(myExternalIP.equals(sendersExternal)) { writeLog("INFO: File transfer user is on the same network. " + sendersExternal + ":" + myExternalIP); Thread getFile = new fileRecvThread(fromUser,filePath,fileSize,toUser,username, sendersLocal); getFile.start(); } else { writeLog("INFO: FIle transfer user is on a different network. " + sendersExternal + ":" + myExternalIP); Thread getFile = new fileRecvThread(fromUser,filePath,fileSize,toUser,username, sendersExternal); getFile.start(); } } /** * This method returns the cipher text of the message encrypted with a sessionKey * @param myChatUID The UID for the chat the message belongs to (to find the session key) * @param message The plaintext message * @return The encrypted message */ public static String encryptAES(String myChatUID, String message) { SecretKeySpec sessionKey = sessionKeys.get(myChatUID); BigInteger messageBigInt = new BigInteger(AESCrypto.encryptMessage(sessionKey, message)); return messageBigInt.toString(); } public static byte[] encryptAES(String myChatUID, byte[] message) { SecretKeySpec sessionKey = sessionKeys.get(myChatUID); byte[] messageBigInt = AESCrypto.encryptMessage(sessionKey, message); return messageBigInt; } /** * Decrypt a chat message using AES * @param myChatUID The UID the message is associated with (to find the session key) * @param message The message to decrypt * @return The decrypted message */ public static String decryptAES(String myChatUID, String message) { SecretKeySpec sessionKey = sessionKeys.get(myChatUID); BigInteger cipherBigInt = new BigInteger(message); return new String(AESCrypto.decryptMessage(sessionKey, cipherBigInt.toByteArray())); } public static byte[] decryptAES(String myChatUID, byte[] message) { SecretKeySpec sessionKey = sessionKeys.get(myChatUID); return AESCrypto.decryptMessage(sessionKey, message); } /** * This method takes the message the user types and will get it ready to send * @param message The message to send * @throws BadLocationException * @throws IOException */ public static void processMessage(String message) throws BadLocationException, IOException { //Is this a chat or IM tab? JPanel currentTab = (JPanel) clientResource.imTabbedPane.getSelectedComponent(); //This is a chat tab if (Integer.parseInt(currentTab.getName()) != -1) { //Prepend username and comma to message so we know who it's from. message = username + "," + message; //Sending information to Aegis systemMessage("17"); c2sdout.writeUTF(encryptServerPublic(currentTab.getName())); c2sdout.writeUTF(encryptAES(currentTab.getName(), message)); toUser = clientResource.imTabbedPane.getTitleAt(clientResource.imTabbedPane.getSelectedIndex()); print = (MapTextArea) clientResource.tabPanels.get(toUser); if (username.equals("null")) { print.writeToTextArea("Error: You are not connected!\n", print.getSetHeaderFont(new Color(130, 0, 0))); print.moveToEnd(); print.clearTextField(); } else { String[] localMessage = message.split(",", 2); //Print the message locally print.writeToTextArea(username + ": ", print.getSetHeaderFont(new Color(0, 130, 0))); //print.writeToTextArea(message+"\n", print.getTextFont()); parseMarkdown(localMessage[1], print); print.clearTextField(); } //This is an IM tab } else { //Get user to send message to from active tab toUser = clientResource.imTabbedPane.getTitleAt(clientResource.imTabbedPane.getSelectedIndex()); //Get the JPanel in the active tab print = (MapTextArea) clientResource.tabPanels.get(toUser); if (debug >= 1) { //writeLog("JPANEL : " + print.toString()); } //See if the user is logged in. If yes, send it. If no, error. if (debug >= 1) { //writeLog("USERNAME: " + username); } if (username.equals("null")) { print.writeToTextArea("Error: You are not connected!\n", print.getSetHeaderFont(new Color(130, 0, 0))); print.moveToEnd(); print.clearTextField(); } else { //Print the message locally print.writeToTextArea(username + ": ", print.getSetHeaderFont(new Color(0, 130, 0))); //print.writeToTextArea(message+"\n", print.getTextFont()); parseMarkdown(message, print); //Send the message try { if(sessionKeys.containsKey(toUser)){ c2sdout.writeUTF(encryptServerPublic(toUser)); c2sdout.writeUTF(encryptServerPublic(username)); c2sdout.writeUTF(encryptAES(toUser, message)); // Append own message to IM window print.moveToEnd(); // Clear out text input field print.clearTextField(); } else if(message.length() > 245) { double messageNumbers = (double) message.length() / 245; double messageNumbersInt = Math.ceil(messageNumbers); String[] messageChunks = new String[(int) messageNumbersInt]; for (int i = 0; i < messageChunks.length; i++) { int begin = i * 245; int end = begin + 245; if (end > message.length()) { end = message.length() - 1; } messageChunks[i] = message.substring(begin, end); //Check to see if this tab is a chat tab!!! // if(clientResource.) //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the toUser with the Server's public key and send it to the server //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(messageChunks[i], toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(encryptServerPublic(toUser)); c2sdout.writeUTF(encryptServerPublic(username)); c2sdout.writeUTF(messageCipher.toString()); //Hash the Message for the digital signature //String hashedMessage = ClientLogin.computeHash(message); // Append own message to IM window print.moveToEnd(); // Clear out text input field print.clearTextField(); } } else { //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(message, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(encryptServerPublic(toUser)); c2sdout.writeUTF(encryptServerPublic(username)); c2sdout.writeUTF(messageCipher.toString()); //TODO Hash the Message for the digital signature // Append own message to IM window print.moveToEnd(); // Clear out text input field print.clearTextField(); } //TADA } catch (IOException ie) { if (debug >= 1) { writeLog(getStackTraceAsString(ie)); } print.writeToTextArea("Error: You probably don't have the user's public key!\n", print.getTextFont()); print.moveToEnd(); print.clearTextField(); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); e.printStackTrace(); } } } System.gc(); } /** * Send a message to specified user (used for away message) * @param usertoreply * @param message * @throws BadLocationException */ public static void processMessage(String usertoreply, String message) throws BadLocationException { //Get user to send message to from active tab toUser = usertoreply; //Get the JPanel in the active tab print = (MapTextArea) clientResource.tabPanels.get(toUser); if (debug > 1) { writeLog("DEBUG: " + print.toString()); } //See if the user is logged in. If yes, send it. If no, error. if (username.equals("null")) { print.writeToTextArea("Error: You are not connected!\n", print.getSetHeaderFont(new Color(130, 0, 0))); print.moveToEnd(); print.clearTextField(); } else { //Print the message locally print.writeToTextArea(username + ": ", print.getSetHeaderFont(new Color(0, 130, 0))); parseMarkdown(message, print); //Send the message try { if (message.length() > 245) { double messageNumbers = (double) message.length() / 245; double messageNumbersInt = Math.ceil(messageNumbers); String[] messageChunks = new String[(int) messageNumbersInt]; for (int i = 0; i < messageChunks.length; i++) { int begin = i * 245; int end = begin + 245; if (end > message.length()) { end = message.length() - 1; } messageChunks[i] = message.substring(begin, end); //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the toUser with the Server's public key and send it to the server //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(messageChunks[i], toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(encryptServerPublic(toUser)); c2sdout.writeUTF(encryptServerPublic(username)); c2sdout.writeUTF(messageCipher.toString()); //Hash the Message for the digital signature //String hashedMessage = ClientLogin.computeHash(message); // Append own message to IM window print.moveToEnd(); // Clear out text input field print.clearTextField(); } } else { //Grab the other user's public key from file RSAPublicKeySpec toUserPublic = RSACrypto.readPubKeyFromFile("users/" + username + "/keys/" + toUser + ".pub"); //Encrypt the message with the toUser's public key and send it to the server BigInteger messageCipher = new BigInteger(RSACrypto.rsaEncryptPublic(message, toUserPublic.getModulus(), toUserPublic.getPublicExponent())); c2sdout.writeUTF(encryptServerPublic(toUser)); c2sdout.writeUTF(encryptServerPublic(username)); c2sdout.writeUTF(messageCipher.toString()); //TODO Hash the Message for the digital signature // Append own message to IM window print.moveToEnd(); // Clear out text input field print.clearTextField(); } //TADA } catch (IOException ie) { if (debug >= 1) { writeLog(getStackTraceAsString(ie)); } print.writeToTextArea("Error: You probably don't have the user's public key. Please add them to your contact list!\n", print.getSetHeaderFont(new Color(130, 0, 0))); print.moveToEnd(); print.clearTextField(); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); e.printStackTrace(); } } System.gc(); } /** This method adds a user to the buddylist * @param usernameToAdd This is the username you want to add * @throws Exception */ public static void buddyList(String usernameToAdd) throws Exception { //Add the username to a new line in the file //Will take in more inputs as we add other functionality to Athena like Pubkey, group, etc //Set exists to 0, this means that the usernameToAdd is not already in the buddylist file int exists = 0; BufferedWriter out; try { //This for loop checks to see if the usernameToAdd is already in the buddylist file, if so, set exists to 1 if (contactsTable.containsKey(usernameToAdd)) { exists = 1; } //If the usernameToAdd IS NOT in the buddylist file, add it if (exists == 0) { BigInteger encryptedUsername; //Append to the file the usernameToAdd File newFile = new File("users/" + username + "/buddylist.csv"); if (!(newFile.exists())) { boolean success = new File("users/" + username).mkdirs(); if (success) { newFile.createNewFile(); out = new BufferedWriter(new FileWriter("./users/" + username + "/buddylist.csv")); } else { newFile.createNewFile(); } } out = new BufferedWriter(new FileWriter("./users/" + username + "/buddylist.csv", true)); encryptedUsername = new BigInteger(descrypto.encryptData(usernameToAdd.concat(","+usernameToAdd.concat(",")))); //Add new username to contact alias hashtable with username as default alias contactsTable.put(usernameToAdd, usernameToAdd); out.write(encryptedUsername + "\n"); out.close(); } } catch (IOException e) { sendBugReport(getStackTraceAsString(e),null); e.printStackTrace(); } } /** * This method writes the buddy list to file * @param buddyList String array of the lines of the buddy list */ static void writeBuddyListToFile(String[] buddyList) { BigInteger encryptedUsername; BufferedWriter out; File newFile = new File("users/" + username + "/buddylist.csv"); try { if (!(newFile.exists())) { boolean success = new File("users/" + username).mkdirs(); if (success) { newFile.createNewFile(); } else { newFile.createNewFile(); } } out = new BufferedWriter(new FileWriter("./users/" + username + "/buddylist.csv")); for (int i = 0; i < buddyList.length; i++) { encryptedUsername = new BigInteger(descrypto.encryptData(buddyList[i].concat(","+contactsTable.get(buddyList[i]).concat(",")))); out.write(encryptedUsername + "\n"); } out.close(); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); if (debug >= 1) { writeLog("ERROR: Writing to buddylist."); } if (debug == 2) { e.printStackTrace(); } } } /** * This method writes the buddy list to file * @param buddyList String array of the lines of the buddy list * @param flag Differentiating parameter */ static void writeBuddyListToFile(String[] buddyList, boolean flag) { BigInteger encryptedUsername; BufferedWriter out; File newFile = new File("users/" + username + "/buddylist.csv"); try { if (!(newFile.exists())) { boolean success = new File("users/" + username).mkdirs(); if (success) { newFile.createNewFile(); } else { newFile.createNewFile(); } } out = new BufferedWriter(new FileWriter("./users/" + username + "/buddylist.csv")); for (int i = 0; i < buddyList.length; i++) { encryptedUsername = new BigInteger(buddyList[i]); out.write(encryptedUsername + "\n"); } out.close(); } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); if (debug >= 1) { writeLog("ERROR: Writing to buddylist."); } if (debug == 2) { e.printStackTrace(); } } } /** * Get the user's private key from the server * @throws IOException */ public static void receivePrivateKeyFromServer() throws IOException { systemMessage("007"); //Receive ack message c2sdin.readUTF(); int chunks = Integer.parseInt(c2sdin.readUTF()); //Grab the private key information from the server String finalPrivateMod = ""; String[] privateModArray = new String[chunks]; for (int x = 0; x < chunks; x++) { String privateMod = c2sdin.readUTF(); if (!(privateMod.equals("end"))) { if (privateModArray.length > 0) { privateModArray[x] = decryptServerPublic(privateMod); } } } if (privateModArray.length > 0) { finalPrivateMod = privateModArray[0]; // start with the first element for (int i = 1; i < privateModArray.length; i++) { finalPrivateMod = finalPrivateMod + privateModArray[i]; } } int expChunks = Integer.parseInt(c2sdin.readUTF()); //Grab the private key information from the server String finalPrivateExp = ""; String[] privateExpArray = new String[expChunks]; for (int x = 0; x < expChunks; x++) { String privateExp = c2sdin.readUTF(); if (!(privateExp.equals("end"))) { if (privateExpArray.length > 0) { privateExpArray[x] = decryptServerPublic(privateExp); } } } if (privateExpArray.length > 0) { finalPrivateExp = privateExpArray[0]; // start with the first element for (int i = 1; i < privateExpArray.length; i++) { finalPrivateExp = finalPrivateExp + privateExpArray[i]; } } if (debug == 2) { writeLog("INFO: Private modulus: " + finalPrivateMod); } if (debug == 2) { writeLog("INFO: Private exponent: " + finalPrivateExp); } BigInteger privateMod = new BigInteger(finalPrivateMod); BigInteger privateExp = new BigInteger(finalPrivateExp); //Write it to the file RSACrypto.saveToFile("users/" + username + "/keys/" + username + ".priv", privateMod, privateExp); } /** * Method receives the buddy list from Aegis * @throws IOException */ private static void receiveBuddyListFromServer() throws IOException { systemMessage("8");//Can't go over 007 //String array of the buddylist String[] buddyListLines; //Receive buddylist head(should be begin) //if(debug>=1)writeLog("BuddyList header: " + decryptServerPublic(c2sdin.readUTF())); //Parse out how many lines buddylist is buddyListLines = new String[(Integer.parseInt(decryptServerPublic(c2sdin.readUTF())))]; for (int y = 0; y < buddyListLines.length; y++) { buddyListLines[y] = decryptServerPublic(c2sdin.readUTF()); } writeBuddyListToFile(buddyListLines, true); if (debug >= 1) { writeLog("INFO: Successfully wrote Buddylist to file"); } } /** * Method gets the user's public key from Aegis * @param publicKeyToFind The user to find the key for * @throws IOException */ public static boolean getUsersPublicKeyFromAegis(String publicKeyToFind) throws IOException { //Send Aegis event code 004 to let it know what we're doing systemMessage("004"); c2sdout.writeUTF(encryptServerPublic(publicKeyToFind)); modOfBuddy = new BigInteger(c2sdin.readUTF()); if (modOfBuddy.toString().equals("-1")) { JOptionPane.showMessageDialog(null, "Cannot find user's public key.\n" + "Make sure you typed their username correctly and try again.", "Error Retrieving Key", JOptionPane.ERROR_MESSAGE); return false; } else { expOfBuddy = new BigInteger(c2sdin.readUTF()); writeBuddysPubKeyToFile(publicKeyToFind, modOfBuddy, expOfBuddy); } return true; } /** * This method writes the buddy list to file * @param buddysUsername * @param mod * @param exp * @throws IOException */ public static void writeBuddysPubKeyToFile(String buddysUsername, BigInteger mod, BigInteger exp) throws IOException { BufferedInputStream is; //Let's get the number of lines in the file File newFile = new File("users/" + username + "/keys/" + buddysUsername + ".pub"); if (!(newFile.exists())) { boolean success = new File("users/" + username + "/keys/").mkdirs(); if (success) { newFile.createNewFile(); is = new BufferedInputStream(new FileInputStream("users/" + username + "/keys/" + buddysUsername + ".pub")); RSACrypto.saveToFile("users/" + username + "/keys/" + buddysUsername + ".pub", mod, exp); } else { newFile.createNewFile(); RSACrypto.saveToFile("users/" + username + "/keys/" + buddysUsername + ".pub", mod, exp); } } } /** * Method sends the buddy list to Aegis * @throws IOException * @return success */ public static boolean sendBuddyListToServer() throws IOException { String[] buddylistArray = returnBuddyListArray(true); systemMessage("006"); int numLines = buddylistArray.length; //Send Aegis the begin message so it knows that this is beginning of the file c2sdout.writeUTF(encryptServerPublic("begin")); //Send Aegis the number lines we're sending c2sdout.writeUTF(encryptServerPublic(String.valueOf(numLines))); for (int x = 0; x < buddylistArray.length; x++) { //Now send Aegis the file c2sdout.writeUTF(encryptServerPublic(buddylistArray[x])); } return true; } /** * This encrypts the input string with the server's public key * @param plaintext The plaintext * @return cipherText - The encrypted String */ public static String encryptServerPublic(String plaintext) { BigInteger cipherText = new BigInteger(RSACrypto.rsaEncryptPublic(plaintext, Athena.serverPublic.getModulus(), Athena.serverPublic.getPublicExponent())); return cipherText.toString(); } /** * This method decrypts the input string with the server's public key * @param ciphertext * @return decrypted message */ public static String decryptServerPublic(String ciphertext) { //Turn the String into a BigInteger. Get the bytes of the BigInteger for a byte[] byte[] cipherBytes = (new BigInteger(ciphertext)).toByteArray(); //Decrypt the byte[], returns a String return RSACrypto.rsaDecryptPublic(cipherBytes, Athena.serverPublic.getModulus(), Athena.serverPublic.getPublicExponent()); } /** * This method returns a string array of the lines from the buddylist * @throws IOException Cannot read the file * @return String array of the buddylist */ public static void returnBuddyListArray() throws IOException { int count; int readChars; InputStream is; //Let's get the number of lines in the file File newFile = new File("users/" + username + "/buddylist.csv"); if (!(newFile.exists())) { boolean success = new File("users/" + username).mkdirs(); if (success) { newFile.createNewFile(); is = new BufferedInputStream(new FileInputStream("users/" + username + "/buddylist.csv")); } else { newFile.createNewFile(); } } is = new BufferedInputStream(new FileInputStream("users/" + username + "/buddylist.csv")); byte[] c = new byte[1024]; count = 0; readChars = 0; while ((readChars = is.read(c)) != -1) { for (int i = 0; i < readChars; ++i) { if (c[i] == '\n') { ++count; } } } //End section //Make the string array the size of the number of lines in the file //String[] usernames = new String[count]; //String[] aliases = new String[count]; //If there are no lines in the file we know that the user has no buddies! :( if (count == 0) { writeLog("WARN: No lines in the buddy list."); //return usernames; } else { File newFile2 = new File("users/" + username + "/buddylist.csv"); if (!(newFile2.exists())) { newFile2.createNewFile(); } BufferedReader in = new BufferedReader(new FileReader("users/" + username + "/buddylist.csv")); int x = 0; String raw, str; BigInteger strNum; //Split each line on every ',' then take the string before that and add it to the usernames array | God I love split. while ((raw = in.readLine()) != null) { // Read in the BigInteger in String form. Turn it to a BigInteger // Turn the BigInteger to a byteArray, and decrypt it. strNum = new BigInteger(raw); str = descrypto.decryptData(strNum.toByteArray()); String foo[] = str.split(","); if(foo.length==2) contactsTable.put(foo[0], foo[1]); else contactsTable.put(foo[0], foo[0]); x++; } //return usernames; } } //Retrieve aliases from buddy list from returnBuddyListArray method public static String[] getContactsArrayFromTable() { Enumeration userEnumeration = contactsTable.keys(); String[] contacts = new String[contactsTable.size()]; int count = 0; //Get all user names from the hashtable and return as array if(contacts.length > 0) { for (Enumeration<?> e = userEnumeration; e.hasMoreElements();) { contacts[count] = e.nextElement().toString(); count++; } } else { } return contacts; } //This method returns a nice string array full of the usernames (for now) that are in the buddylist file /** * An overload of returnBuddyListArray for some reason * @param flag Differentiating overload parameter * @return String[] of user's buddies * @throws IOException File not found */ public static String[] returnBuddyListArray(boolean flag) throws IOException { int count; int readChars; InputStream is; //Let's get the number of lines in the file File newFile = new File("users/" + username + "/buddylist.csv"); if (!(newFile.exists())) { boolean success = new File("users/" + username).mkdirs(); if (success) { newFile.createNewFile(); is = new BufferedInputStream(new FileInputStream("./users/" + username + "/buddylist.csv")); } else { newFile.createNewFile(); } } is = new BufferedInputStream(new FileInputStream("./users/" + username + "/buddylist.csv")); byte[] c = new byte[1024]; count = 0; readChars = 0; while ((readChars = is.read(c)) != -1) { for (int i = 0; i < readChars; ++i) { if (c[i] == '\n') { ++count; } } } //End section //Make the string array the size of the number of lines in the file String[] usernames = new String[count]; //If there are no lines in the file we know that the user has no buddies! :( if (count == 0) { return usernames; } else { File newFile2 = new File("users/" + username + "/buddylist.csv"); if (!(newFile2.exists())) { newFile2.createNewFile(); } BufferedReader in = new BufferedReader(new FileReader("users/" + username + "/buddylist.csv")); int x = 0; String raw; //Split each line on every ',' then take the string before that and add it to the usernames array | God I love split. while ((raw = in.readLine()) != null) { usernames[x] = raw; x++; } return usernames; } } /** * Method sets the away message text * @param toAwayText */ public static void setAwayText(String toAwayText) { awayText = toAwayText; } /** * Method sets the buddy status * @param status */ public static void setStatus(int status) { away = status; } /** * Send a message directly to Aegis * @param message The message to send */ public static void systemMessage(String message) { //Send the message try { //Send recipient's name and message to server c2sdout.writeUTF(encryptServerPublic("Aegis")); c2sdout.writeUTF(encryptServerPublic("")); //Blank username field c2sdout.writeUTF(encryptServerPublic(message)); } catch (IOException ie) { ie.printStackTrace(); } } /** * This sets the username * @param usernameToSet Username you want to set */ public static void setUsername(String usernameToSet) { username = usernameToSet; } /** * Method returns a DOUT for other classes to use * @return DataOutputStream c2sdout */ public static DataOutputStream returnDOUT() { return c2sdout; } /** * Method returns a DIN for other classes to use * @return DataInputStream c2sdin */ public static DataInputStream returnDIN() { return c2sdin; } /** * Method returns a hash of the local buddylist * @param buddyname Buddylist you want to find the hash of * @return String hash of the buddylist * @throws Exception */ public static String returnHashOfLocalBuddyList(String buddyname) throws Exception { String path = "users/".concat(buddyname).concat("/buddylist.csv"); File buddyList = new File(path); if (!buddyList.exists()) { boolean success = new File("users/" + username).mkdirs(); if (success) { buddyList.createNewFile(); } else { buddyList.createNewFile(); } } return FileHash.getMD5Checksum(path); } /** * Method returns the last date modified for the buddylist * @param buddyname Buddylist you want to find the lastModified from * @return long lastModified of the buddylist */ private static long returnLocalModDateOfBuddyList(String buddyname) { File buddylist = new File("users/" + buddyname + "/buddylist.csv"); return buddylist.lastModified(); } /** * Method returns the hash of the remote buddylist * @param buddyname Buddylist you want to find the hash of * @return The hash and date modified */ public static String[] returnHashOfRemoteBuddyList(String buddyname) { try { systemMessage("005"); //Send buddyname c2sdout.writeUTF(encryptServerPublic(buddyname)); String[] remoteValues = new String[2]; //counter int x = 0; while (x <= 1) { remoteValues[x] = decryptServerPublic(c2sdin.readUTF()); x++; } return remoteValues; } catch (Exception e) { sendBugReport(getStackTraceAsString(e),null); e.printStackTrace(); return null; } } /** * Enable or disable sound notifications * @param activated to enable or disable */ public static void setEnableSounds(boolean activated) { if (activated) { enableSounds = true; } else { enableSounds = false; } } public static void setSoundFiles(String msgSound, String inSound, String outSound) { currentMsgSound = "sounds/" + msgSound; currentInSound = "sounds/" + inSound; currentOutSound = "sounds/" + outSound; } /** * Are sounds enabled? * @return If sounds are enabled or not */ public static boolean getEnableSounds() { return enableSounds; } /** * Close all connections, and shut 'er down */ public static int disconnect() { try { writeLog("INFO: Disconnecting/Exiting"); closeLog(); //Exit all chats Enumeration userEnumeration = sessionKeys.keys(); //Get the outputStream for each socket and send message for (Enumeration<?> e = userEnumeration; e.hasMoreElements();) { String chatToLeave = e.nextElement().toString(); leaveChat(chatToLeave); System.out.println("Leaving chat: "+chatToLeave); } if (c2sdout != null) { c2sdout.close(); } if (c2cdout != null) { c2cdout.close(); } if (c2sdin != null) { c2sdin.close(); } if (c2cdin != null) { c2cdin.close(); } c2ssocket.close(); c2csocket.close(); connected = 0; away = 0; if (clientResource != null) { clientResource.setVisible(false); } return 0; } catch (Exception e) { e.printStackTrace(); return 0; } } /** * Exit the program */ public static void exit() { System.exit(0); } /** * Send a bug report to Aegis as a result of an exception * @param stackTrace the StackTrace of the exception */ public static void sendBugReport(String stackTrace,LoginProgress myloginBar) { System.out.println("In sendBugReport"); if(myloginBar != null){ System.out.println("closing the loginbar"); myloginBar.dispose(); } else{ System.out.println("There was no loginbar"); } int toSend = JOptionPane.showConfirmDialog(null, "Sorry, it looks like something went wrong.\n" + "Would you like to submit this as a bug report?", "File a Bug Report?", JOptionPane.YES_NO_OPTION); if (toSend == JOptionPane.YES_OPTION) { String comments = JOptionPane.showInputDialog("Optional: Any comments about this bug (what were you doing when this happened)?"); if (comments.equals("")) { comments = "No comment entered"; } systemMessage("8"); try { c2sdout.writeUTF(stackTrace); c2sdout.writeUTF(comments); } catch (Exception e) { e.printStackTrace(); } } System.exit(0); } /** * Turn a stackTrace into a string for transmission * @param e The stacktrace to convert * @return The stacktrace as a string */ public static String getStackTraceAsString(Exception e) { StringWriter stackTrace = new StringWriter(); e.printStackTrace(new PrintWriter(stackTrace)); return stackTrace.toString(); } public static void sendEmail(String to, String re, String body) throws IOException{ systemMessage("25"); c2sdout.writeUTF(encryptServerPublic(to)); c2sdout.writeUTF(encryptServerPublic(re)); c2sdout.writeUTF(encryptServerPublic(body)); } public static String[] getBlockList() throws IOException{ systemMessage("26"); String blockedUsers = decryptServerPublic(c2sdin.readUTF()); System.out.println("CURRENT BLOCKLIST: " + blockedUsers); String[] blockedUsersArray = blockedUsers.split(","); return blockedUsersArray; } /** * Spawn the login GUI * @param args nothing * @throws AWTException */ public static void main(String[] args) throws AWTException { loginGUI = new AuthenticationInterface(); } }