Android Open Source - CipherChat Client Handler






From Project

Back to project page CipherChat.

License

The source code is released under:

MIT License

If you think the Android project CipherChat listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.desperate;
/*from   www .j a  v a2  s . c o  m*/
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLSocket;

import com.desperate.common.Message;
import com.desperate.common.NoncePacket;
import com.desperate.common.SessionKeyRequestInfo;
import com.desperate.common.Utilities;
import com.desperate.common.messages.IPMessage;
import com.desperate.common.messages.LoginMessage;
import com.desperate.common.messages.LogoutMessage;
import com.desperate.common.messages.RegisterMessage;
import com.desperate.common.messages.SessionKeyRequestMessage;
import com.desperate.common.messages.UserListMessage;
import com.desperate.common.replies.IPReplyMessage;
import com.desperate.common.replies.LoginReplyMessage;
import com.desperate.common.replies.LogoutReplyMessage;
import com.desperate.common.replies.RegisterReplyMessage;
import com.desperate.common.replies.ReplyMessage;
import com.desperate.common.replies.SessionKeyReplyMessage;
import com.desperate.common.replies.UserListReplyMessage;

public class ClientHandler implements Runnable {

  private SSLSocket clientSocket;
  private ObjectOutputStream outStream;
  private ObjectInputStream inStream;

  /** True while client is active with server */
  private transient boolean isActive;

  private User user;

  public ClientHandler(SSLSocket clientSocket) {

    this.clientSocket = clientSocket;
  }

  /**
   * @see java.lang.Runnable#run()
   */
  @Override
  public void run() {

    try {
      System.out.println("Accepted" + clientSocket.getRemoteSocketAddress().toString());

      outStream = new ObjectOutputStream(clientSocket.getOutputStream());
      inStream = new ObjectInputStream(clientSocket.getInputStream());

      // Receive messages, follow protocol
      Message requestMessage;

      isActive = true;
      boolean isLoggedIn = false;

      // Attempt to login or register before handling any other messages
      try {
        while (isLoggedIn == false && (requestMessage = (Message) inStream.readObject()) != null) {

          System.out.println(requestMessage.toString());
          
          if (requestMessage instanceof RegisterMessage) {
            handleRegister((RegisterMessage) requestMessage);
          }

          else if (requestMessage instanceof LoginMessage) {
            handleLogin((LoginMessage) requestMessage);
            isLoggedIn = true;
          }
        }
      } catch (Exception e) {

        if (user != null)
          user.setOnlineStatus(false);
        System.out.println("Client " + clientSocket.getRemoteSocketAddress().toString() + " could not register or log in.");
        clientSocket.close();
        return;
      }

      // Handle other messages
      try {
        while (isActive && (requestMessage = (Message) inStream.readObject()) != null) {

          // if (requestMessage instanceof RegisterMessage) {
          // handleRegister((RegisterMessage) requestMessage);
          // }
          //
          // else if (requestMessage instanceof LoginMessage) {
          // handleLogin((LoginMessage) requestMessage);
          // }
          
          System.out.println(requestMessage.toString());

          if (requestMessage instanceof LogoutMessage) {
            handleLogout((LogoutMessage) requestMessage);
            break;
          }

          else if (requestMessage instanceof UserListMessage) {
            handleUserListRequest((UserListMessage) requestMessage);
          }

          else if (requestMessage instanceof IPMessage) {
            handleIPRequest((IPMessage) requestMessage);

          } else if (requestMessage instanceof SessionKeyRequestMessage) {
            handleSessionKeyRequest((SessionKeyRequestMessage) requestMessage);
          }
        }
      } catch (RuntimeException e) {

        e.printStackTrace();
        if (user != null)
          user.setOnlineStatus(false);
      }

      // Out of listening loop

      System.out.println("Closing socket.");
      System.out.println("User " + user != null ? user.userName : clientSocket.getRemoteSocketAddress().toString() + " disconnected.");
      if (user != null) {
        user.setOnlineStatus(false);
      }

      clientSocket.close();
    } catch (IOException e) {

      System.out.println("IOException probably because client " + (user != null ? user.userName : clientSocket.getRemoteSocketAddress())
          + " committed sudoku. Socket closed.");

      if (user != null)
        user.setOnlineStatus(false);

    } catch (ClassNotFoundException e) {
      System.err.println("Client sent unknown message.");
      e.printStackTrace();
    }
  }

  private void handleLogin(LoginMessage message) {

    LoginReplyMessage reply = new LoginReplyMessage();

    String mRemote = message.remoteHMAC;
    String mServer = message.computeHMAC(message.password);

    // Check HMAC
    if (!mRemote.equals(mServer)) {
      // Login rejected
      sendErrorMessage(reply, "Login failed: wrong HMAC");
      return;
    }

    // Check freshness
    if (!message.verifyTimestamp(Utilities.tolerance)) {

      // Login rejected due to time-out
      sendErrorMessage(reply, "Login timed out");
    }

    // Try to find user
    User user;
    try {

      user = CryptoServer.db.getUser(message.username);

    } catch (Exception e) {

      sendErrorMessage(reply, "User does not exist, please register.");
      return;
    }

    // Attempt to authenticate with given password
    boolean loginStatus;
    try {
      loginStatus = user.authenticate(message.password);
      user.setOnlineStatus(true);

    } catch (NoSuchAlgorithmException e) {

      sendErrorMessage(reply, "Log-in failed: Could not authenticate");
      return;
    }

    if (loginStatus == false) {

      sendErrorMessage(reply, "Log-in failed: Wrong password.");

      return;
    }

    // Login successful - reply to client with KEK
    this.user = user;
    user.ip = clientSocket.getInetAddress();

    reply = new LoginReplyMessage();

    reply.timestamp = System.currentTimeMillis();
    reply.accepted = true;
    reply.keyEncryptionKey = user.getKeyEncryptionKey();

    reply.remoteHMAC = reply.computeHMAC(message.password);

    reply.send(outStream);

    return;

  }

  private void handleLogout(LogoutMessage message) {

    LogoutReplyMessage reply = new LogoutReplyMessage();

    String mRemote = message.remoteHMAC;
    String mServer = message.computeHMAC(user.getStrKeyEncryptionKey());

    // Check HMAC
    if (!mRemote.equals(mServer)) {
      // Logout rejected
      sendErrorMessage(reply, "Invalid Log-Out Message.");

      return;
    }

    if (!message.verifyTimestamp(Utilities.tolerance)) {
      sendErrorMessage(reply, "Log-Out Message Time-out.");

      return;
    }

    // Finish Thread on user log-out
    isActive = false;

    user.setOnlineStatus(false);
    System.out.print("User" + user.userName + "has logged out.");

    return;
  }

  private void handleRegister(RegisterMessage requestMessage) {

    RegisterReplyMessage reply = new RegisterReplyMessage(false, System.currentTimeMillis());

    // Don't let already-logged-in user register again
    if (user != null) {
      sendErrorMessage(reply, "You are already logged in.");
      return;
    }

    if (!requestMessage.verifyTimestamp(Utilities.tolerance)) {
      sendErrorMessage(reply, "Message expired.");
      return;
    }

    User newUser;
    try {
      newUser = new User(requestMessage.username, requestMessage.password, clientSocket.getInetAddress(), clientSocket);

    } catch (NoSuchAlgorithmException e) {

      sendErrorMessage(reply, "Unable to create user.");
      e.printStackTrace();
      return;
    }

    if (CryptoServer.db.addUser(newUser)) {
      // User was accepted
      this.user = newUser;

      reply = new RegisterReplyMessage(true, System.currentTimeMillis());
      reply.remoteHMAC = reply.computeHMAC(requestMessage.password);
      reply.send(outStream);
      return;

    } else {
      sendErrorMessage(reply, "Registration failed: user already exists.");
      return;
    }
  }

  private void handleIPRequest(IPMessage message) {

    IPReplyMessage reply = new IPReplyMessage();

    String mRemote = message.remoteHMAC;
    String mServer = message.computeHMAC(message.username);

    // Check HMAC
    if (!mRemote.equals(mServer)) {
      // IPRequest rejected
      sendErrorMessage(reply, "Invalid IPRequest Message: wrong HMAC");

      return;
    }

    // Check timestamp
    if (!message.verifyTimestamp(Utilities.tolerance)) {
      sendErrorMessage(reply, "User IP request message time-out.");

      return;
    }

    reply.IPaddress = CryptoServer.db.getIP(message.username);

    if (reply.IPaddress == null) {
      sendErrorMessage(reply, "User " + message.username + " is offline.");
      return;
    }

    reply.username = message.username;
    reply.timestamp = System.currentTimeMillis();

    reply.remoteHMAC = reply.computeHMAC(message.username);

    reply.send(outStream);

    return;
  }

  /**
   * Handles the following messages
   * <p>
   * <b>A -> KDC:</b> A, B, Ra, Eb(A, Rb) <b><br>
   * KDC -> A:</b> Ea(Ra, B, Ks, Eb(A, Rb, Ks)))
   * 
   * @param message
   */
  private void handleSessionKeyRequest(SessionKeyRequestMessage message) {

    SessionKeyReplyMessage reply = new SessionKeyReplyMessage();

    String localHMAC = message.computeHMAC(user.getStrKeyEncryptionKey());

    // Check HMAC validity
    if (!message.remoteHMAC.equals(localHMAC)) {
      sendErrorMessage(reply, "Session key request rejected: wrong HMAC.");
      return;
    }

    if (!message.verifyTimestamp(Utilities.tolerance)) {
      sendErrorMessage(reply, "Chat buddy resquest time-out.");
      return;
    }

    // Verify if users exist
    User userA;
    User userB;
    try {
      userA = CryptoServer.db.getUser(message.usernameA);
      userB = CryptoServer.db.getUser(message.usernameB);
    } catch (Exception e) {
      sendErrorMessage(reply, "Invalid users.");
      e.printStackTrace();
      return;
    }

    // Get keys from users
    SecretKeySpec keyA = userA.getKeyEncryptionKey();
    SecretKeySpec keyB = userB.getKeyEncryptionKey();

    NoncePacket bInfo;

    // Decrypt B's encrypted packet containing its freshness nonce, to append the session key inside
    try {

      bInfo = (NoncePacket) Utilities.decipherObject(message.userBPacket, keyB);

    } catch (Exception e) {

      sendErrorMessage(reply, "Could not verify other user's permission to start chat.");
      e.printStackTrace();
      return;
    }

    // confirm if requester is who he says it is
    if (!message.usernameA.equals(bInfo.requesterUserName)) {
      sendErrorMessage(reply, "Wrong request for chat session.");
      return;
    }

    // Generate a session key for A and B to use in communication
    SecretKeySpec sessionKey;
    try {

      KeyGenerator keyGen = KeyGenerator.getInstance(Utilities.cipherAlgorithm);
      keyGen.init(Utilities.keySize);
      sessionKey = (SecretKeySpec) keyGen.generateKey();

    } catch (NoSuchAlgorithmException e) {

      sendErrorMessage(reply, "Could not create session key for secure communication.");
      e.printStackTrace();
      return;
    }

    // Append session key
    bInfo.sessionKey = sessionKey;

    // Re-cipher nonce packet
    byte[] cipheredNonce;
    try {

      cipheredNonce = Utilities.cipherObject(bInfo, keyB);

    } catch (Exception e) {

      sendErrorMessage(reply, "Could not create session key for secure communication.");
      e.printStackTrace();
      return;
    }

    SessionKeyRequestInfo replyInfo = new SessionKeyRequestInfo(userB.userName, message.randomA, sessionKey, cipheredNonce);

    // Cipher the whole message contents with A's key
    byte[] cipheredReplyInfo;
    try {

      cipheredReplyInfo = Utilities.cipherObject(replyInfo, keyA);
    } catch (Exception e) {

      sendErrorMessage(reply, "Could not cipher KDC response");
      e.printStackTrace();
      return;
    }

    reply.cipheredPacket = cipheredReplyInfo;

    // Assemble rest of reply
    reply.accepted = true;
    reply.timestamp = System.currentTimeMillis();
    reply.remoteHMAC = reply.computeHMAC(user.getStrKeyEncryptionKey());

    // Send reply
    reply.send(outStream);

    return;
  }

  /**
   * Sends the list of all online users to the client, except the client himself.
   * 
   * @return true if the request and response were successfully formed
   */
  private void handleUserListRequest(UserListMessage message) {

    UserListReplyMessage userList = new UserListReplyMessage();

    String mRemote = message.remoteHMAC;
    String mServer = message.computeHMAC(user.getStrKeyEncryptionKey());

    // Check HMAC
    if (!mRemote.equals(mServer)) {
      // Request rejected
      sendErrorMessage(userList, "Invalid Log-Out Message: wrong HMAC");
      return;
    }

    if (!message.verifyTimestamp(Utilities.tolerance)) {
      sendErrorMessage(userList, "User List request message time-out.");
      return;
    }

    userList.usernames = CryptoServer.db.getOnlineUserList(user);
    userList.timestamp = System.currentTimeMillis();
    userList.remoteHMAC = userList.computeHMAC(user.getStrKeyEncryptionKey());
    userList.send(outStream);

    return;
  }

  private void sendErrorMessage(ReplyMessage message, String error) {
    message.accepted = false;
    message.errorMessage = error;
    message.timestamp = System.currentTimeMillis();
    message.send(outStream);

    System.out.println("error for client " + ((user != null && user.userName != null) ? user.userName : "???") + ": "
        + message.getClass().getSimpleName());
  }
}




Java Source Code List

com.desperate.AdminConsole.java
com.desperate.ClientHandler.java
com.desperate.CryptoServer.java
com.desperate.UserDatabase.java
com.desperate.User.java
com.desperate.common.Message.java
com.desperate.common.NoncePacket.java
com.desperate.common.SessionKeyRequestInfo.java
com.desperate.common.TestCipherSerializable.java
com.desperate.common.Utilities.java
com.desperate.common.messages.ChatMessage.java
com.desperate.common.messages.IPMessage.java
com.desperate.common.messages.LoginMessage.java
com.desperate.common.messages.LogoutMessage.java
com.desperate.common.messages.RegisterMessage.java
com.desperate.common.messages.SessionKeyRequestMessage.java
com.desperate.common.messages.StartChatMessage.java
com.desperate.common.messages.UserListMessage.java
com.desperate.common.replies.CheckSessionMessage.java
com.desperate.common.replies.IPReplyMessage.java
com.desperate.common.replies.LoginReplyMessage.java
com.desperate.common.replies.LogoutReplyMessage.java
com.desperate.common.replies.NeedhamSchroederSuccessReply.java
com.desperate.common.replies.RegisterReplyMessage.java
com.desperate.common.replies.ReplyMessage.java
com.desperate.common.replies.SessionKeyReplyMessage.java
com.desperate.common.replies.StartChatReply.java
com.desperate.common.replies.UserListReplyMessage.java
com.desperate.debug.DebugClient.java
com.desperate.debug.DebugCryptoClient.java
com.desperate.debug.PlainServer.java
com.ist.cipherchat.gui.ChatActivity.java
com.ist.cipherchat.gui.ChooseServerActivity.java
com.ist.cipherchat.gui.Contacts.java
com.ist.cipherchat.gui.Origin.java
com.ist.cipherchat.networking.ChatActivityRunnable.java
com.ist.cipherchat.networking.ChatInRunnable.java
com.ist.cipherchat.networking.ChatOutHandler.java
com.ist.cipherchat.networking.Core.java
com.ist.cipherchat.networking.Globals.java
com.ist.cipherchat.networking.OutputSocketHandler.java
com.ist.cipherchat.networking.PhoneServerSocketHandler.java
com.ist.cipherchat.networking.ThreadComm.java