Java - Generating a random salt isn't random

2020-02-26 14:13发布

问题:

I'm trying to generate a salt in Java to use with a hashing algorithm for secure password storage. I'm using the following code to create the random salt:

private static String getSalt() throws NoSuchAlgorithmException {
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    byte[] salt = new byte[16];
    sr.nextBytes(salt);
    System.out.println(salt.toString());
    return salt.toString();
}

Which should generate a completely secure, randomly generated salt to use in my hashing algorithm. When I run the code however, it keeps outputting the same salt every time... Indicating that the salt being generated isn't random at all.

For obvious security purposes, each user needs a unique salt however if I use this code each time a new account is created then every user will have the same salt, defeating the purpose of having it in the first place.

My question is this: Why does this keep giving me the same salt and what can I do to ensure the salt generated is completely random each time the code is run?

EDIT:

Thought I'd include the source code of the entire hashing program that has now been fixed and works properly. This is a simple prototype to simulate generating the hash upon creation of the account then checking the password when logging into the system.

package hashingwstest;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.Scanner;


public class HashingWSTest {

    public static void main(String[] args) throws NoSuchAlgorithmException {
        Scanner sc = new Scanner(System.in);
        System.out.print("Enter Password: ");
        String passwordToHash = sc.nextLine();

        byte[] bytes = getBytes();
        String salt = new String(bytes);

        String securePassword = hash256(passwordToHash, salt);
        System.out.println("Hash successfully generated");

        System.out.print("Enter your password again: ");
        String checkPassword = sc.nextLine();
        String checkHash = hash256(checkPassword,salt);
        if (checkHash.equals(securePassword)) {
            System.out.println("MATCH");
        }
        else {
            System.out.println("NO MATCH");
        }
    }

    private static String hash256(String passwordToHash, String salt) {
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(salt.getBytes());
            byte[] bytes = md.digest(passwordToHash.getBytes());
            StringBuilder sb = new StringBuilder();

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

    private static byte[] getBytes() throws NoSuchAlgorithmException {
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] bytes = new byte[16];
        sr.nextBytes(bytes);
        return bytes;
    }
}

回答1:

You are printing out the byte array itself, not its contents. You need to loop through the array to see what it contains.

Edit:

Also changed getSalt to return a byte array. It is not safe to return a String constructed from the byte array (with new String(salt)) as the byte sequence may not form a valid String.

import java.security.*;

public class Salt {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        getSalt();
    }
    private static byte[] getSalt() throws NoSuchAlgorithmException {
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[16];
        sr.nextBytes(salt);
        for(int i = 0; i<16; i++) {
            System.out.print(salt[i] & 0x00FF);
            System.out.print(" ");
        }
        return salt;
    }
}


回答2:

salt.toString is not returning content of the byte array but hashCode

If you replace to the sr.nextInt() on every request you will receive different value. If you print content of the byte array you will notice difference



回答3:

From the javadoc of java.security.SecureRandom.getInstance(String):

The returned SecureRandom object has not been seeded. To seed the returned object, call the setSeed method.

So the obvious answer is to call setSeed. However this may have it's problems when just using the time, because the seed can be guessed easily.

Another approach would be to share the secure random instance (as it is thread safe)



回答4:

You can use method

/**
 * Reseeds this random object, using the eight bytes contained
 * in the given <code>long seed</code>. The given seed supplements,
 * rather than replaces, the existing seed. Thus, repeated calls
 * are guaranteed never to reduce randomness.
 *
 * <p>This method is defined for compatibility with
 * <code>java.util.Random</code>.
 *
 * @param seed the seed.
 *
 * @see #getSeed
 */
public void setSeed(long seed)

passing e.g. current time