【问题标题】:How do I compare salt with a generated password?如何将盐与生成的密码进行比较?
【发布时间】:2020-07-09 14:51:15
【问题描述】:

所以我试图将我的原始密码与加盐的密码进行比较。我知道如何比较哈希密码,我将原始密码添加到它并且它可以工作。但是,我不知道如何比较盐。

public static String saltPassword(String password) throws NoSuchAlgorithmException{
     String salt = getSalt();
     return password + salt;
 }
public static String getSalt(){
     Random r = new SecureRandom();
     byte[] saltBytes = new byte[32];
     r.nextBytes(saltBytes);
     return Base64.getEncoder().encodeToString(saltBytes);
 }

我需要做什么才能将原始密码与此进行比较?

这就是我的任务所说的, “将生成的密码与存储的盐和散列密码进行比较”。

【问题讨论】:

  • 答案是你不知道。你把密码和盐放在一起。然后你散列。然后您将 >>the hash
  • 按什么顺序执行此操作是否重要。我正在尝试类似的操作,但我先对密码进行哈希处理,然后再添加盐
  • 以及我如何使用与以前相同的盐,因为它每次都是随机的
  • 看我的回答。您(通常)将盐与原始密码的哈希一起存储。

标签: java security servlets cryptography salt


【解决方案1】:

如何比较 salt 和生成的密码?

我需要做什么才能将原始密码与此进行比较?

答案是,如果这些事情你都不做。

要注册原始密码,请执行以下操作:

  1. 获得盐1
  2. 将原始密码与盐结合起来
  3. 散列。
  4. 存储盐和哈希。

然后您丢弃原始密码。

要检查提供的密码,请执行以下操作:

  1. 查找存储的哈希和注册时创建的相应盐;见上文。
  2. 以与上述相同的方式组合提供的密码和盐。
  3. 如上所述散列。
  4. 将生成的哈希值与存储的哈希值进行比较。如果它们相同,则提供的密码是正确的密码。

如您所见,您不会将盐或原始密码与任何东西进行比较。

但是,在为原始密码和您正在检查的密码生成哈希值时,使用相同的盐也很重要。否则,密码检查将不起作用。


1 - 盐只是一个数字或字符串。理想情况下,盐值应该不同。 salt 的目的是避免所谓的"rainbow table" attack 从被盗的(未加盐的)密码哈希中恢复原始密码。如果有(比如说)一百万个可能的盐值,那么坏人需要生成一百万个不同的彩虹表。生成和存储许多彩虹表变得不切实际。

【讨论】:

  • 我上面列出了一些代码。我希望这会更有帮助
【解决方案2】:

您还应该储存盐。当两个用户选择相同的密码时,Salt 用于防止生成相同的哈希密码。类似以下代码可用于将密码保存为 hashedPassord 并验​​证输入的密码。它不完整,但可以用作示例代码。

private static void savePassword(String rawPassword) throws InvalidKeySpecException, NoSuchAlgorithmException {
    byte[] salt = getSalt();
    String hashedPassword = getHashedPassword(rawPassword, salt);
    String encodedSalt = base64Encode(salt);

    /* todo: store hashPassword and encodedSalt */
}

private static boolean verifyPassword(String rawPassword, String hashedPassword, String encodedSalt) throws InvalidKeySpecException, NoSuchAlgorithmException {
    return Objects.equals(hashedPassword, getHashedPassword(rawPassword, base64Decode(encodedSalt)));
}

private static String getHashedPassword(String rawPassword, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
    KeySpec spec = new PBEKeySpec(rawPassword.toCharArray(), salt, 65536, 128);
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    byte[] hash = factory.generateSecret(spec).getEncoded();
    return base64Encode(hash);
}

private static byte[] getSalt() {
    Random r = new SecureRandom();
    byte[] saltBytes = new byte[32];
    r.nextBytes(saltBytes);
    return saltBytes;
}

private static String base64Encode(byte[] src) {
    return Base64.getEncoder().encodeToString(src);
}

private static byte[] base64Decode(String src) {
    return Base64.getDecoder().decode(src);
}

【讨论】:

  • 我会问你同样的问题。我加盐/哈希的顺序有关系吗?以及我如何使用与以前相同的盐,因为它每次都是随机的。
  • 嗨@studentguy。生成盐应该在散列密码之前完成。如果您将生成的盐存储在数据库中,那么您可以在需要时使用相同的盐。我添加了一些示例代码,希望对您有所帮助。
  • 谢谢你的帮助
【解决方案3】:

好的,这就是我所拥有的。我很想查找存储的密码,但我的老师在她的解决方案视频中没有这样做

public static String saltPassword(String password) throws NoSuchAlgorithmException{
     String salt = getSalt();
     return hashPassword(password + salt);
 }
 public static String getSalt(){
     Random r = new SecureRandom();
     byte[] saltBytes = new byte[32];
     r.nextBytes(saltBytes);
     return Base64.getEncoder().encodeToString(saltBytes);
 }
 public static String generatePassword(){
        String charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-+!@#$%";
        String password = "";
        int start = 0;
        int stop = 0;
        int minLength = 8;

        for (int i = 0; i <= minLength; i++) {
                // get a random character from the chars string
                start = getRandomNumber(charSet.length());
                stop = start + 1;
                password += charSet.substring(start, stop);
        }        
        return password;
    }
    private static int getRandomNumber(int maxValue){
        double randomNumber;
        randomNumber = Math.floor(Math.random() * maxValue);

        return (int)randomNumber;
    }
    public static String hashPassword(String password)throws NoSuchAlgorithmException{
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(password.getBytes());
        byte[] mdArray = md.digest();
        StringBuilder sb = new StringBuilder(mdArray.length * 2);
        for (byte b : mdArray){
            int v = b & 0xff;
            if(v < 16){
                sb.append('0');
            }
            sb.append(Integer.toHexString(v));
        }
        return sb.toString();   
    }

然后我要去创造它

    String newPassword = PasswordUtil.generatePassword();
    String hashedPassword = "";
    String saltedPassword = "";

try{
            hashedPassword = PasswordUtil.hashPassword(newPassword);
        }
        catch(NoSuchAlgorithmException e){
            System.out.println();
        }
        try{
            saltedPassword = PasswordUtil.saltPassword(hashedPassword);
        }
        catch(NoSuchAlgorithmException e) {
            System.out.println();
        }

接下来会发生什么?

这是散列密码:

50f99d2a635cc9bac7e001506789b55a7c603d93c89d362cc5d95ab257fc2666

这是带有盐的哈希

e954fbc2309cc359cd603effb6d0644947a3253110ad6c3b2416dd49168331a3

【讨论】:

  • 每次生成一个新的随机盐并且不将其与哈希一起存储是行不通的。您可能误解了这应该如何工作。我注意到您的自我回答中没有任何讲师的代码实际上是存储任何东西。我倾向于认为教练搞错了......
  • 那么,如果随机盐无法比较,那么为什么要存储它呢?
  • 我不明白你的评论。
  • 我没有说盐具有可比性。
  • 我说的是在生成原始密码的哈希和要检查的密码的哈希时必须使用相同的盐。这就是为什么您必须存储原始盐......或从其他一些(恒定)信息(例如用户的帐户名)生成盐
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
  • 2017-12-08
  • 1970-01-01
  • 2021-09-27
  • 2017-11-25
相关资源
最近更新 更多