自实现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位整数,保证位运算逻辑正确。
修复优先级建议
- 先修复轮次索引错误,修复后你就能观察到加盐、修改输入后缀时哈希值会发生变化
- 修复消息扩展的s1取值错误、工作变量重置问题
- 重写预处理填充逻辑,补全整数无符号截断处理
完成以上修复后,你的实现输出就能和标准SHA-256结果对齐。
注:你代码中
saltGen方法每次创建Random实例都用当前时间戳做种子的写法不影响哈希正确性,但存在安全隐患,密码学使用场景应改用SecureRandom生成盐值。
内容的提问来源于stack exchange,提问作者Trung Kiên
相关产品推荐
相关产品推荐

