比较哈希结果总是返回false,即使它们完全相同

时间:2016-11-24 08:51:04

标签: java mysql hash cryptography

所以我正在编写一个java程序,它从用户那里获取用户名和密码,从数据库(mysql)中检索哈希密码,并对用户进行身份验证。散列密码和salt在我的数据库中保存为TEXT数据类型。我的问题是,当我比较存储的密码和用户输入的密码时(当然在运行散列算法之后),结果总是为假。我复制了2个哈希,并在另一个程序中对它们进行了比较,结果证明它们是相同的。这是我的代码(请忽略我的代码容易受到SQL注入攻击的事实,我计划稍后解决这个问题:

public class Security {

public static void main(String[] args) throws UserNotExistingException {
    Security s=new Security();
    s.signUp("John.Smith", "John Smith", "text@lau.edu", "test");
    System.out.println(s.Authenticate("John.Smith" , "test"));
}

public boolean Authenticate(String username, String password) throws UserNotExistingException {
    String dbpass = null;
    byte[] salt = null;
    try {
        // Load driver for connecting to db
        Class.forName("com.mysql.jdbc.Driver");
        // Establishing connection to db
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/vote sys", "root", "");
        // Creating statement object to be executed on dbms
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("select  pass, salt from user_acc where username = '" + username + "';");

        if (rs.next()) {
            dbpass = rs.getString(2);
            String temp = rs.getString(2);
            System.out.println(temp);
            salt = temp.getBytes();
        }
        for (byte i : salt)
            System.out.print(i);
        System.out.println();
        // Terminating connection to db
        con.close();
    } catch (Exception e) {
        System.out.println(e);
    }
    if (dbpass == null || salt == null)
        throw new UserNotExistingException("User " + username + " doesn't exist");

    try { //this is where im facing the problem, the condition is always returning true when its not
        String hashed=generateHash(password, salt);
        System.out.println(hashed);
        if (hashed.compareTo(dbpass)!=0)
            return false;


    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
        e.printStackTrace();
    }

    return true;
}

private static String generateHash(String password, byte[] salt)
        throws NoSuchAlgorithmException, InvalidKeySpecException {
    int iterations = 1000;
    char[] chars = password.toCharArray();

    PBEKeySpec spec = new PBEKeySpec(chars, salt, iterations, 64 * 8);
    SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    byte[] hash = skf.generateSecret(spec).getEncoded();
    return iterations + ":" + toHex(salt) + ":" + toHex(hash);
}

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

private static String toHex(byte[] array) throws NoSuchAlgorithmException {
    BigInteger bi = new BigInteger(1, array);
    String hex = bi.toString(16);
    int paddingLength = (array.length * 2) - hex.length();
    if (paddingLength > 0) {
        return String.format("%0" + paddingLength + "d", 0) + hex;
    } else {
        return hex;
    }
}

public void signUp(String username, String name, String email,  String password) {
    String dbuser = "", dbemail = "";
    try {
        // Load driver for connecting to db
        Class.forName("com.mysql.jdbc.Driver");
        // Establishing connection to db
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/vote sys", "root", "");
        // Creating statement object to be executed on dbms
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("select  username, email from user_acc where username = '" + username
                + "' or email = '" + email + "';");

        if (rs.next()) {
            dbuser = rs.getString(2);
            dbemail = rs.getString(2);
        }
        if (!dbuser.equals("") || !dbemail.equals(""))
            throw new UserNotExistingException("Username or email already exists");

        byte[] salt = getSalt();
        for (int i = 0; i < salt.length; i++) {
            System.out.print(salt[i]);
        }
        System.out.println();
        String temp= new String(salt);
        System.out.println(temp);
        String hashedPass = generateHash(password, salt);
        System.out.println(hashedPass);
        stmt.executeUpdate("INSERT INTO `user_acc`(`username`, `name`, `email`, `pass`, `salt`) VALUES ('"
                + username + "','" + name + "','" + email + "','" + hashedPass + "','" + temp + "');");

    } catch (Exception e) {
        System.out.println(e);
    }
}

}

1 个答案:

答案 0 :(得分:0)

您尝试将盐存储为字符串(saltbyte[]):

String temp= new String(salt);

以后再提取它:

salt = temp.getBytes();

您无法将字节数组转换为字符串,并且像这样返回并期望检索相同的字节。 docs for (String(byte[]))说:

  

未指定给定字节在默认字符集中无效时此构造函数的行为。

由于您的密码格式已将salt包含为十六进制字符串,因此无论如何都不需要单独存储盐。相反,从此字符串中提取salt和密码哈希的十六进制字符串,将它们转换为byte[],并在比较中使用它们。