0%

加密算法之对称加密

加密算法简介

加密算法就是加密的方法,在密码学中,加密是将明文信息隐藏起来,使之缺少特殊信息时不可读。加密算法可以分为两类:对称加密非对称加密

1. 对称加密

将信息使用一个密钥进行加密,解密时使用同样的密钥,同样的算法进行解密。

2. 非对称加密

非对称加密又称公开密钥加密,密钥分为私密密钥和公开密钥,通过公钥加密,私钥解密的过程,并且加密和解密算法不同。

关于对称加密

对称加密是加密和解密双方持有相同的私钥,即加密方通过密钥执行加密过程,解密方通过相同的密钥执行解密过程,因为双方持有相同的密钥,所以叫做对称加密。同时,因为密钥理论上只有加解密双方持有,所以也叫做私钥加密。常见的对称加密算法有:DES,3DES,AES以及RC4等。

对称加密分为序列密码和分组密码两类,如下图:

加密算法分类

1. 序列密码

序列密码单独加密每个位,它是通过将密钥序列中的每个位 与每个明文位相加实现的。同步序列密码的密码序列仅仅取决于密钥,而异步序列密码序列则取决于密钥和密文,如RC4。

2. 分组密码

分组密码每次使用相同的密钥加密整个明文位分组。这意味着对给定分组内任何明文位的加密都依赖于与它同在一个分组内的其他所有明文位。绝大多数分组密码的分组长度要么是128位(16字节),比如AES,要么是64位(8字节),比如DES和3DES。

对称加密特点

  1. 算法可逆,即加密后可以通过解密还原数据。
  2. 加密效率高,加密速度快
  3. 安全性没有非对称加密高,因为密钥分发时存在安全隐患

对称加密算法

在写对称加密算法之前,有必要先提一下分组加密的操作模式有哪些,因为分组加密是对称加密中应用最多的加密方式,并且可以有不同的操作模式。

分组加密模式

1. 电子密码本模式(ECB)

ECB模式要求明文长度必须是所使用密码的分组大小的整数倍,比如在AES(后面会提到)中,明文长度应该是16字节的整数倍,如果不满足此长度要求,则必须对其进行填充,填充的方式有很多种,比如:在明文后附加单个”1”,然后再根据需要附加足够多的”0”位,直到明文的长度达到分组长度的整数倍,如果明文的长度刚好是分组长度的整数倍,则填充的所有位刚好形成了一个单独的额外分组。ECB加密解密如下图:

其中$x_i$表示明文分组,e表示加密方法,k为密钥,$y_i$表示密文分组,$e^{-1}$表示解密方法。假设分组密码加密(解密)的分组大小为b位,长度超过b位的消息将被分割为大小为b位的分组,如果消息长度不是b位的整数倍,则在加密前必须将其填充位b位的整数倍。ECB模式中的每个分组都是单独加密的,

ECB优缺点:

  1. 加密和解密之间的分组同步不是必须的,即如果数据丢失,接收方还是可能解密已收到的分组。
  2. 加密高度确定,即只要密钥不变,相同的明文分组总是产生相同的密文分组。
  3. 明文分组的加密是完全独立的,与前面的分组没有任何关系。如果攻击者将密文分组重新排序,有可能会得到有效的明文。
  4. 密码分组链接模式(CBC)

2. 密码分组链接模式(CBC)

CBC模式主要基于两种思想。第一,所有分组的加密都链接在一起,是得密文$y_i$不仅依赖$x_i$,而且还依赖前面所有的明文分组。第二,加密过程使用的初始向量(IV)进行了随机化。

密文$y_i$是明文$x_i$加密后的结果,它将反馈为密码输入,并与后续明文分组$x_{i+1}$进行异或操作,然后将得到的异或和进行加密,得到下一个密文$y_{i+1}$,而这个密文将被用来加密$x_{i+2}$,以此类推。整个过程如下图所示。

由于第一个明文分组$x_1$没有前向密文,所以将IV与第一个明文相加会使每轮的CBC加密变得不确定。

注意:第一个密文$y_1$取决于明文$x_1$(和IV);第二个密文$y_2$则取决于IV、$x_1$和$x_2$;第三个明文取决于IV、$x_1$、$x_2$和$x_3$,以此类推。最后一个密文则是所有明文分组和IV的函数。

3. 输出反馈模式(OFB)

OFB模式使用分组密码来构建一个序列密钥加密方案。注意,在OFB模式中,密钥序列不是按位产生,而是以分组方式产生。密码的输出是b个密钥序列位,其中b为所使用的分组密码的宽度;将b为的明文与该b位的密钥序列进行异或操作即可实现对明文的加密。如下图:

OFB模式首先使用分组密码加密IV,得到的密钥输出为b位密钥序列的第一个集合;将前一个密钥输出反馈给分组密码进行加密,即可计算出密钥序列位的下一个分组;不断重复此过程。
因为使用的是同步序列密码,所以加密与解密操作完全相同,且加密解密位异或操作。OFB的一个优点就是分组密码的计算与明文无关。因此,可以预计算密钥序列材料的一个或多个分组$S_i$。

4. 密码反馈模式(CFB)

CFB模式也使用分组密码作为构建序列密码的基本元件,与OFB模式相同的是,CFB也使用了反馈;而不同的是,OFB反馈的是分组密码的输出,而CFB反馈的是密文。OFB的基本思想是:要生成第一个密钥序列分组$S_1$,必须先加密IV;而所有后续密钥序列分组$S2$,$S_3$,…都是通过加密前一个密文得到的。如下图所示:

根据序列密码加密,CFB加密解密操作也完全相同,但CFB模式是一个异步序列密钥加密

5. 计数器模式(CTR)

与OFB和CFB模式一样,密钥序列也是以分组方式计算的。分组密码的输入为一个计数器,每当分组密码计算一个新密钥序列分组时,该计数器都会产生一个不同的值,如下图所示:

初始化分组密码的输入时必须避免两次使用相同过的输入值,如果攻击者知道了使用相同输入加密的明文中的任何一个,就可以计算出密钥序列分组,从而可以解密其他明文。计数器模式最大的特点就是并行化,因为计数器模式不需要任何反馈。加入有两个分组加密引擎,即可以让两个引擎同时使用第一个分组密码加密计数器值$TRC_1$和$CTR_2$,这两个分组密码引擎完成后,一个继续加密$CTR_3$,而另一个则加密$CTR_4$。

6. 伽罗瓦计数器模式(GCM)

GCM是一种计算消息验证码的加密模式。MAC(消息验证码)由发送者计算密码校验和,并附加在消息后面,接收方也会计算此消息的密码校验和,并校验与发送者发送过来的是否相同,这样便可以确认消失是否是发送者发送过来的,且密文是否有被修改过。

主流对称加密算法实现(Go语言)

下面将逐一介绍上面提到的对称加密的算法实现。

1. RC4

RC4为流加密算法,即前面提到的序列加密,由伪随机数生成器和异或运算组成,密钥长度可变,范围在[1,255]。RC4一个字节一个字节地加解密,由于采用的是异或运算,使用的是同一套算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func Encrypt(bytes []byte, key []byte) ([]byte, error) {
c, err := rc4.NewCipher(key)
if err != nil {
return nil, err
}
var dst = make([]byte, len(bytes))
c.XORKeyStream(dst, bytes)
return dst, nil
}

//RC4为序列密码加密,采用异或运算,所以此处直接调用解密算法
func Decrypt(encryptData, key []byte) ([]byte, error) {
return Encrypt(encryptData, key)
}

2. DES(3DES)

DES是一种使用56位(7字节)(注:其实DES的输入密钥是64位(8字节),但是每个字节的第8位都作为前面7位的一个奇校验位,所以实际密钥有效位为56位。)密钥对64位(8字节)长分组进行加密的算法,加、解密过程使用相同对密钥。

DES算法机制为迭代,一共16轮,每轮操作完全相同,但使用不同的子密钥,子密钥都是由主密钥推导而来的,都是从输入密钥中选择48个置换位。DES加密流程如下:

3DES即为三重DES加密,3DES加密密钥长度为192位(24字节)。

解密过程主要为密钥编排逆转,即解密的第一轮需要子密钥16;第二轮需要子密钥15;以此类推。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
func Encrypt(plainText []byte, key []byte) ([]byte, error) {
//block, err := des.NewTripleDESCipher(key)为三重DES加密
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}

blockSize := block.BlockSize() //分组大小
plainText = paddingWithPKCS5(plainText, blockSize) //填充

var cipherText = make([]byte, len(plainText))

//ECB每个分组单独加密
var temp = cipherText
for len(plainText) > 0 {
block.Encrypt(temp, plainText[:blockSize])
plainText = plainText[blockSize:]
temp = temp[blockSize:]
}
return cipherText, nil
}

func Decrypt(cipherText []byte, key []byte) ([]byte, error) {
//block, err := des.NewTripleDESCipher(key)为三重DES加密
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}

blockSize := block.BlockSize() //分组大小

var plainText = make([]byte, len(cipherText))

//ECB每个分组单独加密
var temp = plainText
for len(cipherText) > 0 {
block.Decrypt(temp, cipherText[:blockSize])
cipherText = cipherText[blockSize:]
temp = temp[blockSize:]
}
plainText = unPaddingWithPKCS5(plainText)
return plainText, nil
}

func paddingWithPKCS5(data []byte, blockSize int) []byte {
//需要填充的值以及数量
padding := blockSize - len(data)%blockSize
//组装填充值([]byte)
var paddingData = []byte{byte(padding)}
paddingData = bytes.Repeat(paddingData, padding)
//append填充
data = append(data, paddingData...)
return data
}

func unPaddingWithPKCS5(data []byte) []byte {
padding := int(data[len(data)-1])
return data[:len(data)-padding]
}

3. AES

AES拥有三种不同的密钥长度抵抗蛮力攻击,分别为128位,192位和256位,分组大小为128位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func Encrypt(plainText []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

blockSize := block.BlockSize() //分组大小
plainText = paddingWithPKCS5(plainText, blockSize) //填充

var cipherText = make([]byte, len(plainText))

//CBC模式
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
blockMode.CryptBlocks(cipherText,plainText)
return cipherText, nil
}

func Decrypt(cipherText []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

blockSize := block.BlockSize() //分组大小

var plainText = make([]byte, len(cipherText))

//CBC模式
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
blockMode.CryptBlocks(plainText, cipherText)

plainText = unPaddingWithPKCS5(plainText)

return plainText, nil
}

引用资料

《维基百科》

《深入浅出密码学——常用加密技术原理与应用》

最后

如有错误,请不吝赐教!

Thanks!

附上代码链接[github.com/pyihe/secret]