如何在scala中正确生成字符串的SHA-256校验和?

15

我需要从一个将作为get参数发送的字符串生成SHA-256校验和。

我找到了这个链接来生成校验和。

生成校验和的方法如下:

  val digest = MessageDigest.getInstance("SHA-256");      
  private def getCheckSum() = {
    println(new String(digest.digest(("Some String").getBytes(StandardCharsets.UTF_8))))        
  }

打印出类似于这样的校验和:
*║┼¼┬]9AòdJb:#↓o6↓T╞B5C♀¼O~╟╙àÿG

我们需要将此发送到的API表示校验和应如下所示:

45e00158bc8454049b7208e76670466d49a5dfb2db4196

我做错了什么?
请给予建议。 谢谢。

1
她的消息摘要将返回原始字节。你期望的格式需要将它们编码为十六进制。 - puhlen
4个回答

27

等价的,但稍微更有效率:

MessageDigest.getInstance("SHA-256")
  .digest("some string".getBytes("UTF-8"))
  .map("%02x".format(_)).mkString

1
使用您的方法生成的哈希码似乎与从前面的回答中使用的方法生成的哈希码不相等:https://imgur.com/a/a3Gfv - An Illusion
是的,对此感到抱歉。现在已经修复了。 - Dima
2
@Regressor 这是一个 String.format 指令。x 表示十六进制,02 表示两位数,如果需要则用 0 填充。因此,例如,10(十六进制中的 a)将被打印为“0a”,而 255(ff)将是“ff”。 - Dima

13

java.security.MessageDigest#digest 返回一个字节数组。

scala> import java.security.MessageDigest
scala> import java.math.BigInteger

scala> MessageDigest.getInstance("SHA-256").digest("some string".getBytes("UTF-8"))
res1: Array[Byte] = Array(97, -48, 52, 71, 49, 2, -41, -38, -61, 5, -112, 39, 112, 71, 31, -43, 15, 76, 91, 38, -10, -125, 26, 86, -35, -112, -75, 24, 75, 60, 48, -4)

使用String.format创建十六进制数:

scala> val hash = String.format("%032x", new BigInteger(1, MessageDigest.getInstance("SHA-256").digest("some string".getBytes("UTF-8"))))
hash: String = 61d034473102d7dac305902770471fd50f4c5b26f6831a56dd90b5184b3c30fc

您可以使用命令行工具在Linux和Unix中验证哈希值。

$ echo -n "some string" | openssl dgst -sha256
61d034473102d7dac305902770471fd50f4c5b26f6831a56dd90b5184b3c30fc

注意:

如果Java返回的哈希长度小于64个字符,您可以使用0进行左填充。(例如:39)

def hash64(data: String) = {
  val hash = String.format(
               "%032x", 
               new BigInteger(1, MessageDigest.getInstance("SHA-256").digest(data.getBytes("UTF-8")))
             )
  val hash64 = hash.reverse.padTo(64, "0").reverse.mkString 
  hash64      
}

3
这里有一个小bug,字符串表示缺少前导的0,例如字符串39会返回b918943df0962bc7a1824c0555a389347b4febdc7cf9d1254406d80ce44e3f9而不是0b918943df0962bc7a1824c0555a389347b4febdc7cf9d1254406d80ce44e3f9 - Gustek
2
看起来你应该使用"%064x"而不是"%032x",这样就可以解决填充问题,因此你不需要使用padTo - Alex Hall

1

可以使用DatatypeConverter.printHexBinary

类似于:

DatatypeConverter.printHexBinary(
  MessageDigest
    .getInstance(algorithm)
    .digest("some string").getBytes("UTF-8")))

0
自 JDK 17 开始,我们可以使用 java.util.HexFormat
import java.security.MessageDigest
import java.util.HexFormat

val bytes = MessageDigest.getInstance("SHA-256")
  .digest("any string".getBytes("UTF-8"))

val sha256 =  HexFormat.of().formatHex(bytes)
// 1e57a452a094728c291bc42bf2bc7eb8d9fd8844d1369da2bf728588b46c4e75

val another = HexFormat.ofDelimiter(":").withUpperCase().formatHex(bytes)
// 1E:57:A4:52:A0:94:72:8C:29:1B:C4:2B:F2:BC:7E:B8:D9:FD:88:44:D1:36:9D:A2:BF:72:85:88:B4:6C:4E:75


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