如何多次哈希并每轮拼接字符串?SHA-256实现疑问
问题解析与解决方案
嘿,我来帮你把这个问题掰明白~
首先先解释你困惑的Code 1 和 Code 2 结果不同的原因:
两者的核心差异在于第二次哈希的输入完全不一样:
- Code 1 是直接对「第一次哈希得到的字节数组」做第二次哈希,全程没有经过字符串的编码和解码,相当于计算
SHA256(SHA256(原始字节数组))。 - Code 2 则是把第一次哈希的字节数组转成十六进制字符串,再把这个字符串转回字节数组,然后对这个字符串的字节做第二次哈希。因为十六进制字符串的字节和原始哈希字节完全不是一回事(比如哈希字节里的
0x0A转成十六进制是"0a",对应的字节是0x30和0x61),所以两次哈希的输入天差地别,结果自然不同。
接下来是你的核心问题:每次将哈希(Byte[])解码为十六进制字符串以拼接R,再重新编码为字节的正确方式
按照你的伪逻辑(每次把上一次的哈希十六进制结果拼R,再哈希),正确的步骤和代码应该是这样的:
正确实现思路
- 初始输入:密码字符串直接拼接R,转成UTF-8字节数组。
- 循环100次:
- 对当前输入的字节数组计算SHA-256哈希,得到字节数组。
- 将哈希字节数组转换成固定64位的小写(或大写)十六进制字符串(必须补前导零,保证长度一致)。
- 输出这个十六进制哈希值。
- 把这个十六进制字符串和R拼接,转成UTF-8字节数组,作为下一次哈希的输入。
修正后的示例代码
import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class RepeatedHash { public static void main(String[] args) throws NoSuchAlgorithmException { String password = "password"; String R = "f@ghj!$g"; int totalIterations = 100; // 初始化第一次输入:密码拼接R String currentInput = password.concat(R); byte[] inputBytes = currentInput.getBytes(StandardCharsets.UTF_8); for (int i = 0; i < totalIterations; i++) { // 每次计算哈希都创建新的MessageDigest实例(也可以复用并调用reset()) MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] hashBytes = md.digest(inputBytes); // 转成64位小写十六进制字符串,补前导零避免长度缺失 String hexHash = String.format("%064x", new BigInteger(1, hashBytes)); System.out.println(hexHash); // 输出当前哈希值 // 拼接R,准备下一轮输入 currentInput = hexHash.concat(R); inputBytes = currentInput.getBytes(StandardCharsets.UTF_8); } } }
关键注意事项
- 固定十六进制字符串长度:必须用
%064x格式化,因为SHA-256是256位,对应32字节、64个十六进制字符。如果省略前导零(比如用%x),会导致字符串长度不足64,拼接R后的输入长度不一致,最终哈希结果完全错误。 - 统一大小写:要么全小写(
%064x)要么全大写(%064X),不能混合。因为A和a的UTF-8字节值不同,会导致拼接后的输入字节数组不一样,哈希结果也会不同。 - 正确使用MessageDigest:每次计算新的哈希时,要么新建实例,要么调用
md.reset()清空之前的状态,避免残留的update数据干扰结果。
内容的提问来源于stack exchange,提问作者Codefather
相关产品推荐
相关产品推荐

