CBC 文件加解密

想法

借助cli做一个命令行工具,可以对所有类型的文件进行加解密,并支持输入自定义的 key 和 iv
通过字节流读取文件,加解密采用上篇文章实现的 sm4算法,加密文件按照 [iv][hashed key][密文…] 格式进行组合

CBC 介绍

CBC 全称为密码分组链接(Cipher Block Chaining)模式,即每个明文块先与前一个密文块进行异或后,再进行加密,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量 iv

加密模式

cbc_enc

解密模式

cbc_dec

代码实现

这里给出了 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
66
67
68
69
70
71
72
73
74
func Encrypt(file, key, iv string) {
res := make([]byte, 0)
fileSuffix := path.Ext(file) //获取文件后缀

data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatal(err)
}

IV, err := hex.DecodeString(iv)
if err != nil {
log.Fatal("iv decode err :" + err.Error())
}
res = append(res, IV...) // 加密文件[0,16)表示iv

KEY, err := hex.DecodeString(key)
if err != nil {
log.Fatal("key decode err :" + err.Error())
}
sum := sha256.Sum256(KEY)
for i := 0; i < BlockSize; i++ { // 加密文件[16,32)表示hash之后的key
res = append(res, sum[i])
}

data = sm4.Padding(data, BlockSize) // 尾部填充
n := len(data)
for i := 0; i < n; i += BlockSize { // CBC加密
text := data[i : i+BlockSize]
process(text, IV)
IV = sm4.Encrypt(text, KEY)
res = append(res, IV...)
}
err = ioutil.WriteFile("tmp"+fileSuffix, res, 0644) //创建加密后的文件tmp
if err != nil {
log.Fatal(err)
}
}

func Decrypt(file, key string) {
res := make([]byte, 0)
fileSuffix := path.Ext(file) //获取文件后缀

data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatal(err)
}
n := len(data)

IV := data[:BlockSize]
KEY, err := hex.DecodeString(key)
if err != nil {
log.Fatal("key decode err :" + err.Error())
}

sum := sha256.Sum256(KEY)
for i := BlockSize; i < BlockSize*2; i++ { // 判断加解密的key是否相同
if sum[i-BlockSize] != data[i] {
log.Fatal("you use wrong key")
}
}

for i := 2 * BlockSize; i < n; i += BlockSize { // CBC解密
sText := data[i : i+BlockSize]
text := sm4.Decrpty(sText, KEY)
process(text, IV)
IV = sText
res = append(res, text...)
}

err = ioutil.WriteFile("result"+fileSuffix, sm4.UnPadding(res), 0644) //创建解密后的文件result
if err != nil {
log.Fatal(err)
}
}