密码学之哈希/散列/杂凑算法

密码学之哈希/散列/杂凑算法

简介

​ 密码散列函数或叫Hash函数,该函数将一个任意长的比特串映射到一个固定长的比特串。该函数在数字签名和消息完整性检测方面有广泛的应用。

​ 散列算法的目的是为需要认证的数据产生一个“指纹”。为了实现对数据的认证,杂凑函数应该满足下列三个性质。

  • 第一是单向性,也就是为一个给定的输出找出能映射到该输出的一个输入在计算上是困难的。

  • 第二是弱无碰撞性,也就是为一个给定的输入找到能映射到同一个输出的另一个输入在计算上是困难的。

  • 第三是强无碰撞性,也就是发现不同的输入映射到同一个输出在计算上是困难的。

    如果散列函数对不同的输入具有相同的输出则称他们具有碰撞性。

    目前比较常见的哈希算法有MD5、SHA-1到SHA-256等。其最常见的应用就是使用共享密钥情况下的消息鉴别码,用以防止第三方攻击对消息内容的篡改或伪造。

MD5散列/哈希/杂凑算法

算法描述

​ 其总体过程如图所示,很显然其经过了几个比较明显的过程。首先就是散列算法一个比较明显的特征,散列算法的输入任意长度的比特串,但是其输出是定长的,所以在最开始时,会有一个分组的过程。接下来就是对分组的处理,但其实每个分组都是作为影响因子来输入的,其影响的初始数据就在上图中的缓冲区IV(可表示为4个32bit的寄存器ABCD)部分。每一次会对数据进行具体的核心MD5运算其输入就为当前处理的消息分组Cₙ和当前缓冲区ABCD,输出为根据当前处理分组Cₙ经过核心MD5运算后的缓冲区。每一组的输出作为相邻下一次运算的输入,也就是缓冲区始终作为输入嘛。

​ 经过最后一次核心MD5运算,最终得到的缓冲区ABCD的结果,就是我们的算法最终输出结果。得到一个长度为128位的比特串

​ 以下是具体过程

添加填充位

​ 我们知道,我们会对报文进行分组,而算法要求分出的每一组数据段的长度都是固定的,为512bit长度,但是我们无法保证输入的每一个报文的长度都为512的倍数对吧。所以这个时候要求我们对不满足长度为512的倍数的报文进行填充。

​ 所以我们往往这样对报文进行操作

​ 在报文尾部首先添加首位为1,其余位为0的填充位,并在末尾留有64bit的报文长度记录位,用于记录这个消息报文的长度X,用二进制数表示报文长度,不包括填充位的长度(当报文长度大到无法用64位的二进制数表示时,就需要取报文长度的最低64位)。

​ 而报文分组的倍数要求就是加上填充位以及长度记录位后需达到512的倍数,达不到就用填充为填充。但是注意,填充位是不可缺少的,所以,填充位的长度Y在1~512之间(Y>=1)。

​ 也就是(X+Y+64) mod 512 = 0

​ 或者说(X+Y) mod 512 = 448

分组操作

​ 在长度达到分组要求时,也就是在上述条件中如果报文长度满足X+Y+64 = 512*N,就需要对填充后的报文进行分组了,分组操作在总览中的图片中刻画得比较清楚,会将其分为N组,依照顺序将报文分为Y₀-Yₙ₋₁即可,每组512bit。

缓冲区初始化

​ 在每次的输入中,除了当前处理的相应当前分组外,还有一个固定的缓冲区输入。因为整个过程其实可以看作输入的当前消息分组对缓冲区IV持续影响的一个过程。所以我们会对缓冲区IV设置一个初值。

​ 缓冲区初始向量IV也是中间结果,会分为4个32位的字分别记作A、B、C、D。ABCD4个字的初始值如下(以小端法进行存储)

  • A = 0x67452301

  • B = 0xEFCDAB89

  • C = 0x98BADCFE

  • D = 0x10325476

MD5核心运算

轮操作层面

​ 万事俱备,只欠东风。准备好了N组512位的数据段后,每一组都将应用到MD5核心算法,跟缓冲区作运算。

​ 当前处理的分组512bit会分为16个32bit的字,我们可以将其记作M[n](n∈[0,15]),再次分组参与到每轮16步函数运算

​ 整个MD5算法一共有四轮,分别为FF、GG、HH、II,其中每一轮会再次细分为16步具体的函数运算,其总过程如图所示

​ 显然每一轮输出为下一轮的输入。而且在经过4轮运算后会与原缓冲区ABCD的数据进行异或才得到每次MD5核心算法的结果。

步操作层面

下面介绍的操作具体到轮运算中每一步的层面,看看在每一步中,数据与数据到底是如何进行运算的。每一轮操作中的具体的步操作大体框架步骤是一致但是在如何处理数据方面根据不同的轮次仍会有差别,使用的函数亦或是取的数据会不尽相同

下图是步操作的具体框架,每一轮会进行16次这样的迭代运算。

值得注意的就是F函数、数据子分组M[k]、常数ACT[i]和左移位数s就是在每一轮中会根据轮数的不同有差异的部分,下面将一次介绍各部分的意义和值的具体数值。其中,i为每一轮中的第i步,共16步,取值0-15

  • F函数:四轮过程会分别使用不同的逻辑函数F、G、H、I,每个逻辑函数的输入都是3个32bit的字,输出则是一个32bit的字,其运算为逐比特逻辑运算,包括与、或、非和异或逻辑运算。

    4个逻辑函数的定义为:

    • F(X, Y, Z) = (X & Y) | (!X & Z)

    • G(X, Y, Z) = (X & Z) | (Y & !Z)

    • H(X, Y, Z) = X ^ Y ^ Z

    • I(X, Y, Z) = Y ^ (X | !Z)

    注意,运算是逐笔特的,不会进位。&表示与运算、|表示或运算、!表示非运算、^表示异或运算。

  • 数据子分组M[k]:即输入的此消息分组中的第k个字(一共有16个分组,也就是16个字)。4论处理过程中,每轮会以不同的次序使用16个字,其次在第1轮以字的初始次序使用,第2到第4轮分别对字的次序i进行置换后得到一个新次序,然后以新次序使用16个字。3个置换分别为:

    • 第2轮GG轮时:k = (1 + 5i) mod 16

    • 第3轮HH轮时:k = (5 + 3i) mod 16

    • 第4轮II轮时:k = 7i mod 16

    其中,i为每一轮中的第i步,共16步,取值0-15

  • 常数ACT[i]:这里就需要引入每一轮的参数表了,改值在每一轮的每一步中都是固定的,具体值在下面的这个参数表中


DWORD md5::T[64] = {
0xD76AA478,0xE8C7B756,0x242070DB,0xC1BDCEEE,0xF57C0FAF,0x4787C62A,0xA8304613,0xFD469501,
0x698098D8,0x8B44F7AF,0xFFFF5BB1,0x895CD7BE,0x6B901122,0xFD987193,0xA679438E,0x49B40821,
//FF轮
0xF61E2562,0xC040B340,0x265E5A51,0xE9B6C7AA,0xD62F105D,0x02441453,0xD8A1E681,0xE7D3FBC8,
0x21E1CDE6,0xC33707D6,0xF4D50D87,0x455A14ED,0xA9E3E905,0xFCEFA3F8,0x676F02D9,0x8D2A4C8A,
//GG轮
0xFFFA3942,0x8771F681,0x6D9D6122,0xFDE5380C,0xA4BEEA44,0x4BDECFA9,0xF6BB4B60,0xBEBFBC70,
0x289B7EC6,0xEAA127FA,0xD4EF3085,0x04881D05,0xD9D4D039,0xE6DB99E5,0x1FA27CF8,0xC4AC5665,
//HH轮
0xF4292244,0x432AFF97,0xAB9423A7,0xFC93A039,0x655B59C3,0x8F0CCC92,0xFFEFF47D,0x85845DD1,
0x6FA87E4F,0xFE2CE6E0,0xA3014314,0x4E0811A1,0xF7537E82,0xBD3AF235,0x2AD7D2BB,0xEB86D391
//II轮
};

共64个32bit的字,从左到右从上到下依次被MD5核心运算的64步迭代运算所使用。

  • 左移位数s:在此过程,字会相应地左移s位,其值也会根据轮数或是步数而有所差异。具体值见下面这个参数表。


DWORD md5::s[64]={
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,   //FF轮
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,       //GG轮
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,   //HH轮
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21   //II轮
};

有了上诉参数值,就可以进行最核心的运算了,其每一步地运算表达如下式

Aₙ = Dₙ₋₁

Bₙ = Bₙ₋₁ + CLSₛ(Aₙ₋₁ + F(Bₙ₋₁, Cₙ₋₁, Dₙ₋₁) + X[k] + T[i])

Cₙ = Bₙ₋₁

Dₙ = Cₙ₋₁

其中,+表示逐位与计算

到这里,整个算法差不多就介绍得差不多了。

其他

MD5算法的输出固定为128位,也就是说,输出的值是在一个有限集中。但是我们的输入可以为任何值,也就是在一个无限集中。可以想象到的是,必然存在两个不同的输入,在经过MD5运算后,会产生相同hash值,这也就是所谓的碰撞。但通常情况下,对于给定的数值,找到这样一个跟它MD5后输出相同的值基本是很困难的。比如使用穷举法。但是,在我们中国有这样一位牛人,就找到了这样一种算法,可以找到能够产生碰撞的宁一组值,她就是原山东大学,现清华大学的王小云教授,“破解”了MD5。

但是仍需注意的是,如果只有经过MD5运算后的密文,找到它的明文,几乎是不可能的。

SHA运算跟MD5有相似性,暂不详细介绍

HMAC

散列消息鉴别码HMAC(Hashed Message Authentication Codes)算法,可以基于密钥将任意长度的报文消息转换成固定长度的报文摘要。常常运用在报文需要在公共信道传输时,对报文进行源端鉴别和完整性检测的一种算法。

如果仅仅只有一个算法,是无法完成源端鉴别和消息完整性检测的,因为任何人在公共信道上,或是被窃听之后拦截报文以后,可能会对报文进行篡改后再发送给目标以达到恶意目的。所以如果我们在双方协商出一个共享密钥,就能有效避免上诉情况,在攻击者得不到密钥的情况下,无法修改消息鉴别码,无法通过消息鉴别,从而无法篡改报文。

在一般情况下,消息鉴别码是根据需要进行完整性检测的消息计算出的,用于实现消息完整性检测的附加消息,通常是对消息的报文摘要,在计算出报文摘要后会将其拼接在原报文的尾部。

比较经典的运用就是IPsec中的AH报文和ESP报文的消息鉴别和TLS记录协议的MAC鉴别

其运算在我们已经知晓MD5算法具体步骤的情况下就比较简单了,将原来的报文和共享密钥拼接后作为新的报文处理

 

 

posted @ 2022-02-27 16:19  三木森林  阅读(2189)  评论(0编辑  收藏  举报