<file_sep>package client_code;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.BlockingQueue;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;

public class MessageParser extends Thread
{
	BlockingQueue<InternalMessage> 	outQueue 		= null;
	private BouncyEncryption 		encryptor 		= null;
	private BufferedReader 			in 				= null;
	
	public MessageParser(BlockingQueue<InternalMessage> outQueue,BufferedReader in, BouncyEncryption encryptor)
	{
		this.encryptor = encryptor;
		this.outQueue 	= 	outQueue;
		this.in			= 	in;
	}
	
	public void run()
	{
		while(true)
		{
			String mess 			= null;
			String messDecrypted 	= null;
			
			try {mess = in.readLine();} catch (IOException e1) {e1.printStackTrace();}
			try {messDecrypted = encryptor.Decrypt(mess.getBytes());} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException | IOException e1) {e1.printStackTrace();}
			String[] splitMess = messDecrypted.split("\u001e");
			
			switch(splitMess[0])
			{
			case"CHAT_STARTED":	try {outQueue.put(new InternalMessage(splitMess[0],null,splitMess[1],splitMess[2], false));} catch (InterruptedException e) {e.printStackTrace();}
								break;
			case"UNREACHABLE": 	try {outQueue.put(new InternalMessage(splitMess[0], null,null,splitMess[1],false));} catch (InterruptedException e) {e.printStackTrace();}
								break;
			case"END_NOTIF":	try {outQueue.put(new InternalMessage(splitMess[0],null,splitMess[1],null,false));} catch (InterruptedException e) {e.printStackTrace();}
								break;
			case"CHAT":			try {outQueue.put(new InternalMessage(splitMess[0],splitMess[2],splitMess[1],null,false));} catch (InterruptedException e) {e.printStackTrace();}
								break;
			case"HISTORY_RESP":	try {outQueue.put(new InternalMessage(splitMess[0],splitMess[2],null,splitMess[1],false));} catch (InterruptedException e) {e.printStackTrace();}
								break;
			}
		}
	}
}
<file_sep>package client_code;

public class InternalMessage 
{
	String 	action 		= 	null;
	String 	data		=	null;
	boolean	isInternal	= 	true;
	String 	sessionID	=	null;
	String	client		=	null;
	
	
	public InternalMessage(String action, String data, String sessionID, String client, boolean isInternal)
	{
		
		this.action 		= 	action;
		this.data			=	data;
		this.sessionID		=	sessionID;
		this.client 		=	client;
		this.isInternal		=	isInternal;
	}


	public String getAction() {
		return action;
	}


	public void setAction(String action) {
		this.action = action;
	}


	public String getData() {
		return data;
	}


	public void setData(String data) {
		this.data = data;
	}


	public boolean isInternal() {
		return isInternal;
	}


	public void setInternal(boolean isInternal) {
		this.isInternal = isInternal;
	}


	public String getSessionID() {
		return sessionID;
	}


	public void setSessionID(String sessionID) {
		this.sessionID = sessionID;
	}


	public String getClient() {
		return client;
	}


	public void setClient(String client) {
		this.client = client;
	}
	
}
<file_sep>package client_code;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/*
 * Copyright (C) 2011 www.itcsolutions.eu
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1, or (at your
 * option) any later version.
 *
 * This file 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.
 *
 *
 */

/**
 *
 * @author Catalin - www.itcsolutions.eu
 * @version 2011
 *
 */

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Implementation of AES
 * Bouncy Castle API installed as a JCA provider
 * CBC mode for encryption and decryption
 * @author <NAME>
 */

public class BouncyEncryption {

    // The default block size
    public static int blockSize = 16;

    Cipher encryptCipher = null;
    Cipher decryptCipher = null;
    
    // Buffer used to transport the bytes from one stream to another
    byte[] buf = new byte[blockSize];       //input buffer
    byte[] obuf = new byte[512];            //output buffer

    // The key
    byte[] key = null;
    // The initialization vector needed by the CBC mode
    byte[] IV = null;

    public BouncyEncryption(int rand, int SK) throws NoSuchAlgorithmException{
        //for a 192 key you must install the unrestricted policy files
        //  from the JCE/JDK downloads page
    	
    	//Hash the rand and SK using SHA-256 and trunctuate to 128 bytes. This is our A8
    	StringBuilder sb = new StringBuilder();
    	sb.append(rand);
		sb.append(SK);
		
		MessageDigest digest = MessageDigest.getInstance("SHA-256");
		key = digest.digest((sb.toString()).getBytes(StandardCharsets.UTF_8));
		//key = Arrays.copyOfRange(key, 0, 127);
		
        //default IV value initialized with 0
        IV = new byte[blockSize];
    }

    public BouncyEncryption(String pass, byte[] iv){
        //get the key and the IV
        key = pass.getBytes();
        IV = new byte[blockSize];
        System.arraycopy(iv, 0 , IV, 0, iv.length);
    }
    public BouncyEncryption(byte[] pass, byte[]iv){
        //get the key and the IV
        key = new byte[pass.length];
        System.arraycopy(pass, 0 , key, 0, pass.length);
        IV = new byte[blockSize];
        System.arraycopy(iv, 0 , IV, 0, iv.length);
    }

    public void InitCiphers()
            throws NoSuchAlgorithmException,
            NoSuchProviderException,
            NoSuchProviderException,
            NoSuchPaddingException,
            InvalidKeyException,
            InvalidAlgorithmParameterException{
       //1. create the cipher using Bouncy Castle Provider
       encryptCipher =
               Cipher.getInstance("AES/CBC/NoPadding", "BC");
       //2. create the key
       SecretKey keyValue = new SecretKeySpec(key,"AES");
       //3. create the IV
       AlgorithmParameterSpec IVspec = new IvParameterSpec(IV);
       //4. init the cipher
       encryptCipher.init(Cipher.ENCRYPT_MODE, keyValue, IVspec);

       //1 create the cipher
       decryptCipher =
               Cipher.getInstance("AES/CBC/NoPadding", "BC");
       //2. the key is already created
       //3. the IV is already created
       //4. init the cipher
       decryptCipher.init(Cipher.DECRYPT_MODE, keyValue, IVspec);
    }

    public void ResetCiphers()
    {
        encryptCipher=null;
        decryptCipher=null;
    }

    public void CBCEncrypt(InputStream fis, OutputStream fos)
            throws IOException,
            ShortBufferException,
            IllegalBlockSizeException,
            BadPaddingException
    {
       //optionally put the IV at the beggining of the cipher file
       //fos.write(IV, 0, IV.length);

       byte[] buffer = new byte[blockSize];
       int noBytes = 0;
       byte[] cipherBlock =
               new byte[encryptCipher.getOutputSize(buffer.length)];
       int cipherBytes;
       while((noBytes = fis.read(buffer))!=-1)
       {
           cipherBytes =
                   encryptCipher.update(buffer, 0, noBytes, cipherBlock);
           fos.write(cipherBlock, 0, cipherBytes);
       }
       //always call doFinal
       cipherBytes = encryptCipher.doFinal(cipherBlock,0);
       fos.write(cipherBlock,0,cipherBytes);

       //close the files
       fos.close();
       fis.close();
    }
    public void CBCDecrypt(InputStream fis, OutputStream fos)
            throws IOException,
            ShortBufferException,
            IllegalBlockSizeException,
            BadPaddingException
    {
       // get the IV from the file
       // DO NOT FORGET TO reinit the cipher with the IV
       //fis.read(IV,0,IV.length);
       //this.InitCiphers();

       byte[] buffer = new byte[blockSize];
       int noBytes = 0;
       byte[] cipherBlock =
               new byte[decryptCipher.getOutputSize(buffer.length)];
       int cipherBytes;
       while((noBytes = fis.read(buffer))!=-1)
       {
           cipherBytes =
                   decryptCipher.update(buffer, 0, noBytes, cipherBlock);
           fos.write(cipherBlock, 0, cipherBytes);
       }
       //allways call doFinal
       cipherBytes = decryptCipher.doFinal(cipherBlock,0);
       fos.write(cipherBlock,0,cipherBytes);

       //close the files
       fos.close();
       fis.close();
    }
    
    public byte[] Encrypt(String s) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException, IOException
    {
    	String sn = s + "\u0003";
    	InputStream fis = new ByteArrayInputStream(sn.getBytes());
    	ByteArrayOutputStream bos = new ByteArrayOutputStream();
    	
    	CBCEncrypt(fis, bos);
    	byte[] buffer = bos.toByteArray();
    	return buffer;
    }
    
    public String Decrypt(byte[] encrypted) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException, IOException
    {
    	ByteArrayOutputStream bos = new ByteArrayOutputStream();
    	ByteArrayInputStream bis = new ByteArrayInputStream(encrypted);

    	
    	CBCDecrypt(bis,bos);
    	String unsplit = new String(bos.toByteArray());
    	String[] splitstrings = unsplit.split("\u0003");
    	
    	String strdecrypt = new String(splitstrings[0].getBytes());
    	return strdecrypt;
    }
    
}<file_sep>package client_code;

import java.io.*;

import java.util.Scanner;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.LinkedBlockingQueue;

import java.math.BigInteger;

import java.net.*;

import java.nio.charset.Charset;

import java.nio.charset.StandardCharsets;

import java.security.InvalidAlgorithmParameterException;

import java.security.InvalidKeyException;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.security.NoSuchProviderException;



import javax.crypto.BadPaddingException;

import javax.crypto.IllegalBlockSizeException;

import javax.crypto.NoSuchPaddingException;

import javax.crypto.ShortBufferException;





public class Client{



	private DatagramSocket 					datagramSocket ;

	private InetAddress 					address;

	private int 							port;

	private DatagramPacket 					packet;

	private byte[] 							buffer;

	private String 							username 				= 	null;

	private String 							clientID;

	private int 							secretkey 				= 	123456;

	private BouncyEncryption 				encryptor 				= 	null;

	private int 							cookie;

	private static boolean 						connected;

	private boolean 						serverConnect;

	private Scanner 						scanner 				= 	new Scanner(System.in);

	private MessageParser 					message_parser_thread 	= 	null;

	private CLI_Thread 						cli_thread 				= 	null;

	private Socket 							clientSocket;

	private BufferedReader 					in 						= 	null;

	private PrintWriter     				out 					= 	null;

	private BlockingQueue<InternalMessage> 	actionQueue 			= 	new LinkedBlockingQueue<InternalMessage>();

	boolean 								inChat					=	false;

	private	String							currentSessID			= 	null;

	private String							currentChatPartner		= 	null;



	Client ()throws SocketException, UnknownHostException{

		buffer = new byte[1024];

		datagramSocket = new DatagramSocket();

		address = InetAddress.getLocalHost();

		packet = new DatagramPacket(buffer, buffer.length, address, 8888);

		scanner = new Scanner (System.in);

	}



	public int sendLogin(String user)throws IOException, NoSuchAlgorithmException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException{

		// send "Hello" msg to SERVER

		System.out.println("Sending HELLO");

		byte[] buffer = user.getBytes();

		DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, 4445);

		// send packet

		datagramSocket.send(packet);



		// receive auth

		datagramSocket.receive(packet);

		String s = unpack(packet);

		// check to see if user exists on the server

    if (!s.contains("User")){

    	int rand = Integer.parseInt(s);

    	int key = rand + secretkey;



    	StringBuilder sb = new StringBuilder();

		sb.append(rand);

		sb.append(secretkey);

		key = Integer.parseInt(sb.toString());

		

		encryptor = new BouncyEncryption(rand,secretkey);

		encryptor.InitCiphers();

		

		

	

    

    	byte[] password = BigInteger.valueOf(key).toByteArray();

	

		MessageDigest md = MessageDigest.getInstance("MD5");

    	md.update(password);	

    	byte[] challenge = new byte[1024];

    	challenge = md.digest();

    

    	DatagramPacket cresponse = new DatagramPacket(

    	        challenge, challenge.length, address, 4445

    	        );

    

    	datagramSocket.send(cresponse);

    	buffer = new byte[1024];

    	packet = new DatagramPacket(

            buffer, buffer.length, address, 8888

            );

    	datagramSocket.receive(packet);

    

    	String strdecrypt = encryptor.Decrypt(packet.getData());

    	String [] str = strdecrypt.split(",");

   		cookie = Integer.parseInt(str[0]);

    	System.out.println(strdecrypt);



    	

    	// establish TCP connection

    	clientSocket = new Socket("localhost", Integer.parseInt(str[1]));

    	try {in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));} 	catch (IOException e) {System.out.println("In TCP_Welcome_Thread: Unable to create Buffered Reader");e.printStackTrace();}

		try {out = new PrintWriter(clientSocket.getOutputStream(), true);} 						catch (IOException e) {System.out.println("In TCP_Welcome_Thread: unable to create PrintWriter");e.printStackTrace();}

		out.println("CONNECT\u001e" + cookie);



		System.out.println(encryptor.Decrypt(in.readLine().getBytes()));

    	serverConnect = true;

    	return 1;

    }

    else{

    	System.out.println("User DNE");

    	// exit

    	datagramSocket.receive(packet);

    	String strdecrypt = encryptor.Decrypt(packet.getData());

    	System.out.println(strdecrypt);

    	return -1;

    	

    }



	}

                 

	public void chatRequest()throws IOException{

		String second_client;

		//Create the input and output streams so that we can communicate with the client

		

		// request connection with 2nd chat client

		 do {

		 	second_client = scanner.nextLine();

		 	// use bouncy encryption???



		 	out.println("CHAT_REQUEST\u001e" + second_client);

		 	System.out.println("dsf1");

		 	String msgRcv = in.readLine();

		 	System.out.println(msgRcv);

		 	String [] msgSplit = msgRcv.split("\u001e");

		 	/*

		 	DatagramPacket packet = Packet_Helpers.stringToPacket(second_client, address, 4445);

		 	datagramSocket.send(packet);

		 	// receive something from server and decrypt

		 	datagramSocket.receive(packet);

		 	String a = unpack(packet);

		 	String [] msgSplit = a.split("\u001e");

		 	// CHAT_STARTED\u001e{SESSION_ID}\u001e{CLIENT_B}*/

		 	if (msgSplit[0].equals("CHAT_STARTED")){

		 		System.out.println(msgSplit[0] + " " + msgSplit[1] + " " + msgSplit[2]);

		 		connected = true;

		 	}	 

		 	else 

		 		System.out.println("User DNE, try again");



		 }while (!connected);

		

	}



	public void sendMSG() throws ShortBufferException, IllegalBlockSizeException, BadPaddingException,

	IOException{

		// send message to server

		String msg;

		 do {

			System.out.print("[You]:    ");

			msg = scanner.nextLine();

			System.out.println (msg);

			if (msg.equals("End Chat") == true)

				 break;

			

			else

				{

					// else send msg to server

					//encrpyton

					out.println(msg);

				}

			}

			 while (true);



		// close TCP connections

		//endChat();

		System.out.println("Chat ended");

	}



	public void rcvMSG(DatagramPacket msg) throws IOException{

		datagramSocket.receive(packet);

		// gets msg

		String rcv = unpack(msg);

		// switch (rcv)??

			//case 1: 



			//case 2: 



			//case 3: ENDMSG

				// endChat();



			//case 4: EXIT

				// sendQuit();

	}



	public void endChat(){

			// end chat with 2nd client, but still maintain connection with UDP.

	}



	public void sendQuit(){

		// disconnect from server when user types "Log off"

	}



	public static String unpack(DatagramPacket p1)

	{

		// upaacks datagram into readable format

		String str = new String(

		p1.getData(),

		p1.getOffset(),

		p1.getLength(),

		StandardCharsets.UTF_8 // or some other charset

	);

	return str;

	}

	/*

	private void control(Client a) throws UnknownHostException, SocketException, IOException, NoSuchAlgorithmException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException

	{

		while(true)

		{

			InternalMessage action = null;

			try {action = actionQueue.take();} catch (InterruptedException e) {e.printStackTrace();}

			

			switch(action.getAction())

			{

			case	"CHAT_REQUEST":	

									break;

			}

			

			

		}

	}

	*/

	

	public void chat_state_machine(BlockingQueue<InternalMessage> actionQueue, PrintWriter out,BouncyEncryption encryptor ) throws InterruptedException, IOException

	{

		boolean isChatting = true;

		while(isChatting)

		{

			InternalMessage temp = null;

			temp = actionQueue.take();

			

			

				if(temp.isInternal())

				{

					//Messages from user

					switch(temp.getAction())

					{

					case "CHAT":		try {out.println(encryptor.Encrypt(temp.getAction() + "\u001e" +  currentSessID + "\u001e" + temp.getData()));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

										break;

					case "LOG_OFF":		try {out.println(encryptor.Encrypt(temp.getAction() + "\u001e" + currentSessID));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

										try {out.println(encryptor.Encrypt("DISCONNECT"));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

										out	.close();

										in	.close();

										out = null;

										in 	= null;

										isChatting = false;

										connected = false;

										break;

					case "END_REQUEST":	try {out.println(encryptor.Encrypt(temp.getAction() + "\u001e" + currentSessID));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

										isChatting = false;

										break;

					case "HISTORY_REQ":	try {out.println(encryptor.Encrypt(temp.getAction() + "\u001e" + temp.getClient()));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

										break;

					}

				}

				else

				{

					//messages from server

					switch(temp.getAction())

					{

					case "CHAT":			System.out.println(currentChatPartner + ": " + temp.getData());

											break;

					case "END_NOTIF":		System.out.println("Chat session " + currentSessID + " with " + currentChatPartner + " has ended.");

											currentSessID 		= null;

											currentChatPartner 	= null;

											isChatting 			= false;

											break;

					case "HISTORY_RESP":	System.out.println(temp.getData());

											break;

					}

				}

			

			

		}

	}

	

	

	

public static void main(String[] args) throws UnknownHostException, SocketException, IOException, NoSuchAlgorithmException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException, InterruptedException  {



	Client a = new Client();

	

	while(true)

	{

		System.out.println("Type \"Log On\" to begin connection to Server.");

		String input = a.scanner.nextLine();

		if(input.toUpperCase().equals("LOG ON"))

		{

			System.out.println("Enter your Username: ");

			String username = a.scanner.nextLine();

			if(a.sendLogin(username) > 0)

			{

				connected 				= true;

				a.message_parser_thread = new MessageParser(a.actionQueue, a.in, a.encryptor);

				a.cli_thread			= new CLI_Thread(a.actionQueue);

				a.message_parser_thread	.start();

				a.cli_thread			.start();

				

				while(a.connected)

				{

					InternalMessage temp = a.actionQueue.take();

					

					if(temp.isInternal())

					{

						switch(temp.getAction())

						{

							case "CHAT_REQUEST":	a.out.println(a.encryptor.Encrypt(temp.getAction() + "\u001e" + temp.getClient()));

													a.currentChatPartner = temp.getClient();

													break;

	

							case "HISTORY_REQ":		try {a.out.println(a.encryptor.Encrypt(temp.getAction() + "\u001e" + temp.getClient()));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

													break;



							case "LOG_OFF":			try {a.out.println(a.encryptor.Encrypt("DISCONNECT"));} catch (ShortBufferException | IllegalBlockSizeException | BadPaddingException| IOException e) {e.printStackTrace();}

													a.out		.close();

													a.in		.close();

													a.out 		= null;

													a.in 		= null;

													connected 	= false;

													break;

						}

					}

					else

					{

						switch(temp.getAction())

						{

						case"CHAT_STARTED":	a.currentChatPartner 	= temp.getClient();

											a.currentSessID			= temp.getSessionID();

											System.out.println("Chat session " + a.currentSessID + " with " + a.currentChatPartner + " has begun.");

											a.chat_state_machine(a.actionQueue,a.out,a.encryptor);

											break;

											

						case"UNREACHABLE": 	System.out.println("User " + a.currentChatPartner + " is unreachable.");

											a.currentChatPartner = null;

											break;

					

						case"HISTORY_RESP":	System.out.println(temp.getData());

											break;

						}

					}

				}

			}

		}

		//a.sendLogin("UserA");

		//a.chatRequest();

	}

}
}

