密码加密解密(七)——MD5散列算法实现

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)

测试结果:
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值