You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自实现Java SHA256算法加不同盐值仍输出相同哈希值问题排查

问题根因

你的SHA-256实现存在多处逻辑错误,其中几处核心错误直接导致了「同前缀输入哈希固定、加盐不改变结果」的现象,具体错误点如下:

1. 压缩轮次索引完全用错(最核心bug)

在64轮压缩循环里,你错误使用了**分块索引i**去读取轮常量K和消息调度数组w,完全没有用到轮次索引j:

// 错误代码
for (int j = 0; j < 64; j++) {
  int temp1 = h + rightRotate3(e) + ((e & f) ^ (~e & g)) + K[i] + w.get(i);
  // ...
}

这意味着64轮运算全程只读取了当前块的第一个32位字、第一个轮常量参与计算,块内后续所有字节(包括你拼接的盐值、admin后面多输入的字符)完全没有参与运算,自然不管加什么盐、后面拼什么内容,只要开头几个字节一致,结果就完全相同。
此处应改为K[j]和w.get(j)。

2. 消息扩展逻辑取值错误

生成w数组16~63位元素时,s1的输入源写错了:

// 错误代码
for (int j = 16; j < 64; j++) {
  int s0 = rightRotate1(w.get(j - 15));
  int s1 = rightRotate2(w.get(j - 15)); // 错误,和s0取了同一个位置
  w.add(w.get(j - 16) + s0 + w.get(j - 7) + s1);
}

按照SHA-256规范,s1应该取w.get(j-2)计算,你当前写法会导致消息扩展生成的后续字完全错误,就算修复了轮次索引,计算结果也和标准值不一致。

3. 工作变量未按块重置

你在所有分块的循环外部只初始化了一次a~h这8个工作变量:

// 错误位置:在分块循环外初始化
int a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
for (i = 0; i < l; i++) {
  // 处理块逻辑,没有重新给a~h赋值
}

正确逻辑是每处理一个新块,都要把a~h重置为当前H数组的8个哈希初值/上一块的计算结果,否则多块输入的压缩初始值完全错误。

4. 消息预处理填充逻辑错误

你当前的填充逻辑存在两个问题:

  • 追加10000000(即0x80,代表消息结束的分隔位)的判断不严谨:如果剩余待处理的尾巴刚好凑满4字节,你不会进入temp字符串的拼接逻辑,直接漏掉这个分隔字节;如果原始消息长度超过55字节,加完分隔位后一个块塞不下64位长度字段,你也没有做跨块填充处理,直接硬补0凑32位,导致长度字段位置完全错误。
  • 补零长度计算错误:int l = 14 - hash.size() % 16;的计算逻辑不符合SHA-256规范,正确规则是补零直到「消息+分隔位+补零」的总比特长度模512等于448,预留最后64位存原始消息的比特长度。

5. 整数溢出未做无符号处理

Java的int是32位带符号整数,你代码中所有加法运算直接使用原生int相加,溢出后符号位会干扰后续旋转、位运算的结果,每次加法运算后需要和0xFFFFFFFF做按位与,将值截断为无符号32位整数,保证位运算逻辑正确。

修复优先级建议
  1. 先修复轮次索引错误,修复后你就能观察到加盐、修改输入后缀时哈希值会发生变化
  2. 修复消息扩展的s1取值错误、工作变量重置问题
  3. 重写预处理填充逻辑,补全整数无符号截断处理
    完成以上修复后,你的实现输出就能和标准SHA-256结果对齐。

注:你代码中saltGen方法每次创建Random实例都用当前时间戳做种子的写法不影响哈希正确性,但存在安全隐患,密码学使用场景应改用SecureRandom生成盐值。

内容的提问来源于stack exchange,提问作者Trung Kiên

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 23:54:32