如何在Java中使用SHA-256哈希某个字符串?

297

我如何在Java中使用 SHA-256 哈希一些 String


2
可能是Java中通过SHA-256哈希字符串的重复问题。 - mmmmmm
1
https://www.baeldung.com/sha-256-hashing-java - Barett
18个回答

384

SHA-256不是一种“编码”方式 - 它是单向哈希。

你基本上需要将字符串转换为字节(例如使用text.getBytes(StandardCharsets.UTF_8)),然后对字节进行哈希。请注意,哈希的结果也将是任意的二进制数据,如果您想将其表示为字符串,应该使用base64或十六进制... 不要尝试使用String(byte[], String)构造函数。

例如:

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));

24
“SHA-256不是一种编码”这个说法绝对正确,但我必须说,相比于“如何使用sha进行加密”(许多人似乎认为它是加密),我更喜欢当前问题的标题。也许我们应该把它视为编码,而不是与密码学有关的东西,因为实际上这更接近它的使用方式。 - Luc
10
@Luc: 好的,它是一个密码哈希,所以我认为说它与密码学有关系是合理的...加密和密码学是不可互换的... - Jon Skeet
12
注意:在 Java 7+ 中,使用 StandardCharsets.UTF_8 而不是 "UTF-8" 文字常量是一个好主意,可以少担心一个已检查异常。 - kryger
3
当处理哈希结果时,为什么应该避免使用String(byte[], String)构造函数? - Isaac van Bakel
7
因为哈希值不是编码文本,它是任意二进制数据。 - Jon Skeet
显示剩余5条评论

231

我认为最简单的解决方案是使用Apache Common Codec

String sha256hex = org.apache.commons.codec.digest.DigestUtils.sha256Hex(stringText);   

2
最佳答案,易于使用,简洁。谢谢! - fl0w

114

将完整的哈希值转换为另一个字符串。

public static String sha256(final String base) {
    try{
        final MessageDigest digest = MessageDigest.getInstance("SHA-256");
        final byte[] hash = digest.digest(base.getBytes("UTF-8"));
        final StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < hash.length; i++) {
            final String hex = Integer.toHexString(0xff & hash[i]);
            if(hex.length() == 1) 
              hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    } catch(Exception ex){
       throw new RuntimeException(ex);
    }
}

7
将Jon的结果编码为十六进制,可以考虑使用现有的库,比如apache commons,而不是自己编写。 - Leigh
1
为什么要使用StringBuffer?(而不是StringBuilder)?也许将StringBuilder的默认大小设置为更好? - Bogdan
46
@Leigh:有些人不想添加整个库的依赖,仅仅因为他们需要其中的一个函数,因此自己编写这个函数有时是个好主意。 - Chris
5
@Chris - 没错,这就是为什么我说“考虑”使用它的原因;-) 现有的库可能会增加负担,但另一方面,它们通常比自己编写的代码经过更高的测试,并且节省时间。但并不是每个人都适用于通用解决方案。 - Leigh
2
你也可以阅读库的源代码并复制它的代码! - Olav Grønås Gjerde
显示剩余3条评论

112

另一个选择是Guava,它拥有易于使用的哈希工具包。例如,要使用SHA256对字符串进行哈希处理并将其转换为十六进制字符串,只需执行以下操作:

final String hashed = Hashing.sha256()
        .hashString("your input", StandardCharsets.UTF_8)
        .toString();

2
只需注意 - 自从版本11中引入以来,它在版本30中仍被标记为@Beta。 - m1ld

64

如果您使用的是Java 8,您可以通过以下方式对byte[]进行编码:

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
String encoded = Base64.getEncoder().encodeToString(hash);

1
这种方式对我来说很方便。但是,你应该使用以下代码: Base64.encodeToString(hash, Base64.DEFAULT); - Motassem Jalal
@MotassemJalal 在最新版本的Java8中不支持Base64.DEFAULT,我目前正在使用jdk1.8.0_144,你能告诉我你是如何创建它的吗? - rajadilipkolli
2
@rajadilipkolli 我认为这是Android的实现:https://developer.android.com/reference/android/util/Base64 - dbm
2
由于某种原因,我从这里得到了错误的结果。例如:对于输入“test”,我得到的是n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=而不是9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08。为什么会这样? - android developer
这是一种实现方式,但不是常见的标准。@android开发者,这就是为什么你在这里看到的值与其他实现中看到的值不同的原因。 - Durga Swaroop

15
import java.security.MessageDigest;

public class CodeSnippets {

 public static String getSha256(String value) {
    try{
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(value.getBytes());
        return bytesToHex(md.digest());
    } catch(Exception ex){
        throw new RuntimeException(ex);
    }
 }
 private static String bytesToHex(byte[] bytes) {
    StringBuffer result = new StringBuffer();
    for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
    return result.toString();
 }
}

将字节值与0xff进行按位&操作有什么意义?它不会产生任何结果,对吗? - yktoo
3
它将其转换为正整数(在Java中,字节是带符号的,不幸的是)。https://dev59.com/nWgu5IYBdhLWcg3wWl4q - leonbloy
StringBuffer 可以被 StringBuilder 替代。 - User8461

12
String hashWith256(String textToHash) {
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    byte[] byteOfTextToHash = textToHash.getBytes(StandardCharsets.UTF_8);
    byte[] hashedByetArray = digest.digest(byteOfTextToHash);
    String encoded = Base64.getEncoder().encodeToString(hashedByetArray);
    return encoded;
}

8

我通过追踪Apache代码中的DigestUtilssha256发现,它似乎默认返回计算结果给java.security.MessageDigest。Apache没有实现独立的sha256解决方案。我正在寻找一个独立的实现来与java.security库进行比较。仅供参考。


5

这里有一种更高效的方式将摘要转换为十六进制字符串:

private static final char[] hexArray = "0123456789abcdef".toCharArray();

public static String getSHA256(String data) {
    StringBuilder sb = new StringBuilder();
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(data.getBytes());
        byte[] byteData = md.digest();
        sb.append(bytesToHex(byteData);
    } catch(Exception e) {
        e.printStackTrace();
    }
    return sb.toString();
}

private static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for ( int j = 0; j < bytes.length; j++ ) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return String.valueOf(hexChars);
}

有没有人知道Java中更快的方法?


5
在Java 8中
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import javax.xml.bind.DatatypeConverter;


Scanner scanner = new Scanner(System.in);
String password = scanner.nextLine();
scanner.close();

MessageDigest digest = null;
try {
    digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
String encoded = DatatypeConverter.printHexBinary(hash);        
System.out.println(encoded.toLowerCase());

你为什么要这样使用try/catch? - dragi

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