Hash函数是将任意长的数字串转换成一个较短的定长输出数字串的函数,输出的结果称为Hash值。Hash函数具有如下特点:
(1)快速性:对于任意一个输入值x,由Hash函数H(x),计算Hash值y,即y=H(x),是非常容易的。
(2)单向性:对于任意一个输出值y,希望反向推出输入值x,使得y=H(x),是非常困难的。
(3)无碰撞性:对任意给定的数据块x,希望找到一个y,满足H(x)=H(y),且x≠y,具有计算的不可行性。
Hash函数可用于数字签名、消息的完整性检测、消息的起源认证检测等。现在常用的Hash算法有MD5、SHA-1等。下面从MD5入手来介绍Hash算法的实现机制。
MD系列单向散列函数是由Ron Rivest设计的,MD5算法对任意长度的输入值处理后产生128位的输出值。MD5算法的实现步骤如下(见图4-1):
在MD5算法中,首先需要对信息进行填充,使其字节长度与448模512同余,即信息的字节长度扩展至n512+448,11为一个正整数。填充的方法如下:在信息的后面填充第一位为1,其余各位均为0,直到满足上面的条件时才停止用0对信息填充。然后,再在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,现在的信息字节长度为n512+448+64=(n+1)*512,即长度恰好是512的整数倍,这样做的目的是为满足后面处理中对信息长度的要求。n个分组中第q个分组表示为Yq。
MD5中有A、B、C、D,4个32位被称作链接变量的整数参数,它们的初始值分别为:A=01234567,B=89abcdef,C=fedcba98,D=76543210
当设置好这4个链接变量后,就开始进入算法的4轮循环运算。循环的次数是信息中512位信息分组数目。
首先将上面4个链接变量复制到另外4个变量中:A到AA,B到BB,C到CC,D到DD,以备后面进行处理。
然后进入主循环,主循环有4轮,每轮循环都很相似。第1轮进行16次操作,每次操作对A、B、C和D中的其中3个作一次非线性函数运算,然后将所得结果加上第4个变量,文本的一个子分组和一个常数。再将所得结果向左循环移S位,并加上A、B、C或D其中之一。最后用该结果取代A、B、C或D其中之一。
以下是每次操作中用到的4个非线性函数(每轮一个)。
下面为每一轮16步操作中的4次操作,16步操作按照一定次序顺序讲行。
(注:“+”定义为mod232的模运算。)
M[j]表示在第q个512位数据块中的第j个32位子分组,0≤j≤15。
常数T[i]可以有如下选择,在第i步中,T[i]是4294967296*abs(sin(i))的整数部分(注:4294967296=232。),i的单位是弧度。其中,T[i]是32位的随机数源,它消除了输入数据中任何规律性的特征。
表4-1说明了四轮主循环中每轮16步操作的具体步骤。
所有这些完成之后,将A、B、C、D分别加上AA、BB、CC、DD。然后用下一分组数据继续运行算法,最后的输出是A、B、C和D的级联。
需要额外说明的是,2004年8月,在Crypto’2004国际密码学会议上,山东大学王小云教授的研究成果证实了MD5算法存在碰撞性,即不同的输入值经过MD5转换可以产生相同的输出值。这一发现意味着采用MD5算法的数字签名、完整性检验等信息安全应用系统将不再安全了,这就促使信息安全系统的设计者尽快去寻找和探索新的Hash算法。
源代码:(python语言编写)
import os
# 常数表T
T = [["d76aa478", "e8c7b756", "242070db", "c1bdceee", "f57c0faf", "4787c62a", "a8304613", "fd469501",
"698098d8", "8b44f7af", "ffff5bb1", "895cd7be", "6b901122", "fd987193", "a679438e", "49b40821"],
["f61e2562", "c040b340", "265e5a51", "e9b6c7aa", "d62f105d", "02441453", "d8a1e681", "e7d3fbc8",
"21e1cde6", "c33707d6", "f4d50d87", "455a14ed", "a9e3e905", "fcefa3f8", "676f02d9", "8d2a4c8a"],
["fffa3942", "8771f681", "6d9d6122", "fde5380c", "a4beea44", "4bdecfa9", "f6bb4b60", "bebfbc70",
"289b7ec6", "eaa127fa", "d4ef3085", "04881d05", "d9d4d039", "e6db99e5", "1fa27cf8", "c4ac5665"],
["f4292244", "432aff97", "ab9423a7", "fc93a039", "655b59c3", "8f0ccc92", "ffeff47d", "85845dd1",
"6fa87e4f", "fe2ce6e0", "a3014314", "4e0811a1", "f7537e82", "bd3af235", "2ad7d2bb", "eb86d391"]]
# 压缩函数每步左循环移位的位数
CLS = [[7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22],
[5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20],
[4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23],
[6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]]
F = lambda x, y, z: ((x & y) | ((~x) & z))
G = lambda x, y, z: ((x & z) | (y & (~z)))
H = lambda x, y, z: (x ^ y ^ z)
I = lambda x, y, z: (y ^ (x | (~z)))
L = lambda x, n: (((x << n) | (x >> (32 - n))) & (int("ffffffff", 16)))
# 消息填充与分组
def MessageFill(message):
'''
args = {
对消息转换成十六进制,消息填充,补充长度,分组
message:输入需要求哈希值的字符串
返回分组,每个元素是128位十六进制字符串
}
'''
Hex_String = ''
for c in message:
Hex_String += hex(ord(c)).replace("0x", "") # 转16进制
if len(Hex_String) * 4 == 448: # 如果刚好消息为448位bit,则需要再添加512位
Hex_String += "80"
Hex_String = Hex_String.ljust(len(Hex_String) + 126, '0')
if len(Hex_String) * 4 % 512 != 448: # 消息填充
Hex_String += "80"
Hex_String = Hex_String.ljust(len(Hex_String) + (448 - len(Hex_String) * 4 % 512) // 4, '0')
l = len(message) * 8
if len(message) * 8 > pow(2, 64):
l = len(message) % pow(2, 64)
len_str = hex(l).replace("0x", '')
if len(len_str) % 2 != 0:
len_str = len_str.zfill(len(len_str) + 1) # 长度字符串
len_str1 = LittleEndian(len_str) # 长度字符串转换成小端存储
len_str1 = len_str1.ljust(16, '0')
Hex_String += len_str1 # 添加长度
Hex_List = []
for i in range(0, len(Hex_String), 128): # 将填充好的字符串进行分组
Hex_List.append(Hex_String[i:i + 128])
return Hex_List
def LittleEndian(s):
'''
args = {
字符串转换成小端存储
s:十六进制字符串
返回转换后的十六进制字符串
}
'''
str = ''
for i in range(len(s), 0, -2): # 字符串转换成小端存储
str += s[i - 2:i]
return str
def GetX(Y):
'''
args = {
返回X的值
Y:512位bit分组,字符串形式,128位
X1等:形如:["cccccccc","dddddddd"...]共16个,每个8位
返回X:形如[[],[],[],[]]
}
'''
X1 = []
X2 = []
X3 = []
X4 = []
for i in range(0, len(Y), 8):
X1.append(LittleEndian(Y[i:i + 8]))
for i in range(16):
X2.append(X1[(1 + 5 * i) % 16])
X3.append(X1[(5 + 3 * i) % 16])
X4.append(X1[7 * i % 16])
X = [X1, X2, X3, X4]
return X
def Compression_Fun(CV, XX, TT, FF, round, They_Count):
'''
args = {
压缩函数
CV:128为bit串,十六进制表示,形如["aaaaaaaa","bbbbbbbb"..]
XX:X[k],字符串形式
TT:T[i],常数表值
round = 轮数
They_Count:步数
返回新的CV,128为bit串,十六进制表示,形如["aaaaaaaa","bbbbbbbb"..]
}
'''
temp1 = hex(FF(int(CV[1], 16), int(CV[2], 16), int(CV[3], 16))).replace("0x", "").zfill(8)
temp2 = hex((int(CV[0], 16) + int(temp1, 16)) % pow(2, 32)).replace("0x", "").zfill(8)
temp3 = hex((int(temp2, 16) + int(XX, 16)) % pow(2, 32)).replace("0x", "").zfill(8)
temp4 = hex((int(temp3, 16) + int(TT, 16)) % pow(2, 32)).replace("0x", "").zfill(8)
temp5 = hex(L(int(temp4, 16), CLS[round][They_Count])).replace("0x", "").zfill(8)
temp6 = hex((int(temp5, 16) + int(CV[1], 16)) % pow(2, 32)).replace("0x", "").zfill(8)
new_CV = [CV[3], temp6, CV[1], CV[2]]
return new_CV
def HMD5(Y, CV):
'''
args = {
处理每个分组HMD5函数
Y:每一个分组,128位16进制
CV:上一轮压缩后的值,第一轮为初始值,128为bit串,十六进制表示,形如["aaaaaaaa","bbbbbbbb"..]
返回新一轮压缩后的值CV
}
'''
X = GetX(Y)
CV1 = CV.copy()
fun = [F, G, H, I]
for j in range(4):
for i in range(16):
temp_CV = Compression_Fun(CV1, X[j][i], T[j][i], fun[j], j, i)
CV1 = temp_CV.copy()
temp_CV.clear()
new_CV = []
for i in range(4):
new_CV.append(hex((int(CV[i], 16) + int(CV1[i], 16)) % pow(2, 32)).replace("0x", "").zfill(8))
return new_CV
def MD5(message):
'''
args = {
MD5算法的总控函数
message:需要压缩的消息
返回压缩后的哈希值,十六进制
}
'''
HeX_list = MessageFill(message) # 消息填充
CV0 = ["67452301", "EFCDAB89", "98BADCFE", "10325476"]
CV = CV0.copy()
for i in HeX_list: # 每一个分组进行处理
temp_CV = HMD5(i, CV)
CV = temp_CV.copy()
temp_CV.clear()
MD5_string = ''
for i in CV:
MD5_string += LittleEndian(i) # 转换成小端模式
return MD5_string
if __name__ == "__main__":
while 1:
choice = int(input("请输入选择[1]文件内容压缩[2]输入内容压缩[3]退出:"))
if choice == 1:
filename = input("请输入文件名:")
filepath = os.getcwd() + '\\' + filename
f = open(filepath, "r")
message = f.read()
print("文件内容为:" + message)
print("MD5求得该文件哈希值为:" + MD5(message))
elif choice == 2:
message = input("请输入消息:")
print("MD5求得哈希值为:" + MD5(message))
else:
exit(0)
测试结果: