Java / SpringBoot:验证加盐的哈希返回错误的哈希

时间:2019-06-03 15:20:30

标签: java hash

因此,我目前正在学习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),但我不知道为什么。任何帮助将不胜感激!

1 个答案:

答案 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中对它的支持很大,它将产生更紧凑的编码的阵列。