我该如何在Java中进行密码哈希?

200
我需要为存储在数据库中的密码进行哈希。如何在Java中实现?
我希望将明文密码与随机盐相结合,然后将盐和哈希密码存储在数据库中。
然后,当用户想要登录时,我可以获取他们提交的密码,添加他们帐户信息中的随机盐,对其进行哈希,并查看它是否等于存储在他们帐户信息中的哈希密码。

最好使用SHA哈希函数族。http://en.wikipedia.org/wiki/MD5(虽然没有完美的解决方案) - YGL
11
现在使用GPU攻击变得如此便宜,所以SHA家族算法实际上已经不再是一种安全的密码哈希方法(速度太快),即使加盐也不行。应该使用bcrypt、scrypt或PBKDF2算法。请注意,这并非重新编排。 - Eran Medan
11
为什么这个问题被关闭了?这是一个关于真实工程问题的提问,答案非常有价值。提问者并不是在寻找一个程序库,而是在询问如何解决这个工程问题。 - stackoverflowuser2010
12
太棒了。这个问题有52个赞,但有人决定将其关闭,认为它“不相关”。 - stackoverflowuser2010
1
是的,我之前在 Meta 上发布过关于关闭问题的帖子,但遭到了很严厉的批评。 - Chris Dutrow
9
这个问题应该重新开放。这是一个关于如何编写程序来解决描述的问题(密码认证),并提供了简短的代码解决方案。看到触发词“library”并不足以自动关闭问题;他并没有要求库的建议,而是在询问如何散列密码。编辑:好了,修正了。 - erickson
14个回答

1

在所有标准哈希方案中,LDAP ssha 是最安全的一种。

http://www.openldap.org/faq/data/cache/347.html

我只需按照指定的算法,并使用MessageDigest进行哈希处理。

您需要像您建议的那样将盐存储在数据库中。


1
因为SSHA不迭代哈希函数,所以速度太快。这使得攻击者可以更快地尝试密码。更好的算法,如Bcrypt、PBBKDF1和PBKDF2使用“密钥强化”技术来减缓攻击者的速度,以至于一个密码应该在他们可以暴力破解甚至8个字母的密码空间之前过期。 - erickson
所有这些机制的问题在于你无法获得客户端支持。哈希密码的问题在于你无法支持使用其他算法哈希的密码。至少,使用ssha,所有LDAP客户端都支持它。 - ZZ Coder
它并不是“最安全的”,只是“相当兼容”。bcrypt/scrypt需要更多的资源。 - eckes

0
这是我制作的简单PasswordHasher类:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;

public class PasswordHasher {
    private static final String ALGO = "PBKDF2WithHmacSHA1";
    private static final byte[] SALT = {
            8, 8, 8, 8, 2,
            8, 7, 7, 7, 2,
            1, 1, 1, 1, 2,
            11
    };
    private static final int ITERATION_COUNT = 1000;
    private static final int KEY_LENGTH = 128;

    private SecretKeyFactory mFactory;

    byte[] hashPassword(String password) {
        SecretKeyFactory factory = getFactory();

        if (factory != null) {
            try {
                KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
                return factory.generateSecret(spec).getEncoded();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    boolean verifyPassword(String password, byte[] expectedHashResult) {
        byte[] hashedPassword = hashPassword(password);
        if (hashedPassword == null) {
            // Log fail result
            return false;
        }
        return Arrays.equals(hashedPassword, expectedHashResult);
    }

    private SecretKeyFactory getFactory() {
        if (mFactory == null) {
            try {
                mFactory = SecretKeyFactory.getInstance(ALGO);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
        return mFactory;
    }
}

0

我从Udemy的视频中学到了这个,并进行了编辑,使其成为更强的随机密码。

}

private String pass() {
        String passswet="1234567890zxcvbbnmasdfghjklop[iuytrtewq@#$%^&*" ;

        char icon1;
        char[] t=new char[20];

         int rand1=(int)(Math.random()*6)+38;//to make a random within the range of special characters

            icon1=passswet.charAt(rand1);//will produce char with a special character

        int i=0;
        while( i <11) {

             int rand=(int)(Math.random()*passswet.length());
             //notice (int) as the original value of Math>random() is double

             t[i] =passswet.charAt(rand);

             i++;
                t[10]=icon1;
//to replace the specified item with icon1
         }
        return new String(t);
}






}

1
我乐意接受更正,但我认为散列时不应该使用随机数。这是因为您的哈希函数保持确定性;即如果您多次对一个字符串进行哈希处理,则始终会为该字符串返回相同的哈希值。 - duldi

0
import java.security.MessageDigest;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Base64;
import java.util.Properties;
public class Main{
    public static void main(String[]a]{
    
//enter code here

}
   
    public static String hashPassword(String password) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] hash = md.digest(password.getBytes());
            return Base64.getEncoder().encodeToString(hash);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

hashPassword是一个方法,当我们将一个字符串作为它的参数传递时,它会返回一个哈希值。 MessageDigest是提供散列密码接口的类。 getInstance用于获取散列算法的实例,例如MD-5、SHA 216、SHA-512等。 哈希字符串以-byte []形式呈现。 在返回语句中,我们使用ToString方法将字节转换为字符串。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接