Java 获取同一密码的不同散列值(+;salt)

Java 获取同一密码的不同散列值(+;salt),java,javafx,hash,salt,Java,Javafx,Hash,Salt,我试图在从中输入代码后获取哈希密码,目前我只使用salt方法的代码、hash方法和isExpectedPassword方法 我从文本字段获取密码: char[] passCharArray = txtPassword.toString().toCharArray(); 然后我调用这个类来获取salt值(我称它为Encryptor,而不是像原始帖子中那样的Passwords): 然后我得到散列密码: byte[] hashedPass = Encryptor.hash(passCharArray

我试图在从中输入代码后获取哈希密码,目前我只使用
salt
方法的代码、
hash
方法和
isExpectedPassword
方法

我从文本字段获取密码:

char[] passCharArray = txtPassword.toString().toCharArray();
然后我调用这个类来获取salt值(我称它为
Encryptor
,而不是像原始帖子中那样的
Passwords
):

然后我得到散列密码:

byte[] hashedPass = Encryptor.hash(passCharArray, salt);
我使用以下代码打印结果以查看发生了什么,并对结果进行了注释:

String saltString = Arrays.toString(salt);
System.out.println("SALT: " + saltString);
//SALT: [18, 117, -98, 41, 92, 124, 118, 17, 107, 14, 0, -81, 110, 70, 10, 42]

String hashedPassString = Arrays.toString(hashedPass);
System.out.println("HASHED PASS: " + hashedPassString);
//HASHED PASS: [44, -127, -43, 84, 40, -16, -46, -71, 109, -44, -41, 47, -61, -119, 21, 99, -23, 101, -13, 116, -12, 118, -66, 44, 104, 5, 4, 18, -55, 47, 59, 116]

System.out.println("Passwords match: " + Encryptor.isExpectedPassword(passCharArray, salt, hashedPass));
//Passwords match: false
下面两个是我在
isExpectedPassword
方法中输入的
System.out.print
s,以查看调用时的值

//Encryptor pwdHash: [-103, -87, 53, -75, 59, 11, 77, 116, 123, 59, 68, -35, 16, -68, 42, 34, -32, 75, 22, -94, -37, -26, 16, 20, 7, -46, -6, -20, -88, 104, -121, 77]
//Encryptor expectedHash: [44, -127, -43, 84, 40, -16, -46, -71, 109, -44, -41, 47, -61, -119, 21, 99, -23, 101, -13, 116, -12, 118, -66, 44, 104, 5, 4, 18, -55, 47, 59, 116]
所以基本上,
hashedPass
(和
expectedHash
)应该与
pwdHash
相同,但事实并非如此。我不明白我做错了什么。我的代码中遗漏了什么吗?在我不知情的情况下有什么变化吗

这是我的全部代码,如果人们想看到整个过程以防万一:

public class Encryptor {

    private static final Random RANDOM = new SecureRandom();
    private static final int ITERATIONS = 10000;
    private static final int KEY_LENGTH = 256;

    private Encryptor(){}

    public static byte[] getNextSalt(){
        byte[] salt = new byte[16];
        RANDOM.nextBytes(salt);
        return salt;
    }

    public static byte[] hash(char[] password, byte[] salt) {
        PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
        Arrays.fill(password, Character.MIN_VALUE);
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            return skf.generateSecret(spec).getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
        } finally {
            spec.clearPassword();
        }
    }

    public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) {
        byte[] pwdHash = hash(password, salt);

        String s = Arrays.toString(pwdHash);
        System.out.println("Encryptor pwdHash: " + s);

        String s2 = Arrays.toString(expectedHash);
        System.out.println("Encryptor expectedHash: " + s2);

        Arrays.fill(password, Character.MIN_VALUE);
        if (pwdHash.length != expectedHash.length) return false;
        for (int i = 0; i < pwdHash.length; i++) {
            if (pwdHash[i] != expectedHash[i]) return false;
        }
        return true;
    }


}


public class Controller implements Initializable {
    @FXML
    private Button btnLogin;
    //Some private variables

    @FXML
    private AnchorPane ancPane;
    @FXML
    private ImageView imgLogo;
    @FXML
    private Hyperlink hplRegister;
    @FXML
    private TextField txtUsername;
    @FXML
    private TextField txtPassword;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        //Some styling

        hplRegister.setOnAction(event -> {
            //Registering event
        });

        btnLogin.setOnAction(event -> {
            try {

                //Loading fxml data
                // I've put the code here just for testing purposes
                // and will not be the final placement.

                char[] passCharArray = txtPassword.toString().toCharArray();

                byte[] salt = Encryptor.getNextSalt();
                byte[] hashedPass = Encryptor.hash(passCharArray, salt);

                String saltString = Arrays.toString(salt);
                System.out.println("SALT: " + saltString);

                String hashedPassString = Arrays.toString(hashedPass);
                System.out.println("HASHED PASS: " + hashedPassString);

                System.out.println("Passwords match: " + Encryptor.isExpectedPassword(passCharArray, salt, hashedPass));

            }catch (Exception e){
                e.printStackTrace();
            }
        });
    }

    //Some getter methods.
}
公共类加密机{
private static final Random=new SecureRandom();
私有静态最终整数迭代=10000;
私有静态最终整数密钥长度=256;
专用加密程序(){}
公共静态字节[]getNextSalt(){
字节[]salt=新字节[16];
随机。下一个字节(盐);
返盐;
}
公共静态字节[]散列(字符[]密码,字节[]盐){
PBEKeySpec spec=新的PBEKeySpec(密码、salt、迭代次数、密钥长度);
数组.fill(密码、字符.MIN_值);
试一试{
SecretKeyFactory skf=SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”);
返回skf.generateScret(spec.getEncoded();
}捕获(NoSuchAlgorithmException | InvalidKeySpece异常){
抛出新的断言错误(“哈希密码时出错:+e.getMessage(),e);
}最后{
规范clearPassword();
}
}
公共静态布尔isExpectedPassword(char[]password,byte[]salt,byte[]expectedHash){
字节[]pwdHash=散列(密码,salt);
字符串s=Arrays.toString(pwdHash);
System.out.println(“加密机pwdHash:+s”);
字符串s2=Arrays.toString(expectedHash);
System.out.println(“加密程序expectedHash:+s2”);
数组.fill(密码、字符.MIN_值);
if(pwdHash.length!=expectedHash.length)返回false;
对于(int i=0;i{
//注册事件
});
btnLogin.setOnAction(事件->{
试一试{
//加载fxml数据
//我把代码放在这里只是为了测试
//而且不会是最后的位置。
char[]passCharArray=txtPassword.toString().toCharArray();
字节[]salt=Encryptor.getNextSalt();
字节[]hashedPass=Encryptor.hash(passCharArray,salt);
String saltString=Arrays.toString(salt);
System.out.println(“SALT:+saltString”);
String hashedPassString=Arrays.toString(hashedPass);
System.out.println(“哈希传递:“+hashedPassString”);
System.out.println(“密码匹配:“+Encryptor.isExpectedPassword(passCharray、salt、hashedPass));
}捕获(例外e){
e、 printStackTrace();
}
});
}
//一些getter方法。
}

问题是您正在清除密码。散列密码时,清除字符数组,并用空格填充,这将清除
passCharArray
。当您第二次传递它时,它基本上是检查密码的散列与空白数组的散列。而这些绝对不匹配


在实际情况下,您将从db或其他来源获得salt和哈希密码。传入版本不会被散列,因此会被清除,直到传递到
isExpectedPassword

您的salt不一样
getNextSalt()
将在每次调用中随机创建一个salt。这将导致哈希值每次都不同。toString文本字段不返回
文本字段的内容。结果类似于
TextField@5ef8216b[styleClass=文本输入文本字段]
PasswordField@67d95601[styleClass=文本输入文本字段密码字段]
用于
密码字段
。您需要改用
getText()
。@roces2k这就是为什么我将它存储在
byte[]salt
中,所以我不必再次调用它。我在
byte[]hashedPass
Encryptor中使用了相同的变量。isExpectedPassword(passCharray、salt、hashedPass)
。问题不在于
getNextSalt()
或字段@fabian说得对,你得到的文本不是你想要的文本,但是哈希和匹配应该仍然有效。修复
txtPassword.toString().tocharray()后的答案见下文
to
txtPassword.getText().toCharArray()并注释
数组。填充(密码、字符、最小值),它似乎工作正常。我甚至不知道它为什么会出现在那里,因为它不希望这些值只是在等待被记录或被某个ot记录
public class Encryptor {

    private static final Random RANDOM = new SecureRandom();
    private static final int ITERATIONS = 10000;
    private static final int KEY_LENGTH = 256;

    private Encryptor(){}

    public static byte[] getNextSalt(){
        byte[] salt = new byte[16];
        RANDOM.nextBytes(salt);
        return salt;
    }

    public static byte[] hash(char[] password, byte[] salt) {
        PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
        Arrays.fill(password, Character.MIN_VALUE);
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            return skf.generateSecret(spec).getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
        } finally {
            spec.clearPassword();
        }
    }

    public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) {
        byte[] pwdHash = hash(password, salt);

        String s = Arrays.toString(pwdHash);
        System.out.println("Encryptor pwdHash: " + s);

        String s2 = Arrays.toString(expectedHash);
        System.out.println("Encryptor expectedHash: " + s2);

        Arrays.fill(password, Character.MIN_VALUE);
        if (pwdHash.length != expectedHash.length) return false;
        for (int i = 0; i < pwdHash.length; i++) {
            if (pwdHash[i] != expectedHash[i]) return false;
        }
        return true;
    }


}


public class Controller implements Initializable {
    @FXML
    private Button btnLogin;
    //Some private variables

    @FXML
    private AnchorPane ancPane;
    @FXML
    private ImageView imgLogo;
    @FXML
    private Hyperlink hplRegister;
    @FXML
    private TextField txtUsername;
    @FXML
    private TextField txtPassword;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        //Some styling

        hplRegister.setOnAction(event -> {
            //Registering event
        });

        btnLogin.setOnAction(event -> {
            try {

                //Loading fxml data
                // I've put the code here just for testing purposes
                // and will not be the final placement.

                char[] passCharArray = txtPassword.toString().toCharArray();

                byte[] salt = Encryptor.getNextSalt();
                byte[] hashedPass = Encryptor.hash(passCharArray, salt);

                String saltString = Arrays.toString(salt);
                System.out.println("SALT: " + saltString);

                String hashedPassString = Arrays.toString(hashedPass);
                System.out.println("HASHED PASS: " + hashedPassString);

                System.out.println("Passwords match: " + Encryptor.isExpectedPassword(passCharArray, salt, hashedPass));

            }catch (Exception e){
                e.printStackTrace();
            }
        });
    }

    //Some getter methods.
}