基於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'\x80\x00') # 填充16個比特,第一個為一
t_2 = struct.unpack('>H', b'\x00\x00') # 填充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中內容