Road to growth of rookie

Meaningful life is called life

0%

DES/AES 对称加密

对称加密 (Symmetric-key algorithm) 又被称为: 对称秘钥加密、私钥加密、共享密钥加密. 是密码学中一个种加密算法, 这类加密算法在加密和解密时需要使用到同一个秘钥. 根据不同的加密算法秘钥长度也不同, 一般在 64bit ~ 256bit 之间, 具体长度取决于所需的安全级别, 但是秘钥长度越长加密和解密所需要消耗的资源越多. 常用的对称加密算法有: DES (Data Encrytion Standard)3DES (Triple DES)AES (Advanced Encryption Standard) 等;

DES 加密

Data Encrytion Standard 简称 DES, DES 是一种分组加密算法, 以 64bit 为单位将明文分割并加密. DES 的秘钥长度也是 64bit (8字节), 每个字节中 1bit-7bit 为数据位, 第 8bit64bit , 也就是说秘钥中: 8bit16bit24bit32bit40bit48bit56bit64bit 都是 校验位 , 所以也有 DES 的秘钥长度为 56bit 的说法. 美国国家标准局于 1977 年公布把它作为非机要部门使用的数据加密标准,三十年来,它一直活跃在国际保密通信的舞台上,扮演了十分重要的角色. 但随着计算机的进步, DES 已经能够被暴力破解.

3DES 加密

三重 DES 加密 (Triple DES) 简称 3DES. 现在 DES 已经可以暴力破解, 所以需要一种分组加密算法来代替 DES, 而 3DES 由此被开发出来代替 DES. 3DES 可以看做是 DES 的升级版.

image

3DES 的秘钥可分为三部分: K1K2K3, 三个秘钥决定了加密的安全性. 若数据安全性要求不那么高, K1 可以等于 K3, 也可以 K1K2K3 都使用同一秘钥 (相当于 DES 加密, 多此一举还耗费资源)

AES 加密

高级加密标准 (Advanced Encryption Standard) 简称 AES, 在 DES 可被暴力破解不再安全之后, AES 能够抵御已知的针对 DES 算法的所有攻击, 且比 3DES 加密效率更高, 安全性也更高

AES 也是分组加密算法, AES 的区块长度固定为 128bit, 秘钥长度则可以为 128bit192bit192bit (golang 中只支持 128bit192bit 长度的秘钥); 加密过程中使用的密钥是由 Rijndael 密钥生成方案 产生.

加密模式

密码学中, 分组(block)密码的工作模式(mode of operation)允许使用同一个分组密码密钥对多于一块的数据进行加密, 并保证其安全性. 分组密码自身只能加密长度等于密码分组长度的单块数据, 若要加密变长数据, 则数据必须先被划分为一些单独的密码块. 通常而言, 最后一块数据也需要使用合适填充方式将数据扩展到符合密码块大小的长度. 一种工作模式描述了加密每一数据块的过程, 并常常使用基于一个通常称为初始化向量的附加输入值以进行随机化, 以保证安全.

所有加密模式图示和代码示例只提供加密过程, 如需解密过程请根据加密过程反推

ECB 模式

电子密码本模式 (Electronic codebook) 简称 ECB, 需要加密的消息按照块密码的块大小被分为数个块, 并对每个块进行独立加密. 这样的加密方式会让密文有规律, 安全性较低, 常用在对数据安全性要求不高的场景. 加密过程图示:

image

DES ECB 模式加密和解密实现:
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
60
61
package main

import (
"bytes"
"crypto/des"
"fmt"
)

// The CBC mode and the ECB mode, there are strict limits on the size of the groups,
// During encryption, if the last group of the plaintext does not meet the group size,
// You need to fill in the group size
// By convention, the value of the supplementary array to be filled
// should be (block size - current size).
// If the clear text is just big enough to be grouped,
// then by convention you should also populate a whole set of data with a value of block size
func paddingLastGroup(plaintext []byte, blockSize int) []byte {
padNum := blockSize - len(plaintext)%blockSize
char := []byte{byte(padNum)}
newPlain := bytes.Repeat(char, padNum)

return append(plaintext, newPlain...)
}

// According to the previous fill specification,
// the fill data in the plaintext after decryption needs to be removed
func unPaddingLastGroup(plaintext []byte) []byte {
length := len(plaintext)
return plaintext[:length - int(plaintext[length - 1])]
}

func desEncrypt(plaintext, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

newPlain := paddingLastGroup(plaintext, block.BlockSize())
block.Encrypt(newPlain, newPlain)

return newPlain
}

func desDecrypt(cipherText, key []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

block.Decrypt(cipherText, cipherText)

return unPaddingLastGroup(cipherText)
}

func main() {
str := "this test string"
key := []byte("12345678")
cipherText := desEncrypt([]byte(str), key)
fmt.Printf("After encryption: %v\n", cipherText)
plaintext := desDecrypt(cipherText, key)
fmt.Printf("After decryption: %s\n", string(plaintext))
}
CBC 模式

密码分组链接 (Cipher-block chaining) 简称 CBC, 在 CBC 模式中, 每个明文块先与前一个密文块进行异或后, 再进行加密. 在这种方法中, 每个密文块都依赖于它前面的所有明文块. 同时, 为了保证每条消息的唯一性, 在第一个块中需要使用初始化向量.

image

IV 初始化向量: 初始化向量 (Initialization Vector) 是许多任务作模式中用于将加密随机化的一个位块, 由此即使同样的明文被多次加密也会产生不同的密文, 避免了较慢的重新产生密钥的过程. 初始化向量与密钥相比有不同的安全性需求, 因此IV通常无须保密, 然而在大多数情况中, 不应当在使用同一密钥的情况下两次使用同一个 IV.

DES CBC 模式加密和解密实现
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
60
61
62
63
64
65
package main

import (
"bytes"
"crypto/cipher"
"crypto/des"
"fmt"
)

// The CBC mode and the ECB mode, there are strict limits on the size of the groups,
// During encryption, if the last group of the plaintext does not meet the group size,
// You need to fill in the group size
// By convention, the value of the supplementary array to be filled
// should be (block size - current size).
// If the clear text is just big enough to be grouped,
// then by convention you should also populate a whole set of data with a value of block size
func paddingLastGroup(plaintext []byte, blockSize int) []byte {
padNum := blockSize - len(plaintext)%blockSize
char := []byte{byte(padNum)}
newPlain := bytes.Repeat(char, padNum)

return append(plaintext, newPlain...)
}

// According to the previous fill specification,
// the fill data in the plaintext after decryption needs to be removed
func unPaddingLastGroup(plaintext []byte) []byte {
length := len(plaintext)
return plaintext[:length-int(plaintext[length-1])]
}

func desEncrypt(plaintext, key, iv []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

newPlain := paddingLastGroup(plaintext, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, iv)
blockMode.CryptBlocks(newPlain, newPlain)

return newPlain
}

func desDecrypt(cipherText, key, iv []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

blockMode := cipher.NewCBCDecrypter(block, iv)
blockMode.CryptBlocks(cipherText, cipherText)

return unPaddingLastGroup(cipherText)
}

func main() {
str := "this test string"
key := []byte("12345678")
iv := []byte("abcdefgh")
cipherText := desEncrypt([]byte(str), key, iv)
fmt.Printf("After encryption: %v\n", cipherText)
plaintext := desDecrypt(cipherText, key, iv)
fmt.Printf("After decryption: %s\n", string(plaintext))
}
CFB 模式

密文反馈 (Cipher feedback) 简称 CFB, 类似于 CBC, 可以将块密码变为自同步的流密码; 工作过程亦非常相似, CFB的解密过程几乎就是颠倒的 CBC 的加密过程:

image

DES CFB 模式加密和解密实现
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
package main

import (
"crypto/cipher"
"crypto/des"
"fmt"
)

func desEncrypt(plaintext, key, iv []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

blockMode := cipher.NewCFBEncrypter(block, iv)
blockMode.XORKeyStream(plaintext, plaintext)

return plaintext
}

func desDecrypt(cipherText, key, iv []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

blockMode := cipher.NewCFBDecrypter(block, iv)
blockMode.XORKeyStream(cipherText, cipherText)

return cipherText
}

func main() {
str := "this test string"
key := []byte("12345678")
iv := []byte("abcdefgh")
cipherText := desEncrypt([]byte(str), key, iv)
fmt.Printf("After encryption: %v\n", cipherText)
plaintext := desDecrypt(cipherText, key, iv)
fmt.Printf("After decryption: %s\n", string(plaintext))
}
OFB 模式

输出反馈模式 (Output feedback) 简称 OFB, 可以将块密码变成同步的流密码. 它产生密钥流的块, 然后将其与明文块进行异或, 得到密文. 与其它流密码一样, 密文中一个位的翻转会使明文中同样位置的位也产生翻转. 这种特性使得许多错误校正码, 例如奇偶校验位, 即使在加密前计算, 而在加密后进行校验也可以得出正确结果.

image

DES OFB 模式加密和解密实现
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
package main

import (
"crypto/cipher"
"crypto/des"
"fmt"
)

func desOfbEncryptOrDecrypt(plaintext, key, iv []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

blockMode := cipher.NewOFB(block, iv)
blockMode.XORKeyStream(plaintext, plaintext)

return plaintext
}

func main() {
str := "this test string"
key := []byte("12345678")
iv := []byte("abcdefgh")
cipherText := desOfbEncryptOrDecrypt([]byte(str), key, iv)
fmt.Printf("After encryption: %v\n", cipherText)
plaintext := desOfbEncryptOrDecrypt(cipherText, key, iv)
fmt.Printf("After decryption: %s\n", string(plaintext))
}
CTR 模式

计数器模式 (Counter mode) 简称 CTR (也被称为 整数计数模式 (Integer Counter Mode) 继承 ICMSICSegmented Integer Counter)), 与 OFB 相似, CTR 将块密码变为流密码. 它通过递增一个加密计数器以产生连续的密钥流, 其中, 计数器可以是任意保证长时间不产生重复输出的函数, 但使用一个普通的计数器是最简单和最常见的做法.

注意图中的 “随机数” 与其它图中的 IV (初始化向量) 相同。IV、随机数和计数器均可以通过连接, 相加或异或使得相同明文产生不同的密文

image

DES CTR 模式加密和解密实现
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
package main

import (
"crypto/cipher"
"crypto/des"
"fmt"
)

func desCtrEncryptOrDecrypt(plaintext, key, iv []byte) []byte {
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}

blockMode := cipher.NewCTR(block, iv)
blockMode.XORKeyStream(plaintext, plaintext)

return plaintext
}

func main() {
str := "this test string"
key := []byte("12345678")
iv := []byte("abcdefgh")
cipherText := desCtrEncryptOrDecrypt([]byte(str), key, iv)
fmt.Printf("After encryption: %v\n", cipherText)
plaintext := desCtrEncryptOrDecrypt(cipherText, key, iv)
fmt.Printf("After decryption: %s\n", string(plaintext))
}

参考