【问题标题】:Salted-hashed password verification is not working加盐哈希密码验证不起作用
【发布时间】:2021-03-03 01:00:57
【问题描述】:

当用户使用我的 java 应用程序注册时,我已经构建了一个函数来散列用户密码。我不确定我应该如何尝试验证他们的登录,因为存储的密码将被散列。

我尝试过创建一个函数,该函数获取输入的密码并对其进行哈希处理,然后将哈希值与数据库中的哈希值进行比较,但这不起作用,因为每次在密码(字符串)上运行哈希函数时结果永远不一样。我不确定如何在登录时验证用户密码。下面我提供了:

  • 登录按钮代码
  • 使用盐函数生成安全密码
  • 验证功能

登录按钮代码:

try {
    // Get password from database and verify login
    String slt = passwordUtils.getSalt(100);
    Connection con = connectDB.getConnectionUser();
    Statement stmt = null;
    
    String sql = "SELECT * FROM USER where userEmail=?";
    PreparedStatement pst = con.prepareStatement(sql);
    pst.setString(1, userEmail.getText());
    ResultSet rs = pst.executeQuery();
    
    String fromDatabase = rs.getString(3); //get the password stored in database (hashed version)
    String enteredPassword = String.valueOf(userPassword.getPassword()); //get entered password
    
    if(rs.next()){
        if(passwordUtils.verifyUserPassword(enteredPassword, fromDatabase, slt)){
            JOptionPane.showMessageDialog(null, "Login details are correct. Welcome!");
            con.close();
        } else {
            JOptionPane.showMessageDialog(null, "Invalid Log in details, try again.");
            con.close();
        }
    }
} catch (SQLException ex) {
    Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}

生成安全密码功能

public static String generateSecurePassword(String password, String salt){
    String returnValue = null;
    System.out.println("password supplied is: " + password + "salt added: " + salt);
    System.out.println("");
    byte[] securePassword = hash(password.toCharArray(), salt.getBytes());
    System.out.println("");
    System.out.println("secure pwd byte[]: " + securePassword);
    System.out.println("");
    returnValue = Base64.getEncoder().encodeToString(securePassword);
    System.out.println("");
    System.out.println("secure pwd converted " + returnValue);
    return returnValue;
}

验证登录功能

public static boolean verifyUserPassword(String providedPassword, String securedPassword, String salt){
    boolean returnValue = false;
    // generate new secure password with same salt
    String newSecurePassword = generateSecurePassword(providedPassword, salt); //generate secure password using salt 
    //check to see if both passwords are the same
    returnValue = newSecurePassword.equalsIgnoreCase(securedPassword);
    
    System.out.println("Hashed password: " + securedPassword);
    System.out.println("Verify hash:     "+ newSecurePassword);
   
    return returnValue;
}

加盐功能

public static String getSalt(int length){;
    StringBuilder returnValue = new StringBuilder(length);
    for(int i = 0; i < length; i++){
        returnValue.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
    }
    System.out.println("final salt generated: " + returnValue);
    return new String(returnValue);
}

散列函数:

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("PBKDF2WithHmacSHA512");
        System.out.println("skf return to secure password call: " + skf.generateSecret(spec).getEncoded());
        return skf.generateSecret(spec).getEncoded();
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e){
        throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
    } finally {
        spec.clearPassword();
        System.gc();
    }
}

【问题讨论】:

  • passwordUtils.getSalt(100); 应该做什么?每当设置新密码时,盐应该计算一次,然后盐应该与加盐+散列的密码一起存储并与它一起检索。然后,您需要使用相同的盐与用户刚刚输入的密码相结合,并对结果进行哈希处理,然后比较结果。现在你要么每次都创建一个新的盐,这根本行不通,要么你为所有用户使用相同的盐,这打败了大部分盐。
  • 抱歉,我在帖子中添加了 getSalt() 函数。我调用 passwordUtils.getSalt(100) 并将其存储为 String slt 因为我需要将它传递给我的 verifyUserPassword 方法
  • 然后再次阅读我评论的第一部分,也许会阅读盐应该是什么。当用户尝试登录时,您无法创建新盐,您需要在用户设置密码时生成盐,然后存储该盐并在该用户的所有未来散列操作中使用它。
  • 我明白你的意思。我不太明白如何输入输入的密码,例如“密码”,然后将其验证为数据库中的哈希版本

标签: java authentication salt


【解决方案1】:

在阅读了本帖下收到的反馈后。我设法解决了这个问题。

我在数据库中创建了另一个用于存储用户的列,并将该列命名为“saltPassword”。这将存储密码中使用的盐,以后可以检索该盐以检查密码验证。

在用户注册时存储散列密码和在数据库中使用的盐之后。进行密码验证变得非常简单。

public boolean verifyPassword(String providedPassword, String fromdatabase, String passwordSalt){
    boolean isValid = false;
    String generate = passwordUtils.generateSecurePassword(providedPassword, passwordSalt);
    
    isValid = generate.equals(fromdatabase);
    
    return isValid;
}

此函数用于密码验证。 它采用用户在密码字段中输入的密码、当前存储在数据库中的密码(散列)和密码使用的盐。

然后它会使用输入的密码以及用于数据库中原始密码的相同盐生成一个加盐密码。这应该在数据库中产生相同的散列密码。然后我们通过应用 .equals(fromdatabase) 并检查它是否与数据库中的相同来检查是否是这种情况。如果是这样,那么密码是正确的,用户可以登录了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    • 2011-12-06
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 2021-12-12
    相关资源
    最近更新 更多