0

So, I am currently learning SpringBoot and right now I am trying to store a password together with other data for the users in a database (MySql). In order for it to be secure I am using salted hashes to store. Generating these hashes and storing them in the database works fine, however when I try to validate the password by taking the salt and the password, I am getting a different hash, therefore the wrong result.

Below is my code.

First of: The class User where I start the validation

@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long uID;

@NotNull
private String nname;

@NotNull
private String vname;
private String email;
private String telnr;
@Embedded
private Address address;

@NotNull
private boolean isAdmin;

private String hash;

// Default Constructor
public User() {
}

// Constructor
public User(String name, String vname, String email, String telnr, Address address, boolean isAdmin,
        String password) throws NoSuchAlgorithmException {
    HashHelper hashHelper = new HashHelper();

    this.nname = name;
    this.vname = vname;
    this.email = email;
    this.telnr = telnr;
    this.address = address;
    this.isAdmin = isAdmin;
    this.hash = hashHelper.createHash(password);
}

public boolean validateHash(String password) {
    HashHelper hashHelper = new HashHelper();
    // Get the used Salt
    String[] parts = this.hash.split(":");
    byte[] salt = parts[0].getBytes();

    // Create Hash with old salt
    String newHash = hashHelper.getHash(password, salt);
    if (parts[1] == newHash) {
        return true;
    }

    return false;
}

Second, my class HashHelper where I handle everything to do with hashing. I use createHash whenever a new password gets stored (therefore, new salt) and getHash for validation with a specific salt.

public class HashHelper {

    public HashHelper() {
    }

    public byte[] getSalt() throws NoSuchAlgorithmException {

        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[16];
        sr.nextBytes(salt);

        return salt;
    }

// Create Salt and Hash and store them, seperated by :
        public String createHash(String password) throws NoSuchAlgorithmException {
            String hash = null;
            byte[] salts = getSalt();

        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");

            md.update(salts);

            byte[] bytes = md.digest(password.getBytes());
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < bytes.length; i++) {
                sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
            }

            hash=salts.toString() + ":" + sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        System.out.println("Hash: " + hash);
        return hash;
    }

    public String getHash(String password, byte[] salt) {
        String hash = "";
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");

            md.update(salt);

            byte[] bytes = md.digest(password.getBytes());
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < bytes.length; i++) {
                sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
            }

            hash = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return hash;
    }

}

The call for validation is set in a CommandLineRunner just for testing and is the following:

    Optional<User> user = userRepository.findById((long)10);
    if (user.get().validateHash("Password")) {
            System.out.println("Correct Password");
    }
    else {
            System.out.println("Wrong password");
    }

I think it was something to do with the getBytes() and toString() methods, because byte[] salt seems to have a shorter length when I try to validate it (around 11-12 bytes instead of 16) but I can't figure out why. Any help would be greatly appreciated!

  • Possible duplicate of [How do I compare strings in Java?](https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) – luk2302 Jun 03 '19 at 15:24
  • Without reading through the entire question: your wording is wrong, please correct it. You ***never*** decrypt a hash, firstly that is fundamentally the wrong word since you did not *en*crypt in the first place, you hashed. So the thing you would do is *de*hash which is the exact thing you cannot do with hashes. – luk2302 Jun 03 '19 at 15:25
  • should be `parts[1].equals(newHash)`. – luk2302 Jun 03 '19 at 15:26
  • Oh yeah, I just noticed and corrected it @luk2302. However, it still returns a different hash - the error is not when comparing them. – Dominik Albert Jun 03 '19 at 15:29
  • 3
    It looks like you are generating a random salt each time you call the function getSlat() - from my knowledge you should have a way to use the same salt prefix/suffix when both creating the password and validating it. – Shuky Capon Jun 03 '19 at 15:31
  • @ShukyKappon That's what I am trying. getSalt only get's called when creating a new Hash, otherwise I read out the old salt in validateHash, then create a hash with this salt and the provided password. That's why there are createHash and getHash – Dominik Albert Jun 03 '19 at 15:35

1 Answers1

2

Your question is moot, because you shouldn't use this algorithm to hash passwords, as it's not secure. Even if you aren't protecting anything important, users might use the same password in multiple places, and when an attacker cracks your insecure password table, they will be able to use many of those passwords elsewhere.

Use Argon2, or at least PBKDF2 to hash passwords, with at least 10,000 rounds of hashing.

The reason you are having trouble here is that you aren't storing the salt you use to hash the password. Without it, you'll never be able to compute the same hash. The problem is salts.toString() in the createHash() method:

hash=salts.toString() + ":" + sb.toString();

Calling toString() on a byte[] doesn't tell you anything about the content of the array. That's why you went to all the trouble to convert the result of the digest, bytes, into hexadecimal. You should do something similar to the salt.

Likewise, calling getBytes() on a string just encodes the string with the default encoding for your platform. That's not what you want.

Make sure that you use their equals() method when you compare String instances. The == operator will only tell you if they are the identical instance.

When storing byte arrays, which you'll still need to do with a good hash algorithm, I recommend using base-64, since there is great support for it in java.util.Base64, and it will yield more compact encodings of your array.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Thanks. I will call it a day now and try it out tomorrow, then report back here. – Dominik Albert Jun 03 '19 at 15:44
  • Check out my answer here for a more complete example: https://stackoverflow.com/a/2861125/3474 – erickson Jun 03 '19 at 23:21
  • I swapped SHA-256 to PBKDF2WithHmacSHA1 now for more security and used your suggestion with Base64 and got it to work now. Thanks for your info and the link to the example, was a great help! @erickson – Dominik Albert Jun 04 '19 at 09:58