所以我正在编写一个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);
}
}
}
答案 0 :(得分:0)
您尝试将盐存储为字符串(salt
为byte[]
):
String temp= new String(salt);
以后再提取它:
salt = temp.getBytes();
您无法将字节数组转换为字符串,并且像这样返回并期望检索相同的字节。 docs for (String(byte[])
)说:
未指定给定字节在默认字符集中无效时此构造函数的行为。
由于您的密码格式已将salt包含为十六进制字符串,因此无论如何都不需要单独存储盐。相反,从此字符串中提取salt和密码哈希的十六进制字符串,将它们转换为byte[]
,并在比较中使用它们。