加密算法的分类
- 对称加密
采用对称秘钥的加密系统,加密、解密过程均采用同一把秘钥,通信双方必须同时获得这把钥匙进行加密解密操作。
常见对称加密:DES,3DES,AES - 非对称加密
非对称加密系统采用的加密解密秘钥是不同的,加密的称为公钥,解密的称为私钥。公钥加密私钥解密、私钥签名公钥验证。
常见的非对称算法:RSA,DSA,ECC - 哈希函数加密算法
无需借助任何秘钥,Hash算法严格上来说并不属于加密算法,而是与加密算法属于并列关系的一种算法。概括来说,哈希(Hash)是将目标文本转换成具有相同长度的、不可逆的杂凑字符串(或叫做消息摘要),而加密(Encrypt)是将目标文本转换成具有不同长度的、可逆的密文。
常见的哈希加密算法:MD5,SHA-1,SHA-2
哈希函数特点
- 单向不可逆
哈希算法是一种单向密码体制,即只有加密过程,没有解密过程,很难通过结果推算出输入值,而对称和非对称加密算法都是通过秘钥反向推导密码原文。 - 可重复性
相同输入经过同一哈希函数得到相同散列值,但并非散列值相同则输入结果相同。 - 抗冲突性
不同的输入数据,经过同一散列函数,产生的散列值不相同,相同则产生哈希冲突,。对原始信息的任何一点改变都会导致结果的哈希值巨大的不同。举个例子,假如原始数据是几百万字的文章,你在其中哪怕改动一个标点,计算出的哈希值都会有很大的变化。 - 输出长度固定
无论输入的源数据的长度是多少,同一种Hash算法转换后结果的长度都相同,而加密转换后结果的长度一般与源数据的长度正相关。MD5的返回值总是128bit,SHA-1的返回值是160bit,都是固定长度,MD5如果按十六进制表示的话是32位十六进制的数,SHA-1是40位十六进制的数。
MD5
MD5即Message-Digest Algorithm 5(信息摘要算法5),是计算机广泛使用的散列算法之一。经MD2、MD3和MD4发展而来,诞生于20世纪90年代初。用于确保信息传输完整一致。虽然已被破解,但仍然具有较好的安全性,加之可以免费使用,所以仍广泛运用于数字签名、文件完整性验证以及口令加密等领域。
算法原理:
- 数据填充
对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足X mod 512=448。根据此公式得出需要填充的数据长度。填充方法:在消息后面进行填充,填充第一位为1,其余为0。 - 添加消息长度
在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度 对 264取模)。在此步骤进行完毕后,最终消息长度就是512的整数倍。 - 数据处理
准备需要用到的数据:
4个常数: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
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));
把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。
哈希冲突(碰撞)
散列算法得到的结果位数是有限的,比如MD5算法计算出的结果字长为128位,意味着只要我们穷举2^128次,就肯定能得到一组碰撞,下面让我们来看看一个真实的碰撞案例。我们之所以说MD5过时,是因为它在某些时候已经很难表现出散列算法的某些优势——比如在应对文件的微小修改时,散列算法得到的指纹结果应当有显著的不同,而下面的程序说明了MD5并不能实现这一点。
import hashlib
# 两段HEX字节串,注意它们有细微差别
a = bytearray.fromhex("0e306561559aa787d00bc6f70bbdfe3404cf03659e704f8534c00ffb659c4c8740cc942feb2da115a3f4155cbb8607497386656d7d1f34a42059d78f5a8dd1ef")
b = bytearray.fromhex("0e306561559aa787d00bc6f70bbdfe3404cf03659e744f8534c00ffb659c4c8740cc942feb2da115a3f415dcbb8607497386656d7d1f34a42059d78f5a8dd1ef")
# 输出MD5,它们的结果一致
print(hashlib.md5(a).hexdigest())
print(hashlib.md5(b).hexdigest())
### a和b输出结果都为:
cee9a457e790cf20d4bdaa6d69f01e41
cee9a457e790cf20d4bdaa6d69f01e41
而诸如此类的碰撞案例还有很多,上面只是原始文件相对较小的一个例子。事实上现在我们用智能手机只要数秒就能找到MD5的一个碰撞案例,因此,MD5在数年前就已经不被推荐作为应用中的散列算法方案,取代它的是SHA家族算法,也就是安全散列算法(Secure Hash Algorithm,缩写为SHA)。
SHA
SHA实际包括有一系列算法,分别是SHA-1、SHA-224、SHA-256、SHA-384以及SHA-512。而我们所说的SHA2实际是对后面4中的统称。各种SHA算法的数据比较如下表,其中的长度单位均为位:
MD5和SHA1,它们都有4个逻辑函数,而在SHA2的一系列算法中都采用了6个逻辑函数。
以SHA-1为例,算法包括有如下的处理过程:
- 对输入信息进行处理及填充长度信息
和MD5处理输入方式相同
- 信息分组处理
经过添加位数处理的明文,其长度正好为512位的整数倍,然后按512位的长度进行分组,可以得到一定数量的明文分组,我们用Y0,Y1,……YN-1表示这些明文分组。对于每一个明文分组,都要重复反复的处理,这些与MD5都是相同的。
而对于每个512位的明文分组,SHA1将其再分成16份更小的明文分组,称为子明文分组,每个子明文分组为32位,我们且使用M[t](t= 0, 1,……15)来表示这16个子明文分组。然后需要将这16个子明文分组扩充到80个子明文分组,我们将其记为W[t](t= 0, 1,……79),扩充的具体方法是:当0≤t≤15时,Wt = Mt;当16≤t≤79时,Wt = ( Wt-3 ⊕ Wt-8⊕ Wt-14⊕ Wt-16) <<< 1,从而得到80个子明文分组。
- 初始化缓存
所谓初始化缓存就是为链接变量赋初值。前面我们实现MD5算法时,说过由于摘要是128位,以32位为计算单位,所以需要4个链接变量。同样SHA-1采用160位的信息摘要,也以32位为计算长度,就需要5个链接变量。我们记为A、B、C、D、E。其初始赋值分别为:A = 0x67452301、B = 0xEFCDAB89、C = 0x98BADCFE、D = 0x10325476、E = 0xC3D2E1F0。
如果我们对比前面说过的MD5算法就会发现,前4个链接变量的初始值是一样的,因为它们本来就是同源的。
- 计算信息摘要
经过前面的准备,接下来就是计算信息摘要了。SHA1有4轮运算,每一轮包括20个步骤,一共80步,最终产生160位的信息摘要,这160位的摘要存放在5个32位的链接变量中。
在SHA1的4论运算中,虽然进行的就具体操作函数不同,但逻辑过程却是一致的。首先,定义5个变量,假设为H0、H1、H2、H3、H4,对其分别进行如下操作:
(A)、将A左移5为与 函数的结果求和,再与对应的子明文分组、E以及计算常数求和后的结果赋予H0。
(B)、将A的值赋予H1。
(C)、将B左移30位,并赋予H2。
(D)、将C的值赋予H3。
(E)、将D的值赋予H4。
(F)、最后将H0、H1、H2、H3、H4的值分别赋予A、B、C、D
这一过程表示如下:
而在4轮80步的计算中使用到的函数和固定常数如下表所示:
经过4轮80步计算后得到的结果,再与各链接变量的初始值求和,就得到了我们最终的信息摘要。而对于有多个明文分组的,则将前面所得到的结果作为初始值进行下一明文分组的计算,最终计算全部的明文分组就得到了最终的结果。