java - 重新生成 SHA-1 哈希值与存储的数据库哈希值不匹配

标签 java security jpa hash message-digest

我正在尝试将 key 延伸与 UUID 作为盐相结合来实现 SHA1 密码哈希。盐作为一列存储在数据库后端内的 usersTable 中,因此每个用户都会生成自己独特的盐。

我的问题是,当我尝试重新生成密码哈希值时,哈希值不匹配,并且我没有看到问题所在。

registerUser 从 createUser 中获取用户对象,然后将其保存到数据库中。然后我使用 validatePassword 重新生成哈希。请参阅下面的完整代码片段。这是我第一次尝试保护密码,显然我在某个地方犯了错误,但我无法发现错误。

public void registerUser() {
    try {

        Usertable newUser = createUser();

        // user constructed at this point, persist it to the database.
        utx.begin();
        em.persist(newUser);
        utx.commit();

        // Register user with Meter
        Meter myMeter = (Meter) em.createNamedQuery("Meter.findByMeterid").setParameter("meterid", this.meterId).getSingleResult();
        myMeter.setUsername(newUser);

        utx.begin();
        em.merge(myMeter);
        utx.commit();

    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NotSupportedException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SystemException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (RollbackException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (HeuristicMixedException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (HeuristicRollbackException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SecurityException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalStateException ex) {
        Logger.getLogger(RegisterBean.class.getName()).log(Level.SEVERE, null, ex);
    }

}

private Usertable createUser() throws NoSuchAlgorithmException {
    Security securityLevel = (Security) em.createNamedQuery("Security.findBySecurityid").setParameter("securityid", SECURITY_LEVEL_USER).getSingleResult();
    Usertable newUser = new Usertable();

    // generate UUID to be used as a salt.
    UUID salt = UUID.randomUUID();

    // generate hash
    MessageDigest msgDigest = MessageDigest.getInstance("SHA-1");
    String inputText = new String(salt.toString() + this.password);
    for (int i = 0; i < ITERATIONS; i++) {
        msgDigest.update(inputText.getBytes());
        byte rawByte[] = msgDigest.digest();
        inputText = (new BASE64Encoder()).encode(rawByte);
    }

    String hashValue = inputText;

    newUser.setUsername(this.userName);
    newUser.setSecurityid(securityLevel);
    newUser.setSalt(salt.toString());
    newUser.setPassword(hashValue);

    return newUser;
}

public void validatePassword(FacesContext context, UIComponent ui, Object passwordField) {
    try {
        UIInput userNameInput = (UIInput) context.getViewRoot().findComponent("regform:userName");
        String userName = (String) userNameInput.getValue();

        Usertable myUser = (Usertable) em.createNamedQuery("Usertable.findByUsername").setParameter("username", userName).getSingleResult();

        // generate hash
        MessageDigest msgDigest = MessageDigest.getInstance("SHA-1");
        String inputText = new String(myUser.getSalt() + this.password);
        for (int i = 0; i < ITERATIONS; i++) {
            msgDigest.update(inputText.getBytes());
            byte rawByte[] = msgDigest.digest();
            inputText = (new BASE64Encoder()).encode(rawByte);
        }

        if (!inputText.equals(myUser.getPassword())) {
            String message = "Username or password incorrect";
            throw new ValidatorException(new FacesMessage(message));
        } else {
            // password is valid, store user into session and mark logged in.
            this.myUser = myUser;
        }

    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(LoginBean.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoResultException ex) {
        String message = "Username or password incorrect";
        throw new ValidatorException(new FacesMessage(message));
    }

}

最佳答案

您确定在调用 validatePassword() 之前正确设置了 this.password 吗?您是否检查了 createUser()validatePassword()inputText 的初始值以确保它们匹配?

这种散列密码的方法并不安全。由于哈希函数输出中的冲突,执行的迭代次数越多,生成的哈希值包含的熵就越少。为了保持原始密码中相同级别的不可预测性,您需要将密码添加到每轮哈希中。

做到这一点的最佳方法是使用现有的密码哈希库,例如 scrypt 或 bcrypt,或者至少使用 key 派生函数,例如 PBKDF2,该函数内置于大多数 Java 运行时中。

关于java - 重新生成 SHA-1 哈希值与存储的数据库哈希值不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20230461/

相关文章:

java - 为什么 Java 安全编码很重要?

java - HyperJAXB:如何始终使用 @OneToOne 而不是 @ManyToOne

security - 此 SSO 实现安全吗?

java - 使用 AspectJ 将一个注释转换为多个注释

java - 将 Richfaces 4 与 OpenFaces 3 相结合

java - Action 监听器太快了,我进入下一个菜单

security - 在缓存中安全地存储密码哈希

mysql - 仅使用 2 个模型对象设置 play 2.0 标记?

java - 当我运行程序时,JPA 不会在 MySQL 中创建表

java - 如何将 HTML 元素设置到 session grails 中