gpt4 book ai didi

java - 如何多次哈希并在每轮中连接一个字符串

转载 作者:行者123 更新时间:2023-12-02 01:34:31 26 4
gpt4 key购买 nike

我正在编写一个程序,该程序在密码末尾连接一个单词 R,然后计算 SHA-256 哈希值。随后,在十六进制结果的末尾再次添加 R 字,并使用 SHA256 计算新的哈希值。

我希望这个重复 100 次。每次我想打印哈希值时。

所以像这样的伪代码:

hash = SHA256(...(SHA256(SHA256(“password”||R)||R)||R)..)

我目前正在通过哈希 2 次来测试我的代码:

   String R = "f@ghj!$g";
hash = password.concat(R);

MessageDigest md = MessageDigest.getInstance("SHA-256");
digest = hash.getBytes(StandardCharsets.UTF_8);

for (int i=0;i<2;i++) {

md.update(digest);
digest = md.digest();

hash = String.format("%064x", new BigInteger(1,digest)).concat(R);
System.out.println(hash);

digest = hash.getBytes(StandardCharsets.UTF_8);
}
<小时/>

让我们暂时忘记这个串联。

例如无法理解为什么以下两个代码会产生不同的结果:

代码1:

   for (int i=0;i<2;i++) {

md.update(digest);
digest = md.digest();

}

hash = String.format("%064x", new BigInteger(1,digest));
System.out.println(hash);

代码2:

   for (int i=0;i<2;i++) {

md.update(digest);
digest = md.digest();
//convert hash to string
hash = String.format("%064x", new BigInteger(1,digest));
//convert string again to bytes
digest = hash.getBytes(StandardCharsets.UTF_8);
}

System.out.println(hash);

我的问题是:每次将哈希值(Byte[])解码为十六进制字符串以连接R字并再次编码为字节的正确方法是什么正确的方法?

最佳答案

代码片段 1 是正确的,但您需要向其中添加 print 语句才能获得预期的输出。然而,为此,您需要使用真正的十六进制编码器/解码器,但默认情况下 java.util 中并未提供该编码器/解码器,这毫无帮助。

<小时/>

这是一个经过修改的示例,没有连接,我故意省略了它,以便给您留下一些事情要做。

代码使用相对较慢但易于内存和阅读的toHex函数。 BigInteger 首先需要构造一个 BigInteger,这是浪费并且可能甚至更慢。尽管该代码对于 32 字节哈希值似乎可以正常工作,但我仍然认为该代码难以维护。

public static byte[] printHexadecimalHashIterations(byte[] input, int iterations)
{
var digest = input.clone();

MessageDigest md;
try
{
md = MessageDigest.getInstance("SHA-256");
}
catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException("SHA-256 hash should be available", e);
}

for (int i = 0; i < iterations; i++)
{
md.update(digest);
digest = md.digest();

printDigest("Intermediate hash", digest);
}

printDigest("Final hash", digest);

return digest;
}

public static void printDigest(String hashType, byte[] digest)
{
var digestInHex = toHex(digest);
System.out.printf("%s: %s%n", hashType, digestInHex);
}

public static String toHex(byte[] data)
{
var sb = new StringBuilder(data.length * 2);
for (int i = 0; i < data.length; i++)
{
sb.append(String.format("%02X", data[i]));
}
return sb.toString();
}

public static void main(String[] args)
{
printHexadecimalHashIterations("password".getBytes(StandardCharsets.UTF_8), 2);
}
<小时/>

最主要的一点是,(安全)哈希函数的数据由字节(或八位字节,如果您喜欢这个名称)组成。 十六进制字符串只是这些字节的文本表示。它与数据本身并不相同。

您应该能够区分二进制数据和十六进制,十六进制只是二进制数据的表示形式。永远不要像问题中那样将二进制数据称为“十六进制”:这是一个危险信号,表明您无法理解其中的区别。

但是,在您的情况下,您只需要十六进制即可将它们打印到屏幕上;您根本不需要将 digest 字节数组转换为十六进制;它仍然可用。所以你可以继续下去。

<小时/>

如果您需要将此文本表示形式转换回字节,则需要执行十六进制解码。显然,您将再次需要一个不涉及 BigInteger 的好方法。有很多库(Guava、Apache Commons、Bouncy CaSTLe)提供了良好的十六进制编码器/解码器以及关于此的良好问题/答案 on SO 。代码片段 2 中的语句 hash.getBytes(StandardCharsets.UTF_8) 不执行十六进制解码,而是执行字符编码。

<小时/>

最后提示:update 方法允许将数据流式传输到摘要函数中。这意味着您实际上不需要连接任何内容来计算连接的摘要:您只需执行多次调用 update 即可。

祝你编程愉快。

<小时/>

编辑:

为了执行您的任务,我会这样做:

final byte[] passwordBytes = "password".getBytes(StandardCharsets.UTF_8);
final byte[] rBytes = "f@ghj!$g".getBytes(StandardCharsets.UTF_8);

digest.update(passwordBytes);
digest.update(rBytes);
byte[] currentHash = digest.digest();

for (int i = 1; i < iterations; i++)
{
digest.update(currentHash);
digest.update(rBytes);
currentHash = digest.digest();
}

关于java - 如何多次哈希并在每轮中连接一个字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55429947/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com