首页 > 解决方案 > Java/SpringBoot:验证加盐哈希返回错误哈希

问题描述

所以,我目前正在学习 SpringBoot,现在我正在尝试将密码与其他用户数据一起存储在数据库(MySql)中。为了安全起见,我使用加盐哈希值进行存储。生成这些哈希并将它们存储在数据库中工作正常,但是当我尝试通过获取盐和密码来验证密码时,我得到了不同的哈希,因此结果错误。

下面是我的代码。

第一个: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()andtoString()方法有关,因为byte[] salt当我尝试验证它时似乎长度更短(大约 11-12 个字节而不是 16 个字节),但我不知道为什么。任何帮助将不胜感激!

标签: javahash

解决方案


你的问题没有实际意义,因为你不应该使用这个算法来散列密码,因为它不安全。即使您没有保护任何重要的东西,用户也可能在多个地方使用相同的密码,当攻击者破解您的不安全密码表时,他们将能够在其他地方使用其中的许多密码。

使用 Argon2 或至少 PBKDF2 对密码进行哈希处理,至少进行 10,000 轮哈希处理。

您在这里遇到问题的原因是您没有存储用于散列密码的盐。没有它,您将永远无法计算相同的哈希值。问题salts.toString()出在createHash()方法上:

hash=salts.toString() + ":" + sb.toString();

调用toString()abyte[]不会告诉您有关数组内容的任何信息。这就是为什么您不厌其烦地将摘要的结果 , 转换bytes为十六进制的原因。你应该做一些类似于盐的事情。

同样,调用getBytes()字符串只会使用您平台的默认编码对字符串进行编码。那不是你想要的。

确保equals()在比较String实例时使用他们的方法。==操作员只会告诉您它们是否是相同的实例。

在存储字节数组时,您仍然需要使用良好的哈希算法,我建议使用 base-64,因为在 中对它有很好的支持,java.util.Base64它会产生更紧凑的数组编码。


推荐阅读