如何在Java中使用SHA-512哈希密码?

47

我一直在研究Java字符串加密技术,不幸的是,我没有找到任何有关如何使用Java中的SHA-512哈希字符串的好教程;我读了一些关于MD5和Base64的博客文章,但它们不像我想要的那么安全(实际上,Base64不是一种加密技术),因此我更喜欢SHA-512。


2
SHA2也不是一种加密技术。它是一组密码安全哈希函数,可用于消息摘要,而不是加密(至少在正常模式下不是)。您需要更详细地描述您想要做什么。除此之外,您的MD5代码应该可以与其他哈希算法一起使用,只需更换算法名称即可。它们所有的输出都是二进制byte[]数组,因此您可能希望使用base64或binhex将结果转换为ASCII格式。 - eckes
3
SHA-512也不是一个加密算法。实际上,它被专门设计成无法“解密”由该哈希算法产生的摘要。你想实现什么目标? - JB Nizet
1
Java字符串包含有效的Unicode。在某些字符集(如UTF-8)中获取字节,然后对其进行加密会产生无法正确转换为字符串(用于解密),特别是UTF-8需要正确的序列。因此通常会添加Base64以将加密字节转换为ASCII。 - Joop Eggen
3
没有加密算法能够实现你想要的功能。如果你需要加密(但如果目标是存储密码,就不应该使用加密),那么你需要一个密钥来进行加密和解密。或者你需要盐和摘要函数(你需要哈希函数,SHA-512被认为对于这种用途来说过于薄弱)。盐+摘要的原则是,你可以通过重新加盐和摘要已提交的密码来检查其是否正确,然后检查结果是否与数据库中存储的相同。但是不可能反向得到原始密码。 - JB Nizet
3
使用JDK自带的PBKDF2WithHmacSHA1或BCrypt进行加密。但首先,学习加密原理。 - JB Nizet
显示剩余2条评论
7个回答

80

您可以在 SHA-512 中使用此方法(但不适合用于密码哈希)。

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public String get_SHA_512_SecurePassword(String passwordToHash, String salt){
    String generatedPassword = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-512");
        md.update(salt.getBytes(StandardCharsets.UTF_8));
        byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
        StringBuilder sb = new StringBuilder();
        for(int i=0; i< bytes.length ;i++){
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        generatedPassword = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return generatedPassword;
}

10
@stackpepe - 这不应该用于密码哈希,SHA*系列太快并且容易被暴力破解。使用好的GPU,您可以每秒计算约4千兆SHA512,这就是为什么需要转换成BCrypt,PBKDF2或SCrypt的原因。 - martinstoeckli
5
@Abhi - 盐可以防止攻击者构建一个单一的彩虹表来一次性获取所有密码,而是需要为每个盐构建一个彩虹表,这使得攻击变得不可行。然而,盐对于强制破解(通常是字典攻击)单个密码没有任何帮助。如果强制破解整个英语词典只需花费几毫秒,那么我们就需要一种具有成本因素的算法,该因素定义了计算单个哈希所需的时间(BCrypt、PBKDF2、SCrypt)。 - martinstoeckli
5
除了盐值以UTF-8字符串的形式输入(通常是以字节定义),长度未被检查,异常处理未被明确定义(以致代码无法编译),十六进制程序难以阅读并包含在方法中,未遵守Java命名惯例,最初赋值为null的坏编程实践……这只是一个糟糕的演示如何使用SHA-512。我想点赞数量之所以多,主要是因为这个问题在谷歌上以“SHA-512 Java”搜索词排名 第一 - Maarten Bodewes
2
@MaartenBodewes 人们需要付出一些努力才能获得更好的输出。我提供的代码不是为了复制粘贴,而只是一个提示,可以根据需求进行改进。 - A. Sinha
2
@Abhi:但这正是人们会做的事情:复制和粘贴。 - Martijn Pieters
显示剩余8条评论

27

10
我支持你的答案,但通常这些算法也被称为哈希函数(例如,PBKDF甚至使用SHA)。已经很难解释加密和哈希之间的区别了,因此建议不要使用哈希函数可能有点令人困惑。慢速运算是它们之间的区别。 - martinstoeckli
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - TheGreatContini
@martinstoeckli 读回这段话,这不是一个正确的评论。PBKDF2是一个密钥派生函数密码哈希函数。它使用安全哈希并不意味着它本身就是一个安全哈希。有许多PBKDF / 密码哈希不在内部使用安全哈希。 - Maarten Bodewes
2
@MaartenBodewes - 也许最适当的算法表达式是密码哈希函数。我的意思是,当我们说:不要使用哈希函数,而要使用密码哈希函数时,这听起来很令人困惑。如果我们这样做,我们应该解释什么使它们不同,而在这种情况下重要的一点是“成本因素”,或者更普遍地说,我们可以控制计算哈希所需的时间。 - martinstoeckli
1
我同意,这根本没有回答问题。寻找SHA-512 Java代码示例的人(无论为什么需要)都会发现一个非解决方案。也许在未来你可以:1. 提供一个解决方案。2. 作为附注提供更好的解决方案。 - tresf
显示剩余2条评论

14

使用Guava

Hashing.sha512().hashString(s, StandardCharsets.UTF_8).toString()

2
这仍处于测试版。 - salvador

3

2
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Hex;

public String getHashSHA512(String StringToHash, String salt){
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            md.update(salt.getBytes(StandardCharsets.UTF_8));
            byte[] bytes = md.digest(StringToHash.getBytes(StandardCharsets.UTF_8));
            generatedPassword = Hex.encodeHexString(bytes);
        }
        catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        return generatedPassword;
    }

虽然哈希函数是一种加密密码的方法,但并不推荐使用,因为现在有更先进的算法,例如bcrypt或scrypt。


2

如果你想在Java中哈希密码,可以使用这个方法。

public static boolean isHashMatch(String password, // the password you want to check.
                                  String saltedHash, // the salted hash you want to check your password against.
                                  String hashAlgorithm, // the algorithm you want to use.
                                  String delimiter) throws NoSuchAlgorithmException // the delimiter that has been used to delimit the salt and the hash.
{
    // get the salt from the salted hash and decode it into a byte[].
    byte[] salt = Base64.getDecoder()
                        .decode(saltedHash.split(delimiter)[0]);
    // compute a new salted hash based on the provided password and salt.
    String pw_saltedHash = computeSaltedBase64Hash(password, 
                                                   salt,
                                                   hashAlgorithm,
                                                   delimiter);
    // check if the provided salted hash matches the salted hash we computed from the password and salt.
    return saltedHash.equals(pw_saltedHash);
}

public static String computeSaltedBase64Hash(String password, // the password you want to hash
                                             String hashAlgorithm, // the algorithm you want to use.
                                             String delimiter) throws NoSuchAlgorithmException // the delimiter that will be used to delimit the salt and the hash.
{
    // compute the salted hash with a random salt.
    return computeSaltedBase64Hash(password, null, hashAlgorithm, delimiter);
}

public static String computeSaltedBase64Hash(String password, // the password you want to hash
                                             byte[] salt, // the salt you want to use (uses random salt if null).
                                             String hashAlgorithm, // the algorithm you want to use.
                                             String delimiter) throws NoSuchAlgorithmException // the delimiter that will be used to delimit the salt and the hash.
{
    // transform the password string into a byte[]. we have to do this to work with it later.
    byte[] passwordBytes = password.getBytes();
    byte[] saltBytes;

    if(salt != null)
    {
        saltBytes = salt;
    }
        else
        {
            // if null has been provided as salt parameter create a new random salt.
            saltBytes = new byte[64];
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(saltBytes);              
        }

    // MessageDigest converts our password and salt into a hash. 
    MessageDigest messageDigest = MessageDigest.getInstance(hashAlgorithm);
    // concatenate the salt byte[] and the password byte[].
    byte[] saltAndPassword = concatArrays(saltBytes, passwordBytes);
    // create the hash from our concatenated byte[].
    byte[] saltedHash = messageDigest.digest(saltAndPassword);
    // get java's base64 encoder for encoding.
    Encoder base64Encoder = Base64.getEncoder();
    // create a StringBuilder to build the result.
    StringBuilder result = new StringBuilder();

    result.append(base64Encoder.encodeToString(saltBytes)) // base64-encode the salt and append it.
          .append(delimiter) // append the delimiter (watch out! don't use regex expressions as delimiter if you plan to use String.split() to isolate the salt!)
          .append(base64Encoder.encodeToString(saltedHash)); // base64-encode the salted hash and append it.

    // return a salt and salted hash combo.
    return result.toString();
}

public static byte[] concatArrays(byte[]... arrays)
{   
    int concatLength = 0;
    // get the actual length of all arrays and add it so we know how long our concatenated array has to be.
    for(int i = 0; i< arrays.length; i++)
    {
        concatLength = concatLength + arrays[i].length;
    }
    // prepare our concatenated array which we're going to return later.
    byte[] concatArray = new byte[concatLength];
    // this index tells us where we write into our array.
    int index = 0;
    // concatenate the arrays.
    for(int i = 0; i < arrays.length; i++)
    {
        for(int j = 0; j < arrays[i].length; j++)
        {
            concatArray[index] = arrays[i][j];
            index++;
        }
    }
    // return the concatenated arrays.
    return concatArray;     
}

-1

使用安全哈希算法,将3个盐组件(每个组件包含150个随机字符)组合成一个独立的用户盐(用户数据库表中的用户盐、数据库表中的通用盐(每月通过cron job更改)以及应用程序库中隐藏的一些盐)。将安全哈希的循环次数调整到您的需求。有关哈希方法,请参见上面的答案。

private static String generateSalt(int lenght){
    String abcCapitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String abcLowerCase = "abcdefghijklmnopqrstuvwxyz";
    String numbers = "01234567890123456789";
    String characters = "!@#$%^&*!@#$%%^^&*";

    String total = abcCapitals + abcLowerCase + numbers + characters;

    String response = "";

    char letters[] = new char[lenght];
    for (int i=0; i<lenght-1; i++){
        Random r = new Random();
        char letter = total.charAt(r.nextInt(total.length()));
        letters[i] = letter;
    }

    response = Arrays.toString(letters).replaceAll("\\s+","");
    response = response.replaceAll(",","");

    return response;
}

private static String getHash(String passwordToHash, String salt){
            String generatedPassword = null;
            try {
                 MessageDigest md = MessageDigest.getInstance("SHA-512");
                 md.update(salt.getBytes(StandardCharsets.UTF_8));
                 byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
                 StringBuilder sb = new StringBuilder();
                 for(int i=0; i< bytes.length ;i++){
                    sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
                 }
                 generatedPassword = sb.toString();
                } 
               catch (NoSuchAlgorithmException e){
                   System.out.println(e);
               }
            return generatedPassword;
        }

    public static String getSecureHash(String password, String salt){
        String hash = getHash(password, salt);
        for (int i=0; i<20000; i++){
            hash = getHash(password, hash);      
        }
        return hash;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String salt = generateSalt(150);
        String salt2 = generateSalt(150);
        String salt3 = generateSalt(150);
        String someString = "This is some string!";

        String hash = getSecureHash(someString, salt + salt2 + salt3);
        System.out.println(hash);
    }

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