1

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;
    }
}
ChrisTansey
  • 40
  • 1
  • 7

4 Answers4

3

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;
    }
}
Neil Masson
  • 2,609
  • 1
  • 15
  • 23
  • The solution: The following code above to return the array of bytes. Back in the main I added 'String saltString = new String(salt)' then used 'saltString' in my hashing algorithm along with the password. – ChrisTansey Jan 28 '15 at 14:11
  • You need to use a single-byte codepage to generate your String. The byte values > 127 do not form valid UTF8 characters, as they must be followed by another byte. Use `saltString = new String(salt, "ISO8859-1")` – Neil Masson Jan 28 '15 at 14:19
  • It's working even without the additional parameter. When I add the parameter onto the end it's still generating pretty crazy strings like 'öhî,µ®ÕĬ'. – ChrisTansey Jan 28 '15 at 14:36
  • 2
    `new String(salt)` is platform-dependent, as the default codepage for the platform is used for the conversion. My Linux system uses UTF8, so all large values map to the same value, to represent an invalid byte sequence. This may work on your system, but you may lose randomness on another platform. Using ISO8859-1 will work on all platforms. – Neil Masson Jan 28 '15 at 15:30
  • I see, thank you very much. This has definitely helped since this will eventually be put into an android application. – ChrisTansey Jan 29 '15 at 08:39
  • Can you please elaborate, why do we do "& 0x00FF"? Also what if my salt is: new byte[32]? Will be still use same "& 0x00FF"? – LoveForDroid Apr 27 '17 at 16:37
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

baju
  • 511
  • 5
  • 19
0

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)

Community
  • 1
  • 1
SpaceTrucker
  • 13,377
  • 6
  • 60
  • 99
0

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

StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • Just passing time might not be a good idea, because a user record will often contain the time it was created at. When this is also known to an attacker the number of possible seeds reduces drastically. – SpaceTrucker Jan 28 '15 at 12:37
  • @SpaceTrucker it's enough to set the seed just once on start – StanislavL Jan 28 '15 at 12:42
  • But the code in question creates a new instance every time a salt is requested. – SpaceTrucker Jan 28 '15 at 12:43