1. 概述

SHA(安全哈希算法)是流行的加密哈希函数之一。加密哈希可用于对文本或数据文件创建签名。

在这个教程中,我们将探讨如何使用各种Java库执行SHA-256和SHA3-256哈希操作。

SHA-256算法生成一个几乎唯一的、固定大小的256位(32字节)哈希值。这是一种单向函数,因此结果不能被解密回原始值。

目前,SHA-2哈希广泛使用,因为它被认为是加密领域中最安全的哈希算法。

SHA-3是SHA-2之后的最新安全哈希标准。与SHA-2相比,SHA-3提供了一种生成独特单向哈希的不同方法,并且在某些硬件实现上可能更快。同样,SHA3-256是SHA-3中256位固定长度的算法。

NIST(美国国家标准与技术研究院)于2015年发布了SHA-3,因此目前SHA-3库的数量不如SHA-2多。直到JDK 9,内置默认提供者才包含SHA-3算法。

现在我们从SHA-256开始。

2. Java中的MessageDigest

Java提供了内置的MessageDigest类用于SHA-256哈希:

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

然而,我们需要使用自定义的字节到十六进制转换器来获取十六进制格式的哈希值:

private static String bytesToHex(byte[] hash) {
    StringBuilder hexString = new StringBuilder(2 * hash.length);
    for (int i = 0; i < hash.length; i++) {
        String hex = Integer.toHexString(0xff & hash[i]);
        if(hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }
    return hexString.toString();
}

需要注意的是,MessageDigest不是线程安全的。因此,我们应该为每个线程创建一个新的实例。

3. Guava库

Google Guava库也提供了一个用于哈希的实用类。

首先,让我们定义依赖关系:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

接下来,这是使用Guava对字符串进行哈希的方法:

String sha256hex = Hashing.sha256()
  .hashString(originalString, StandardCharsets.UTF_8)
  .toString();

4. Apache Commons Codec

类似地,我们也可以使用Apache Commons Codec:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.16.0</version>
</dependency>

这里有名为DigestUtils的实用类,支持SHA-256哈希:

String sha256hex = DigestUtils.sha256Hex(originalString);

5. Bouncy Castle库

5.1. Maven依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk18on</artifactId>
    <version>1.76</version>
</dependency>

5.2. 使用Bouncy Castle库进行哈希

Bouncy Castle API提供了一个用于转换十六进制数据为字节再返回的实用类。

但是,我们首先需要使用内置的Java API填充摘要:

MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(
  originalString.getBytes(StandardCharsets.UTF_8));
String sha256hex = new String(Hex.encode(hash));

6. SHA3-256

现在让我们继续SHA3-256。在Java中进行SHA3-256哈希与SHA-256没有太大区别。

6.1. Java中的MessageDigest

从JDK 9开始,我们可以直接使用内置的SHA3-256算法:

final MessageDigest digest = MessageDigest.getInstance("SHA3-256");
final byte[] hashbytes = digest.digest(
  originalString.getBytes(StandardCharsets.UTF_8));
String sha3Hex = bytesToHex(hashbytes);

6.2. Apache Commons Codec

Apache Commons Codec为MessageDigest类提供了一个方便的DigestUtils包装器。

该库从版本1.11开始支持SHA3-256,同时也要求JDK 9+:

String sha3Hex = new DigestUtils("SHA3-256").digestAsHex(originalString);

6.3. Keccak-256

Keccak-256是另一个流行的SHA3-256哈希算法。目前,它作为标准SHA3-256的替代方案。Keccak-256提供了与标准SHA3-256相同的安全性水平,只是在填充规则上有所不同。它已被用于诸如Monero等几个区块链项目。

再次,我们需要导入Bouncy Castle库来使用Keccak-256哈希:

Security.addProvider(new BouncyCastleProvider());
final MessageDigest digest = MessageDigest.getInstance("Keccak-256");
final byte[] encodedhash = digest.digest(
  originalString.getBytes(StandardCharsets.UTF_8));
String sha3Hex = bytesToHex(encodedhash);

我们也可以利用Bouncy Castle API进行哈希:

Keccak.Digest256 digest256 = new Keccak.Digest256();
byte[] hashbytes = digest256.digest(
  originalString.getBytes(StandardCharsets.UTF_8));
String sha3Hex = new String(Hex.encode(hashbytes));

7. 总结

在这篇简短的文章中,我们探讨了使用内置和第三方库在Java中实现SHA-256和SHA3-256哈希的几种方法。

示例代码的源代码可以在GitHub项目中找到。