目录
Des
发展史
分组密码算法设计思想
Des概述
初始置换与逆置换
Feistel结构
轮函数F
E扩展
密钥加
S盒代换
P置换
密钥编排
3Des
优缺点
分组模式
CBC
CFB
OFB
Des的Go实现
明文填充
加密
解密
结果截图
参考
整体结构
明文进行初始置换(Initial Permutation,IP),通过密钥编排算法将密钥拆成16个,进行16轮迭代,最后通过逆置换得到密文。
将输入的64位数据块按位重新组合,把输出分为L0、R0两部分,每部分长32位。
58 |
50 |
42 |
34 |
26 |
18 |
10 |
2 |
60 |
52 |
44 |
36 |
28 |
20 |
12 |
4 |
62 |
54 |
46 |
38 |
30 |
22 |
14 |
6 |
64 |
56 |
48 |
40 |
32 |
24 |
16 |
8 |
57 |
49 |
41 |
33 |
25 |
17 |
9 |
1 |
59 |
51 |
43 |
35 |
27 |
19 |
11 |
3 |
61 |
53 |
45 |
37 |
29 |
21 |
13 |
5 |
63 |
55 |
47 |
39 |
31 |
23 |
15 |
7 |
表中的数字代表新数据中此位置的数据在原数据中的位置,即原数据块的第58位放到新数据的第1位,第50位放到第2位,……依此类推。置换后的数据分为L0和R0两部分。
逆置换就是放回原位置,例如,1位置在40位置,那么逆置换的第一个就是40,我就不一个个画了
40 | ... |
... | ... |
由于初始置换是公开的,没有密码意义。有很多DES实现都去除了,有人认为是当时为了更好放到DES芯片中。
按下述规则进行16次迭代,1≤i≤16
F(,)以长度为32比特串作为第一输入,以长度为48比特串作为第二个输入,产生长度为32比特的输出。
32 |
1 |
2 |
3 |
4 |
5 |
4 |
5 |
6 |
7 |
8 |
9 |
8 |
9 |
10 |
11 |
12 |
13 |
12 |
13 |
14 |
15 |
16 |
17 |
16 |
17 |
18 |
19 |
20 |
21 |
20 |
21 |
22 |
23 |
24 |
25 |
24 |
25 |
26 |
27 |
28 |
29 |
28 |
29 |
30 |
31 |
32 |
1 |
将扩展为48位,左右两列数据是扩展的数据,扩展的数据是从相邻位置取的一位,可理解为循环队列。靠近32位的位为1,靠近1位的位为32,其余就是下一行首或上一行末。
将的E扩展和进行异或
使用8个S盒S1……S8,每个Si是一个固定的4*16阶矩阵,其元素取0~15之间的整数。
给定长度为6的比特串,如Bj=b1b2b3b4b5b6,Sj(Bj)计算如下:
S盒1如下:
14 |
4 |
13 |
1 |
2 |
15 |
11 |
8 |
3 |
10 |
6 |
12 |
5 |
9 |
0 |
7 |
0 |
15 |
7 |
4 |
14 |
2 |
13 |
1 |
10 |
6 |
12 |
11 |
9 |
5 |
3 |
8 |
4 |
1 |
14 |
8 |
13 |
6 |
2 |
11 |
15 |
12 |
9 |
7 |
3 |
10 |
5 |
0 |
15 |
12 |
8 |
2 |
4 |
9 |
1 |
7 |
5 |
11 |
3 |
14 |
10 |
0 |
6 |
13 |
其他的我就不粘贴了,都是公开的,网上能找到,这里只是为了举个例子。
假如,S1的输入Bj为110011
P盒如下:
16 |
7 |
20 |
21 |
29 |
12 |
28 |
17 |
1 |
15 |
23 |
26 |
5 |
18 |
31 |
10 |
2 |
8 |
24 |
14 |
32 |
27 |
3 |
9 |
19 |
13 |
30 |
6 |
22 |
11 |
4 |
25 |
长度为32比特串C=C1C2C3C4C5C6C7C8,根据上面的进行替换,原理和前面的IP一样,就是按照P盒放原始数据的多少位,第一个就放C的第16位,依次类推,最后组合一下即可,不再举例。
前面是迭代加解密,接下来说下密钥编排,密钥编排就是通过密钥K,获得每轮的密钥ki,过程如下
给定64比特密钥K,根据固定的置换PC-1来处理K得到PC-1(K)=C0D0,其中C0和D0分别由最前和最后28比特组成。下面是PC-1,注意,不包括8,16,24,32,40,48,56和64,前面提到,这些是校验位。
57 |
49 |
41 |
33 |
25 |
17 |
9 |
1 |
58 |
50 |
42 |
34 |
26 |
18 |
10 |
2 |
59 |
51 |
43 |
35 |
27 |
19 |
11 |
3 |
60 |
52 |
44 |
36 |
63 |
55 |
47 |
39 |
31 |
23 |
15 |
7 |
62 |
54 |
46 |
38 |
30 |
22 |
14 |
6 |
61 |
53 |
45 |
37 |
29 |
21 |
13 |
5 |
28 |
20 |
12 |
4 |
计算Ci=LSi(Ci-1)和Di=LS(Di-1),且Ki=PC-2(CiDi),LSi表示循环左移两个或一个位置,具体地,如果i=1,2,9,16就移一个位置,否则就移两个位置,PC-2是另一个固定的置换。下面是PC-2
14 |
17 |
11 |
24 |
1 |
5 |
3 |
28 |
15 |
6 |
21 |
10 |
23 |
19 |
12 |
4 |
26 |
8 |
16 |
7 |
27 |
20 |
13 |
2 |
41 |
52 |
31 |
37 |
47 |
55 |
30 |
40 |
51 |
45 |
33 |
48 |
44 |
49 |
39 |
56 |
34 |
53 |
46 |
42 |
50 |
36 |
29 |
32 |
以上就是一轮中获取Ki的过程,每轮选取密钥K的不同48比特进行PC-2的置换,同样是16个矩阵,长宽与PC-2相同,这里就不粘贴了,不然文章太长了。
3DES,顾名思义,使用三次DES算法,有两种模式。
DES-EEE3模式:使用 P->DES加密->DES加密->DES加密->C 进行加密,使用 C->DES解密->DES解密->DES解密->P 进行解密。
DES-EDE3模式:使用 P->DES加密->DES解密->DES加密->C 进行加密,使用 C->DES解密->DES加密->DES解密->P 进行解密。
优点:
不足:
分组密码在加密时,明文分组的长度是固定的,而实际应用中待加密消息的数据量是不定的,数据格式多种多样。为了能在各种应用场合使用DES,美国在FIPS PUS 74和81中定义了DES的4种运行模式:
ECB,CBC,CFB,OFB,分组密码工作模式描述了如何重复加密比较长的多个数据块。
ECB,CBC是块模式,CFB、OFB是流模式。ECB安全性比较差,先不学了,Go的包中也没有。
CBC(Cipher Block Chaining, 密码块链)模式中每一个分组要先和前一个分组加密后的数据进行异或操作,然后再进行加密。
加密:
解密:
CFB(Cipher Feedback, 密码反馈)模式和CBC模式比较相似,前一个分组的密文加密后和当前分组的明文异或操作生成当前分组的密文。
加密:
解密:
注意,解密函数与加密函数一致。
OFB(Output Feedback,输出反馈)模式与CFB模式类似,区别在于使用上一个分组的密码序列加密生成当前分组的密码序列。
总结
CTR模式在学AES时再说。
好,以上都没什么用,因为DES被破解啦!!!(看到这句话的小伙伴都快哭了)不过,加强版还可以用,接下来就说说3DES。
我们不是使用Go语言来实现Des,已经有大神写好了,我们会调用就可以了。
des包
func NewCipher(key []byte) (cipher.Block, error)
创建并返回一个使用DES算法的cipher.Block接口。
cipher包
func NewCBCEncrypter(b Block, iv []byte) BlockMode
返回一个密码分组链接模式的、底层用b加密的BlockMode接口,初始向量iv的长度必须等于b的块尺寸。
BlockMode的方法
CryptBlocks(dst, src []byte)
加密或解密连续的数据块,src的尺寸必须是块大小的整数倍,src和dst可指向同一内存地址
func NewCBCDecrypter(b Block, iv []byte) BlockMode
返回一个密码分组链接模式的、底层用b解密的BlockMode接口,初始向量iv必须和加密时使用的iv相同。
CBC模式需要填充,解密后我们还需要去掉填充,由于填充什么没有要求,我们就填充缺少的长度,这样去掉填充时就容易切片了。
func PaddingLastGroup(plainText []byte,blockSize int) []byte{
padNum := blockSize - len(plainText)%blockSize
char := []byte{byte(padNum)}
newPlain := bytes.Repeat(char,padNum)
plainText = append(plainText,newPlain...)
return plainText
}
func UnpaddingLastGroup(plainText []byte) []byte {
length := len(plainText)
number := int(plainText[length-1])
return plainText[:length-number]
}
func DesEncrypt(plainText,iv, key []byte) ([]byte,error) {
if len(iv) != 8{
_, file, line, _ := runtime.Caller(0)
return nil,util.Error(file,line+1,errors.IvError)
}
block, err :=des.NewCipher(key)
if err != nil{
_, file, line, _ := runtime.Caller(0)
return nil,util.Error(file,line+1,errors.DesKeyError)
}
// padding plainText
newText := util.PaddingLastGroup(plainText,des.BlockSize)
// Create a CBC interface
blockMode := cipher.NewCBCEncrypter(block,iv)
// use same one to save space
blockMode.CryptBlocks(newText,newText)
return newText,nil
}
func DesDecrypt(cipherText,iv,key []byte) ([]byte,error) {
if len(iv) != 8{
_, file, line, _ := runtime.Caller(0)
return nil,util.Error(file,line+1,errors.IvError)
}
block, err :=des.NewCipher(key)
if err != nil{
_, file, line, _ := runtime.Caller(0)
return nil,util.Error(file,line+1,errors.DesKeyError)
}
// Create a CBC interface
blockMode := cipher.NewCBCDecrypter(block,iv)
plainText := make([]byte,len(cipherText))
blockMode.CryptBlocks(plainText,cipherText)
return util.UnpaddingLastGroup(plainText),nil
}
部分测试代码
iv := []byte("12345678")
key := []byte("ladykill")
plainText := []byte("hellocrypto")
cipherText,err := DesEncrypt(plainText,iv,key)
if err != nil{
fmt.Println(err)
os.Exit(0)
}
fmt.Printf("加密后:%s\n",string(cipherText))
decryText,_ := DesDecrypt(cipherText,iv,key)
fmt.Printf("解密后:%s\n",string(decryText))
3Des只需要创建时使用des.NewTripleDESCipher(key)即可。
代码我放到了gitee上:https://gitee.com/frankyu365/gocrypto
您可以查看仓库Readme文档或Go-包管理(管理工具对比及go mod的使用)来进行安装
《现代密码学教程 谷利泽,杨义先等》
Go标准库-crypto/des
Go标准库-crypto/cipher
更多Go相关内容:Go-Golang学习总结笔记
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。如果您感觉有所收获,自愿打赏,可选择支付宝18833895206(小于),您的支持是我不断更新的动力。