Java - генерация случайной соли не случайна
Я пытаюсь создать соль в Java для использования с алгоритмом хеширования для безопасного хранения пароля. Я использую следующий код для создания случайной соли:
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();
}
Который должен генерировать полностью безопасную, случайно сгенерированную соль для использования в моем алгоритме хеширования. Однако когда я запускаю код, он продолжает выводить одну и ту же соль каждый раз... Указывая, что генерируемая соль вовсе не случайна.
В очевидных целях безопасности каждому пользователю нужна уникальная соль, однако, если я буду использовать этот код каждый раз, когда создается новая учетная запись, то у каждого пользователя будет одна и та же соль, что в первую очередь побеждает цель ее наличия.
У меня такой вопрос: почему это дает мне одну и ту же соль и что я могу сделать, чтобы генерируемая соль была абсолютно случайной при каждом запуске кода?
РЕДАКТИРОВАТЬ:
Думаю, я бы включил исходный код всей программы хеширования, которая теперь исправлена и работает правильно. Это простой прототип для имитации генерации хеша при создании учетной записи и проверки пароля при входе в систему.
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;
}
}
4 ответа
Вы распечатываете сам байтовый массив, а не его содержимое. Вам нужно перебрать массив, чтобы увидеть, что он содержит.
Редактировать:
Также изменено getSalt для возврата байтового массива. Не безопасно возвращать строку, созданную из байтового массива (с новой строкой (соль)), поскольку последовательность байтов может не образовывать допустимую строку.
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;
}
}
salt.toString
не возвращает содержимое байтового массива, но hashCode
Если вы замените на sr.nextInt()
на каждый запрос вы будете получать разные значения. Если вы печатаете содержимое массива байтов, вы заметите разницу
От Javadoc java.security.SecureRandom.getInstance(String)
:
Возвращенный объект SecureRandom не был заполнен. Чтобы заполнить возвращаемый объект, вызовите метод setSeed.
Таким образом, очевидный ответ заключается в том, чтобы позвонить setSeed
, Однако это может иметь проблемы при использовании времени, потому что семя может быть легко угадано.
Другой подход заключается в том, чтобы поделиться безопасным случайным экземпляром (так как он безопасен для потоков)
Вы можете использовать метод
/**
* 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)
прохождение, например, текущее время