因此,我目前正在学习SpringBoot,现在我正在尝试将密码以及其他数据存储在数据库(MySql)中。为了确保它的安全性,我正在使用腌制的哈希值进行存储。生成这些散列并将其存储在数据库中可以很好地工作,但是当我尝试通过使用salt和密码来验证密码时,我得到的是不同的哈希,因此结果是错误的。
下面是我的代码。
第一个:我开始验证的课程User
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private long uID;
@NotNull
private String nname;
@NotNull
private String vname;
private String email;
private String telnr;
@Embedded
private Address address;
@NotNull
private boolean isAdmin;
private String hash;
// Default Constructor
public User() {
}
// Constructor
public User(String name, String vname, String email, String telnr, Address address, boolean isAdmin,
String password) throws NoSuchAlgorithmException {
HashHelper hashHelper = new HashHelper();
this.nname = name;
this.vname = vname;
this.email = email;
this.telnr = telnr;
this.address = address;
this.isAdmin = isAdmin;
this.hash = hashHelper.createHash(password);
}
public boolean validateHash(String password) {
HashHelper hashHelper = new HashHelper();
// Get the used Salt
String[] parts = this.hash.split(":");
byte[] salt = parts[0].getBytes();
// Create Hash with old salt
String newHash = hashHelper.getHash(password, salt);
if (parts[1] == newHash) {
return true;
}
return false;
}
第二,我的课程HashHelper
处理与哈希有关的所有事情。每当存储新密码(因此使用新盐)时,我就使用createHash
,并使用getHash
进行特定盐的验证。
public class HashHelper {
public HashHelper() {
}
public byte[] getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
sr.nextBytes(salt);
return salt;
}
// Create Salt and Hash and store them, seperated by :
public String createHash(String password) throws NoSuchAlgorithmException {
String hash = null;
byte[] salts = getSalt();
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salts);
byte[] bytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
hash=salts.toString() + ":" + sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
System.out.println("Hash: " + hash);
return hash;
}
public String getHash(String password, byte[] salt) {
String hash = "";
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] bytes = md.digest(password.getBytes());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
}
hash = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hash;
}
}
在CommandLineRunner中设置验证请求只是为了进行测试,它如下:
Optional<User> user = userRepository.findById((long)10);
if (user.get().validateHash("Password")) {
System.out.println("Correct Password");
}
else {
System.out.println("Wrong password");
}
我认为这与getBytes()
和toString()
方法有关,因为当我尝试验证byte[] salt
时,其长度似乎较短(大约11-12个字节) 16),但我不知道为什么。任何帮助将不胜感激!
答案 0 :(得分:2)
您的问题很重要,因为您不应该使用此算法对密码进行哈希运算,因为它不安全。即使您没有保护任何重要内容,用户也可能会在多个地方使用相同的密码,并且当攻击者破解您不安全的密码表时,他们将能够在其他地方使用许多密码。
使用Argon2或至少PBKDF2来哈希密码,至少要进行10,000轮哈希。
此处遇到问题的原因是您没有存储用于哈希密码的盐。没有它,您将永远无法计算相同的哈希值。问题是salts.toString()
方法中的createHash()
:
hash=salts.toString() + ":" + sb.toString();
在toString()
上调用byte[]
不会告诉您有关数组内容的任何信息。这就是为什么您很麻烦将摘要bytes
的结果转换为十六进制的原因。您应该做一些类似于盐的事情。
同样,在字符串上调用getBytes()
只会使用您平台的默认编码对字符串进行编码。那不是你想要的。
比较equals()
实例时,请确保使用其String
方法。 ==
运算符只会告诉您它们是否是相同的实例。
在存储字节数组时,您仍然需要使用良好的哈希算法来完成此操作,我建议使用base-64,因为java.util.Base64
中对它的支持很大,它将产生更紧凑的编码的阵列。