Working with JPasswordField and handling the password with getPassword + server login procedure

1k views Asked by At

After a day of long programming I want to post something useful for someone else.

Few days ago I was wondering how to handle JPasswordField's method getPassword() with a correct procedure to pass a value to a server and get an answer.

and this is the question:

How can I correctly get a value from a JPasswordField in a safe way and handle it to create a login procedure with a server?

1

There are 1 answers

5
Gianmarco On BEST ANSWER

This is the solution I reached.

First of all I decided the procedure of the login that was safe enough to my purpose, I didn't want to send a plain password to the server and I didn't want to store a plain password (obviously) in my database.

The first thing to say is that a good way to secure a password is that it is never ever exchanged over a network in a plain and readable form, this is obviously because of a possible "man in the middle", that in few words is someone reading your messages in their way to the server.

The password need to be hashed, that means that it is transformed to a quite long sequence of hexadecimal characters. The good thing of the hash is that is (hopefully) one-way. You can't de-hash a password.

There are many algorithms to do this, I choose the SHA256.

The password is then hashed, but just between us, this can be not enough. If an hacker is able to steal the hash there are some techniques that can bring him to a successful "translation" of it. Just to add a variable in his equation, and to make his life harder, we can add a salt to the password prior to hash it. A salt is a piece of string that is added in any position we desire to the password. This avoids some kind of attacks based on dictionary and most used password.

But if an hacker that is better trained than me can't read the password, how can I?

The answer is simple, I don't have to.

But to understand this we need to jump for a moment in the "Registration procedure" that is the moment when a new user is added to my database. This is it:

  1. The client ask the server to be registered sending the nickname.
  2. The server answer with a token that is the salt for the password.
  3. The client salt the password, hash it and send it back to the server.
  4. The server now receive something that is unreadable so there's no security problem, and store it with the nickname and the salt. The salted hashed password is the "common secret".

So the login procedure will be like this:

  1. The client ask the server to login
  2. the server answer with the salt
  3. the client salts the password then hashes it and sends it back to the server.
  4. the server compare the shared secret with the received string. If they are equals the user is allowed to login.

This should be quite fine, but in this case if an hacker knows the shared secret can access the server without any problem because doing so we just changed the password, not to be readable, but still usable directly.

To avoid this behavior we just have to add a passage in our chain:

  1. The client ask the server to login
  2. the server answers with the salt and a random session-salt
  3. the client salts the password, hashes it. At this point it salts again the hash and re-hash it. Then it send the hashed-salted-hash-of-salted-password back to the server
  4. the server takes the shared secret, salts it with the random session salt and then hashes it. If the two strings are equals then the user is allowed to login.

Now that the procedure is clear we have an issue to solve. If I handle any kind of String this can persist in the memory for long time, so if I put my password in a String it can be readable in a plain form for long time. This is not so good for us, but we are not the first to think about it, java indeed created a way to avoid this password persisting. The solution is use an array of characters. This is because even if the array is persisting in the memory, its datas are spread with no order in the memory and is very difficult to re-create the original password.

Re inventing hot water? Yep, just use the getPassword() method in a JPasswordField.

But this is quite difficult for newbie. We get a char[] array, and it is strange for a not expert.

The first thing that reach our mind can be to transform that array in a plain string ....... But is just what I want to avoid. So I need to handle the array as-is.

We need then a method to salt and hash the password, the result can be this:

public static String digestSalted(String salt, char[] password) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");

    ArrayList<Byte> list = new ArrayList<Byte>();
    for (int i = 0; i < password.length; i++) {
        //String ch = String.valueOf(password[i]);
        //byte[] b = ch.getBytes();
        //for (int j = 0; j < b.length; j++) {
        //  list.add(b[j]);
        //}
                    list.add((byte)password[i]);
    }
    byte[] saltInBytes = salt.getBytes();
    byte[] toBeHashed = new byte[(saltInBytes.length + list.size())];
    for (int i = 0; i < saltInBytes.length; i++) {
        toBeHashed[i] = saltInBytes[i];
    }
    for (int i = saltInBytes.length; i < list.size() + saltInBytes.length; i++) {
        toBeHashed[i] = list.get(i - saltInBytes.length);
    }

    md.update(toBeHashed);

    byte byteData[] = md.digest();

    StringBuffer hexString = new StringBuffer();
    for (int i = 0; i < byteData.length; i++) {
        String hex = Integer.toHexString(0xff & byteData[i]);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }
    return hexString.toString();

}

this method create an array of bytes passing through many little strings then append the salt. Once salted it hash the result with SHA256.

Now the return can be a string because is hashed and there is no problem of security.

This give a solution for the first part of the question.

The second part is just to implements our protocol between server and client.

I will only show the code in the client that is significant enough to understand the procedure. I am using a blocking queue where the messages is put when read from the socket. this is the code:

public void login(String nickname, char[] password) {
    if (cl == null) {
        throw new RuntimeException();
    }
    long s = Sys.getTime();
    cl.send("NICK " + nickname);
    IncomingMessage reply = null;
    try {
        reply = this.mh.getMessage(); //The response to NICK msg
        if (reply.getCommand().equalsIgnoreCase("LOGIN")) {
            ArrayList<String> params = reply.getParams();
            String accountSalt = params.get(0);
            String randomSalt = params.get(1);
            try {
                String sharedSecret = SHAHash.digestSalted(accountSalt, password);
                String saltedSharedSecret = SHAHash.digestSalted(randomSalt, sharedSecret);
                if (saltedSharedSecret != null) {
                    cl.send("PASS " + saltedSharedSecret);
                    reply = this.mh.getMessage();
                    if (reply.getCommand().equalsIgnoreCase("WELCOME") && reply.getParams().get(0).equals(nickname)) {
                        // ************ LOG ************ //
                        LOG.config("Logged in.");
                        // ***************************** //
                        this.running = true;
                        this.loggedIn = true;
                        mh.startExecutor();
                        LOG.config("Time passed: " + (Sys.getTime() - s));
                        mh.startGame();
                    } else {
                        // ************ LOG ************ //
                        LOG.warning("A problem has occured while trying to login to the server.");
                        // ***************************** //
                        JOptionPane.showMessageDialog(null, "Error while logging to the server, shutting down.\n- ERROR 006 -");
                        System.exit(0);
                    }
                }
            } catch (NoSuchAlgorithmException e) {
                // ************ LOG ************ //
                LOG.warning("Error while SHA hashing the password, shutting down.");
                // ***************************** //
                JOptionPane.showMessageDialog(null, "Error while SHA hashing the password, shutting down.\n- ERROR 005 -");
                System.exit(0);
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

The code, now that we have clear how the protocol works, it's easy to understand the thing that should be considered is that this.mh.getMessage() is a blocking method, this means that the thread will wait until something is available in the queue before trying to get it.

This (almost) how I solved my problem. Let me know if there is any error in the answer or if you need some clarification. I hope this will be useful to someone. Have a nice programming