本文目录导读:
- 第一步:消息预处理
- 第二步:初始化哈希值 (Initialization)
- 第三步:核心压缩函数 (Compression Function)
- 第四步:输出
- 为什么SHA算法安全(抗碰撞)?
- 与其他SHA算法的区别
我们来深入探讨一下SHA(安全散列算法)系列的底层原理,SHA家族中最常用的是SHA-256和SHA-1(已不再安全),它们的核心逻辑类似,但细节不同,这里我们以SHA-256为例进行讲解,因为它是现代应用最广、安全性也足够高的代表,SHA-1和SHA-512的原理可以视为其变体(主要是参数不同)。
SHA算法的核心目标:将任意长度的消息(输入)压缩成一个固定长度的、看似随机的散列值(输出,SHA-256的输出是256位,即32个字节),这个过程是单向的,即无法从散列值反推出原始消息。
SHA-256的底层原理可以拆解为以下几个关键步骤:
第一步:消息预处理
这一步是为了让输入消息的长度适合后续的分块处理。
-
消息填充 (Padding):
- 目的:将消息长度补足到
512位的整数倍(SHA-256的分块大小)。 - 过程:
- 在消息末尾添加一个
1位。 - 然后添加足够多的
0位,直到消息长度满足(原消息长度 + 填充长度 + 64) mod 512 = 0。 - 用64位的大端序整数表示原始消息的长度(bit数),附加在末尾。
- 在消息末尾添加一个
- 为什么这么麻烦? 填充保证了即使消息长度很短,也能进行后续的压缩循环,附加原始长度可以增强算法的安全性。
- 目的:将消息长度补足到
-
消息分块 (Message Block)
- 将填充后的消息分割成若干个
512位的块,每个块又可以进一步划分为16个32位的字(称为W[0]到W[15])。
- 将填充后的消息分割成若干个
第二步:初始化哈希值 (Initialization)
SHA-256使用8个固定的初始哈希值(常量),它们是通过取前8个质数(2, 3, 5, 7, 11, 13, 17, 19)的平方根的小数部分的前32位得到的,这些值称为 H[0] 到 H[7],本质上是算法的初始状态。
H[0] = 0x6a09e667H[1] = 0xbb67ae85H[2] = 0x3c6ef372H[3] = 0xa54ff53aH[4] = 0x510e527fH[5] = 0x9b05688cH[6] = 0x1f83d9abH[7] = 0x5be0cd19
第三步:核心压缩函数 (Compression Function)
这是整个算法的核心,对于每一个512位的消息块,SHA-256执行64轮(轮次)的复杂运算。
-
消息扩展 (Message Schedule)
- 将当前处理的512位块(16个字
W[0..15])扩展成64个32位的字(W[0]到W[63])。 - 扩展公式:
- 对于
t从 16 到 63: W[t] = σ1(W[t-2]) + W[t-7] + σ0(W[t-15]) + W[t-16]
- 对于
σ0和σ1是特定的位运算函数(循环右移、异或等),这个扩展过程将16个字的信息“扩散”到64个字中,增加了算法的混淆性和雪崩效应。
- 将当前处理的512位块(16个字
-
工作变量 (Working Variables)
- 初始化8个工作变量
a, b, c, d, e, f, g, h为当前块的初始哈希值(H[0]到H[7])。
- 初始化8个工作变量
-
64轮循环 (Main Loop)
- 对于每一轮
t(0 到 63):- 计算两个临时变量
T1和T2:T1 = h + Σ1(e) + Ch(e,f,g) + K[t] + W[t]T2 = Σ0(a) + Maj(a,b,c)
- 更新工作变量:
h = gg = ff = ee = d + T1d = cc = bb = aa = T1 + T2
- 计算两个临时变量
- 关键函数解释:
Σ0(x)和Σ1(x):对32位字进行循环右移和异或操作,用于混淆。Ch(x,y,z):选择函数 ((x & y) ^ (~x & z)),如果x的某一位是1,选择y的对应位;否则选择z的对应位。Maj(x,y,z):多数函数 ((x & y) ^ (x & z) ^ (y & z)),对于每一位,选择x,y,z中出现次数多的那个值(0或1)。K[t]:64个常量,取自前64个质数的立方根的小数部分的前32位,为每轮加入不同的随机性。W[t]:64个扩展后的消息字。
- 对于每一轮
-
更新哈希值 (Update Hash Values)
- 64轮循环结束后,将工作变量
a到h的值与上一轮的哈希值H[0..7]相加(模2^32加法):H[0] = H[0] + aH[1] = H[1] + bH[7] = H[7] + h
- 64轮循环结束后,将工作变量
第四步:输出
当所有消息块都处理完毕后,最终的 H[0] 到 H[7] 就是256位的散列值,通常以32个十六进制数字的形式输出。
为什么SHA算法安全(抗碰撞)?
- 单向性:复杂的运算、模加法、非线性函数(
Ch,Maj)使得从散列值反推输入在计算上不可行。 - 雪崩效应:输入中任意一位的变化,都会导致输出中大约一半的位发生变化,由于64轮循环和消息扩展,这种变化会被迅速放大和扩散。
- 抗碰撞性:找到两个不同的输入产生相同散列值的难度极高(理论上需要2^128次尝试),SHA-256的抗碰撞性至今(2025年)未被攻破。
- 抗第一原像攻击:给定一个散列值,找到一个输入使其散列为该值,难度极高(2^256)。
与其他SHA算法的区别
| 算法 | 输出长度 | 消息块大小 | 轮数 | 状态字 | 安全性现状 |
|---|---|---|---|---|---|
| SHA-1 | 160位 | 512位 | 80轮 | 5个32位字 | 已被攻破(找到实际碰撞),不推荐使用 |
| SHA-224 | 224位 | 512位 | 64轮 | 8个32位字 | 安全,是SHA-256的截断版 |
| SHA-256 | 256位 | 512位 | 64轮 | 8个32位字 | 安全,广泛使用 |
| SHA-384 | 384位 | 1024位 | 80轮 | 8个64位字 | 安全,是SHA-512的截断版 |
| SHA-512 | 512位 | 1024位 | 80轮 | 8个64位字 | 安全,适用于需要更高安全性的场景 |
SHA-256的底层原理可以概括为:
- 填充消息使之成为512位的倍数,并附加原始长度。
- 初始化8个哈希状态。
- 对每个512位块:
- 扩展消息:将16个字扩展成64个字。
- 执行64轮压缩循环:利用非线性函数、循环移位、异或、模加法,并混合当前轮次的常量和扩展后的消息字,持续更新8个工作变量。
- 累积结果:将本轮的结果加到之前的哈希状态上。
- 输出最终的256位状态。
这个精巧的设计确保了其不可逆性和抗碰撞性,现在你可以在代码层面去实现它,或者利用LibTomCrypt等开源库直接调用。