散列函数
散列函数,也称作哈希函数,消息摘要函数,单向函数或者杂凑函数。散列函数主要用于验证数据的完整性。通过散列函数,可以创建消息的“数字指纹”,消息接收方可以通过校验消息的哈希值来验证消息的完整性,防止消息被篡改。散列函数具有以下特性:
- 散列函数的运算过程是不可逆的,这个称为散列函数的单向性。
- 对于一个已知的消息及其散列值,要找到另外一个消息使其获得相同的散列值是不可能的,这个特性称为散列函数的弱碰撞性。这个特性可以用来防止消息伪造。
- 任意两个不同消息的散列值一定不同。
- 对原始消息长度没有限制。
任何消息经过散列函数处理后,都会产生一个唯一的散列值,这个散列值可以用来验证消息的完整性。计算消息散列值的过程被称为“消息摘要”,计算消息散列值的算法被称为消息摘要算法。常使用的消息摘要算法有:MD—消息摘要算法,SHA—安全散列算法,MAC—消息认证码算法。本文主要来了解MD算法。
MD5算法原理
假设原始消息长度是b(以bit为单位),注意这里b可以是任意长度,并不一定要是8的整数倍。计算该消息MD5值的过程如下:
1.填充信息
在计算消息的MD5值之前,首先对原始信息进行填充,这里的信息填充分为两步。
第一步,对原始信息进行填充,填充之后,要求信息的长度对512取余等于448。填充的规则如下:假设原始信息长度为b bit,那么在信息的b+1 bit位填充1,剩余的位填充0,直到信息长度对512取余为448。这里有一点需要注意,如果原始信息长度对512取余正好等于448,这种情况仍然要进行填充,很明显,在这时我们要填充的信息长度是512位,直到信息长度对512取余再次等于448。所以,填充的位数最少为1,最大为512。
第二步,填充信息长度,我们需要把原始信息长度转换成以bit为单位,然后在第一步操作的结果后面填充64bit的数据表示原始信息长度。第一步对原始信息进行填充之后,信息长度对512取余结果为448,这里再填充64bit的长度信息,整个信息恰好可以被512整除。其实从后续过程可以看到,计算MD5时,是将信息分为若干个分组进行处理的,每个信息分组的长度是512bit。
2.分组处理
在进行MD5值计算之前,我们先来做一些定义。
- 信息分组定义
原始信息经过填充之后,最终得到的信息长度(bit)是512的整数倍,我们先对信息进行分组,每512bit为一个分组,然后再将每个信息分组(512bit)再细分为16个小的分组,每个小分组的长度为32bit。规定如下
$M_p$ 代表经过填充之后的信息
$L_M$ 表示 $M_p$ 的长度(以 bit 为单位)
N 表示分组个数,$N = L_M/512$
$M[i]$ 表示将原始信息进行分组后的第 i 个信息分组,其中 i=1…N
$X[i]$ 表示将 $M[i]$ 进行分组后的第 i 个小分组,其中 i=1…16 - 标准幻数定义
现定义四个标准幻数如下,
A = 01 23 45 67
B = 89 ab cd ef
C = fe dc ba 98
D = 76 54 32 10
在计算机中存储时,采用小端存储方式,以A为例,A在Java中初始化的代码为为A=0x67452301 - 常量表T
T 是一个常量表,$T[i] = 4294967296 * abs(sin(i))$ 的运算结果取整,其中 i=1…64 - 辅助方法
我们定义四个辅助方法。
F(x,y,z) = (x & y) | ((~x) & z)
G(x,y,z) = (x & z) | (y & (~z))
H(x,y,z) = x ^ y ^ z
I(x,y,z) = y ^ (x | (~z))
其中,x,y,z长度为32bit
下面就是最核心的信息处理过程,计算MD5的过程实际上就是轮流处理每个信息分组的过程。
|
JDK 中的 MD5实现
Java提供的标准MD5算法实现如下所示
import java.security.MessageDigest; |