基于MD5的HMAC
一、单向散列函数
种类有md4,md5,sha1,sha2,ripemd,ripemd160,sha3等
性质:
- 由不同长度的输入,生成固定长度的输出。
- 计算速度快。
- 单向性,由输入得到输出,由输出得不到输入。
- 弱抗撞击性:不容易找到一条消息与该消息的散列值相同
- 强抗撞击性:不容易找到两条消息的散列值相同
md5:
- 对消息的填充,使其比特长在模512下为448,即使消息本来为448,也需要填充512。填充方式固定第一位为1,后几位都填充0。
- 附加消息的填充,将消息的长度以小端方式64Bit长放在填充好的消息的后面。这样消息的长度被填充成512的整数倍。每512比特为一组,一组即为16个32位的字。
- 需要对4个32位长的寄存器进行初始化,每一个都有初始的固定的值,都以小端方式存储。
- 每一个分组都需要进行一个压缩函数的处理,每一分组经过压缩函数处理完的值为下一分组处理时的寄存器的初始值。压缩函数中包含4轮处理,每一轮经过压缩函数处理完的值为下一分轮处理时的寄存器的初始值。压缩函数中定义有一个常数表,每一轮处理时需要加上常数表中16个值。处理完后,最后一组的输出与第一轮寄存器中的值相加的值为最终本组的值。最后一组处理完后的最终结果为相应的哈希值。
- 每一轮的处理包含16步迭代。运算形式为b<=b+cls(a+g(b,c,d)+x[k]+t[i]) a<=d c<=b d<=c其中abcd为各个寄存器的值t[i]为相应的常数表中的16个值的一个,x[k]为分组中的16个字中的一个。Cls为移位运算每一轮每一步的移位数都有相应的常数的规定。X[k]的使用顺序也是按照一定规则在每一轮中都有相应的置乱.g为一个函数,每一轮的运算规则都不同。以下为移位规则,置乱规则,g的各轮定义
移位规则:
--- | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 7 | 12 | 17 | 22 | 7 | 12 | 17 | 22 | 7 | 12 | 17 | 22 | 7 | 12 | 17 | 22 |
2 | 5 | 9 | 14 | 20 | 5 | 9 | 14 | 20 | 5 | 9 | 14 | 20 | 5 | 9 | 14 | 20 |
3 | 4 | 11 | 16 | 23 | 4 | 11 | 16 | 23 | 4 | 11 | 16 | 23 | 4 | 11 | 16 | 23 |
4 | 6 | 10 | 15 | 21 | 6 | 10 | 15 | 21 | 6 | 10 | 15 | 21 | 6 | 10 | 15 | 21 |
- 置乱规则:第2轮(1+5i)mod16第3轮(5+3i)mod16第4轮7imod16
- g定义:
轮数 | g(b,c,d) |
---|---|
1 | (b∧c) ∨(b`∧d) |
2 | (b∧d) ∨(c∧d`) |
3 | b⊕c⊕d |
4 | c⊕(b∨d`) |
二、消息认证码
- 消息认证码的实现方法:单向散列函数,分组密码,其它密码
- hmac一种使用单向散列函数实现消息认证的方法,简要流程为
(1)如果密钥比散列函数的分组短,补零扩充。如果长计算密钥的散列值。
(2)将密钥与ipad异或(ipad为固定的值00110110)
(3)将结果与消息结合,之后计算散列值
(4)将密钥与opad异或(opad为固定的值01011010)
(5)将结果与散列值结合,之后计算散列值
三、说明
- 程序设置了几个函数:
(1)tianchong:判断消息比特位数,将消息填充到模512为448
(2)xiaoxi:md5的压缩函数,主要为消息进行分组,每512位一组,并对每一组运算,送入hash函
(3)hash:每一组的压缩函数的运算。首先算出用到的常数t[],for i in range(1,65):T.append(math.floor(pow(2, 32)*abs(math.sin(i))))
然后用一个循环判断,判断运算属于哪一轮,之后根据这一轮的规则对消息进行置乱,最后送入16步迭代
(4)diedai:迭代函数。同样用一个循环判断轮数属于那一轮,之后根据这一轮的规则确定g的运算规则。移位的位数存在一个二维数组中,通过轮数与迭代数确定移位的位数,temp=(temp<<yiwei[lunshu-1][i]) - 在主函数中需要输入密钥及文档(需包含文档的实际位置)。读取到文档后,首先对读取到的字符转化为相应的整数,之后进行填充。
- 读取到密钥后,需要将密钥合并为16位的几个值,之后进行填充。直接在左侧插入0,填充到512位。将密钥与ipad与opad异或后得到两组16个32位的值,并通过md5的压缩函数进行运算,作为消息运算时4个寄存器中的初值。最后进行两轮md5的运算,第一轮运算后的128位同样要通过插入0填充到512位
四、代码及结果
def diedai(lunshu,x,t,reg):
yiwei=[[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]]
for i in range(0,16):
a = reg[0]
b = reg[1]
c = reg[2]
d = reg[3]
if lunshu==1:
temp=(b & c) | (~b & d)
elif lunshu==2:
temp=(b&d)|(c&~d)
elif lunshu==3:
temp=b^c^d
else:
temp=c^(b|~d)
temp=(a+temp+x[i]+t[i])%pow(2,32)
temp=(temp<<yiwei[lunshu-1][i])
temp=(temp+b)%pow(2,32)
reg=[d,temp,b,c]
return reg
def hash(zu_1,reg_x):
reg_0=reg_x
reg=reg_x
T = []
temp=[]
for i in range(1, 65):
T.append(math.floor(pow(2, 32) * abs(math.sin(i))))#得到常数t[]
for i in range(1,5):#4轮处理中每一轮不同的置换
if i==2:
for j in range(0,16):
h=(1+5*j)%16
zu_2[j]=zu_1[h]
elif i==3:
for j in range(0,16):
h=(5+3*j)%16
zu_2[j]=zu_1[h]
elif i==4:
for j in range(0,16):
h=(7*j)%16
zu_2[j]=zu_1[h]
else:
zu_2=zu_1
temp=diedai(i,zu_2,T[16*(i-1):16*i],reg)#16步迭代
for i in range(0,len(temp)):
reg[i]=temp[i]
for i in range(0,4):
reg[i]=(reg[i]+reg_0[i])%pow(2,32)
return reg
def xiaoxi(message,a,b,c,d):
changdu=len(message)
zu_num = math.floor(len(message) / 16)#得到组数
A_0 = a
B_0 = b
C_0 = c
D_0 = d
reg_0 = [A_0, B_0, C_0, D_0]
for i in range(0, zu_num):
reg_0 = hash(message[16 * i:16 * (i + 1)], reg_0)
return reg_0
def tianchong(xiao):
t_1 = struct.unpack('>H', b'x80x00') # 填充16个比特,第一个为一
t_2 = struct.unpack('>H', b'x00x00') # 填充16个比特,都为0
changdu=len(xiao)
bit_changdu = changdu * 16 % pow(2, 64)
t_3 = struct.pack('<Q', bit_changdu) # 需要填充的比特长度的值
if (changdu * 2 * 8 == 448): # 如果模512就为448,直接填充512比特
xiao.append(t_1[0])
for i in range(1, 32):
xiao.append(t_2[0])
else: # 否则
i = 0
xiao.append(t_1[0])
num = (changdu + 1) * 16
while num % 512 != 448:
num = num + 1
i = i + 1
if i % 16 == 0:
xiao.append(t_2[0])
xiao.append(struct.unpack('<H', t_3[0:2])[0]) # 将比特位数填充到后64位
xiao.append(struct.unpack('<H', t_3[2:4])[0])
xiao.append(struct.unpack('<H', t_3[4:6])[0])
xiao.append(struct.unpack('<H', t_3[6:8])[0])
changdu_k = len(xiao) # 扩充后的消息的长度
message=[]
i = 0
while i < changdu_k:
t = xiao[i] << 16
if i + 1 != changdu_k - 1:
message.append(t | (xiao[i + 1]))
else:
message.append(t)
i = i + 2
return message
import os
import struct
import math
import string
xiao_xi = [] # 存输入的消息
xiao = [] # 存扩展的消息
key=input('输入一个密钥:')
wenjian=input('输入文件所在位置:')
data = open(wenjian) # 文本文件输入
xiao_xi = data.read()
changdu = len(xiao_xi) # 输入消息的长度
for i in range(0, changdu): # 将每个字符转化为相应的整数
xiao.append(ord(xiao_xi[i]))
xiao=tianchong(xiao)
key_message=[]#存填充后的密钥
key_1=[]
key_2=[]
key_8int=[]
ipad=0x3636
opad=0x5A5A
i=0#对密钥进行填充
while i< len(key):
t =ord( key[i] )<< 8
if i!=len(key)-1:
key_message.append(t | ord(key[i + 1]))
else:
key_message.append(t)
i = i + 2
while len(key_message)*16!=512:#对密钥填充到512位
key_message.insert(0,0)
for i in range(0,len(key_message)):
key_1.append(key_message[i]^ipad)
key_2.append(key_message[i]^opad)
i = 0
key_11=[]#将密钥变为16个32位的字
while i < len(key_1):
t = key_1[i] << 16
if i!=len(key_1)-1:
key_11.append(t | (key_1[i + 1]))
else:
key_11.append(t)
i = i + 2
i=0
key_22=[]
while i< len(key_1):
t = key_2[i] << 16
if i!=len(key_1)-1:
key_22.append(t | (key_2[i + 1]))
else:
key_22.append(t)
i = i + 2
A_0 = 0x01234567
B_0 = 0x89ABCDEF
C_0 = 0xFEDCBA98
D_0 = 0x76543210
key1_hash=[]
key2_hash=[]
trans=[]
final=[]
key1_hash.extend(xiaoxi(key_11,A_0,B_0,C_0,D_0))
key2_hash.extend(xiaoxi(key_22,A_0,B_0,C_0,D_0))
trans.extend(xiaoxi(xiao,key1_hash[0],key1_hash[1],key1_hash[2],key1_hash[3]))
while len(trans)*16!=512:
trans.insert(0,0)
final.extend(xiaoxi(trans,key2_hash[0],key2_hash[1],key2_hash[2],key2_hash[3]))
print('消息认证码为: ')
print(final)
1.txt中内容