1 报文摘要算法
报文摘要算法是一种将任意长度报文转换成固定长度的报文摘要算法。它具有以下六个特点:能够作用于任意长度的报文;产生有限位数的标识信息;易于实现;具有单向性;具有抗碰撞性;具有高灵敏性。
1.1 MD5
1.1.1 添加填充位
将报文X添加首位为1,其余为0的填充位,填充位长度为Y,使得X与Y的和模512为448。最后加上表示报文长度的64位二进制数,共同组成数据序列。
1.1.2 分组操作
将数据序列分割成512位的数据段,对每一段数据段单独进行报文摘要运算,每一段数据段的输入是512的数据段和前一段进行报文摘要运算后的128位结果。
1.1.3 MD5运算过程
每一段的512位数据段,它包含4级运算,每一级运算过程进行16次迭代运算。512位数据段被分成16个32位的字,16次迭代运算,输出为32位。4级运算一共产生4*32位=128位结果。
初始运算有一个128位的初始向量,第一个数据段的运算过程的输入是初始向量和第一个数据段,其后数据段的运算过程的输入是512位的数据段和上一段运算的结果。
1.2 SHA-1
与MD5有相同的填充过程与分组操作。不同的是初始向量与每一段的运算结果是5个32位的字;所以每一级进行20次迭代运算,4级共80次运算。需要将16个32位数据段扩展为80个32位字并分别参加80次迭代运算,但每一级操作使用同一个常量,故常数只需4个。
二者比较来看,SHA-1的摘要长度是160位,MD5的摘要长度是128位,SHA-1的抗碰撞性更好,同时也使得其计算复杂性高。
1.3 消息鉴别码(Hashed Message Authentication)
对消息的报文摘要进行加密运算后得到的结果,用于实现消息完整性检测的附加信息。
密钥K需要扩充到与数据段长度b相同,变为密钥K+。然后K+与某字节1异或得到S1,K+与某字节2异或得到S0,将S1与报文P串接后进行报文摘要运算,并将运算后的结果扩展为b位并与S0串接,再进行报文摘要运算,最后得到的结果就是HMAC。
1.4 报文摘要应用
1.4.1 完整性检测
应用密钥对报文进行加密并生成附加信息,将附加信息与报文一起发送,接收端可以使用相同密钥进行解密以验证报文在运输过程中是否被修改。
1.4.2 消息鉴别
消息鉴别是验证消息M确实是X发送的过程。
1.4.3 口令安全存储
算法的单向性使得在知道报文摘要h的情况下,无法找到报文P使得h=MD§。
1.4.4 数字签名
数字签名是某个报文的附加信息,该附加消息能够证明签名者的真实性,也能证明签名者是对该报文的确认。
2 报文摘要算法代码(MD5)
# -*- coding: utf-8 -*-
import struct
import math
import binascii
lrot = lambda x,n: (x << n)|(x >> 32- n) #循环左移的骚操作
#初始向量
A, B, C, D = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476)
# A, B, C, D = (0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210)
#循环左移的位移位数
r = [ 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
]
#使用正弦函数产生的位随机数,也就是书本上的T[i]
T = [int(math.floor(abs(math.sin(i + 1)) * (2 ** 32))) for i in range(64)]
def init_mess(message):
global A
global B
global C
global D
A, B, C, D = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476)
# A, B, C, D = (0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210)
#将python值转为字节流 Q--Standard size=8
length = struct.pack('<Q', len(message)*8) #原消息长度64位比特的添加格式,bytes类型
while len(message) > 64:
solve(message[:64])
message = message[64:]
#长度不足64位消息自行填充
#因为python3对字符串和二进制数据流做了明确的区分,所以需要先将message的数据类型改为bytes
message=bytes(message,'utf-8') # type convert to bytes
message += b'\x80'
message += b'\x00' * (56 - len(message) % 64)#len=56
message += length # len=64
#print(binascii.b2a_hex(message))
solve(message[:64])
def solve(chunk):
global A
global B
global C
global D
#将字节流转为python数据类型 I---Standard size=4 python type=integer
M = list(struct.unpack('<' + 'I' * 16, chunk)) #分成16个组,I代表1组32位
#print(M)
a, b, c, d = A, B, C, D
for i in range(64): #64轮运算
if i < 16: #每一轮运算只用到了b,c,d三个
f = ( b & c)|((~b) & d)
flag = i #用于标识处于第几组信息
elif i < 32:
f = (b & d)|(c & (~d))
flag = (5 * i +1) %16
elif i < 48:
f = (b ^ c ^ d)
flag = (3 * i + 5)% 16
else:
f = c ^(b |(~d))
flag = (7 * i ) % 16
tmp = b + lrot((a + f + T[i] + M[flag])& 0xffffffff,r[i]) #&0xffffffff为了类型转换
a, b, c, d = d, tmp & 0xffffffff, b, c
#print(hex(a).replace("0x","").replace("L",""), hex(b).replace("0x","").replace("L","") , hex(c).replace("0x","").replace("L",""), hex(d).replace("0x","").replace("L",""))
A = (A + a) & 0xffffffff
B = (B + b) & 0xffffffff
C = (C + c) & 0xffffffff
D = (D + d) & 0xffffffff
def digest():#output长度为16*8=128
global A
global B
global C
global D
print(type(struct.pack('<IIII',A,B,C,D)))
return struct.pack('<IIII',A,B,C,D)
def hex_digest():
#返回二进制表示的十六进制表示
# decode()方法以 encoding 指定的编码格式解码字符串,默认为字符串编码
return binascii.hexlify(digest()).decode()
if __name__ == '__main__':
while True:
mess = input("请输入你的信息:")
init_mess(mess)
out_put = hex_digest()
print(out_put)