IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> DES-CBC加密详解以及C实现 -> 正文阅读

[网络协议]DES-CBC加密详解以及C实现

?
DES-CBC加密详解以及C实现

前言

????本文章主要介绍加密算法DES-CBC的原理及C语言实现。DES概述部分是节选的《图解密码技术》一书中对DES算法的介绍。

1 DES概述

1.1 什么是DES

????DES(Data Encryption Standard)是1977年美国联邦信息处理标准(FIPS)中所采用的一种对称密码(FIPS 46-3)。DES 一直以来被美国以及其他国家的政府和银行等广泛使用。

1.2 加密和解密

????DES是一种将64 比特的明文加密成64 比特的密文的对称密码算法,它的密钥长度是56 比特。尽管从规格上来说,DES 的密钥长度是64 比特,但由于每隔7 比特会设置一个用于错误检查的比特,因此实质上其密钥长度是56 比特。
DES是以64 比特的明文(比特序列)为一个单位来进行加密的,这个64 比特的单位称为分组。一般来说,以分组为单位进行处理的密码算法称为分组密码(block cipher),DES 就是分组密码的一种。
????DES 每次只能加密64 比特的数据,如果要加密的明文比较长,就需要对 DES加密进行迭代(反复),而迭代的具体方式就称为模式(mode)
图1-1DES加密与解密
????????????????????????????????????????????图1-1DES加密与解密

1.3 DES结构(Feistel网络)

????DES的基本结构是由Horst Feistel设计的,因此也称为Feistel 网络(Feistel network)、Feistel 结构(Feistel structure)或者Feistel密码(Feistel cipher)。这一结构不仅被用于DES,在其他很多密码算法中也有应用。
????在Feistel 网络中,加密的各个步骤称为轮(round),整个加密过程就是进行若干次轮的循环。图3-3展现的是 Feistel网络中一轮的计算流程。DES是一种16轮循环的 Feistel 网络。
在这里插入图片描述
????????????????????????????????????????图1-2 Feistel网络中的一轮
下面我们参照图1-2来讲解一下 Feistel 网络的具体结构。
????上面的两个方框表示 Feistel 网络中一轮的输入(明文)。输入的数据被等分为左右两半分别进行处理,在图中,左半部分写作"左侧",右半部分写作"右侧"。
下面的两个方框表示本轮的输出(密文)。输出的左半部分写作"加密后的左侧",右半部分写作"右侧"。
??中间的"子密钥"指的是本轮加密所使用的密钥。在 Feistel 网络中,每一轮都需要使用一个不同的子密钥。由于子密钥只在一轮中使用,它只是一个局部密钥,因此才称为子密钥(subkey )
????轮函数的作用是根据"右侧"和子密钥生成对"左侧"进行加密的比特序列,它是密码系统的核心。将轮函数的输出与"左侧"进行XOR 运算,其结果就是"加密后的左侧"。也就是说,我们用XOR将轮函数的输出与"左侧"进行了合并。而输入的"右侧"则会直接成为输出的"右侧"。
总结一下,一轮的具体计算步骤如下∶
(1)将输入的数据等分为左右两部分。
(2)将输入的右侧直接发送到输出的右侧。
(3)将输入的右侧发送到轮函数。
(4)轮函数根据右侧数据和子密钥,计算出一串看上去是随机的比特序列。
(5)将上一步得到的比特序列与左侧数据进行XOR 运算,并将结果作为加密后 的左侧。
????但是,这样一来"右侧"根本就没有被加密,因此我们需要用不同的子密钥对一轮的处理重复若干次,并在每两轮处理之间将左侧和右侧的数据对调。
图 1-3展现了一个3轮的Feistel网络,3轮加密计算需要进行两次左右对调。对调只在两轮之间进行,最后一轮结束之后不需要对调。
????Feistel 网络这个名字的由来,也许就是因为其结构图看起来酷似一张网吧。
????那么,Feistel网络应该如何解密呢?例如,我们尝试一下将一轮加密的输出结果用相同的子密钥重新运行一次,这时Feistel 网络会怎么样呢?结果可能非常令人意外,无论轮函数的具体算法是什么,通过上述操作都能够将密文正确地还原为明文(图1-3)。关于这一点,大家可以从XOR的性质(两个相同的数进行XOR的结果一定为0)进行思考。
有多个轮的情况下也是一样的。也就是说,Feistel 网络的解密操作只要按照相反的顺序来使用子密钥就可以完成了,而 Feistel网络本身的结构,在加密和解密时都是完全相同的。(图1-5)
在这里插入图片描述
????????????????????????????????????????图1-3 Feistel网络的加密(3轮)
?
?
?

在这里插入图片描述
????????????????????????????????图1-4 用相同的子秘钥运行两次Feistel网络就能将数据还原

?
?
?

在这里插入图片描述
????????????????????????????????????????图1-5 Feistel网络的解密(3轮)
?
?
?
????下面我们来总结一下Feistel网络的性质。
????最容易发现的一点就是,Feistel网络的轮数可以任意增加。无论运行多少轮加密计算都不会发生无法解密的情况。
????其次,我们还可以发现,加密时无论使用任何函数作为轮函数都可以正确解密。也就是说,即便用轮函数的输出结果无法逆向计算出输入的值(即该函数不存在反函数)也没有问题。轮函数可以无需考虑解密的问题,可以被设计得任意复杂。
??Feistel网络实际上就是从加密算法中抽取出"密码的本质部分"并将其封装成一个轮函数。只要使用Feistel网络,就能够保证一定可以解密。因此,设计密码算法的人只要努力设计出足够复杂的就可以了。
????另外,加密和解密可以用完全相同的结构来实现,这也是 Feistel网络的一个特点。在Feistel 网络的一轮中,右半部分实际上没有进行任何处理,这在加密算法中看起来是一种浪费,但却保证了可解密性,因为完全没有进行任何处理的右半部分,是解密过程中所必需的信息。由于加密和解密可以用完全相同的结构来实现,因此用于实现 DES算法的硬件设备的设计也变得容易了。
综上所述,无论是任何轮数、任何轮函数,Feistel 网络都可以用相同的结构实现加密和解密,且加密的结果必定能够正确解密。
正是由于Feistel网络具备如此方便的特性,它才能够被许多分组密码算法使用。

2分组密码的模式

2.1分组密码和模式

?? ?? 分组密码block cipher)是每次只能处理特定长度的一块数据的一类密码算法,这里的"一块"就称为分组(block)。此外,一个分组的比特数就称为分组长度(block length )。
????例如,DES和三重 DES 的分组长度都是64 比特。这些密码算法一次只能加密64 比特的明文.并生成64比特的密文。
????AES的分组长度可以从128 比特、192比特和 256比特中进行选择。当选择128 比特的分组长度时,AES 一次可加密128比特的明文,并生成128 比特的密文。
????分组密码算法只能加密固定长度的分组,但是我们需要加密的明文长度可能会超过分组密码的分组长度,这时就需要对分组密码算法进行迭代,以便将一段很长的明文全部加密。而迭代的方法就称为分组密码的模式mode )。
这里主要对比下两种模式:
● ECB模式∶Electronic CodeBook mode(电子密码本模式)
● CBC模式∶Cipher Block Chaining mode(密码分组链接模式)

2.2 ECB模式

????ECB 模式的全称是 Electronic CodeBook 模式。在ECB模式中,将明文分组加密之后的结果将直接成为密文分组。
在这里插入图片描述
????????????????????????????????????????????图2-1 ECB模式

2.2 CBC模式

2.2.1 CBC概述

????CBC模式的全称是Cipher Block Chaining模式(密文分组链接模式),之所以叫这个名字,是因为密文分组是像链条一样相互连接在一起的。在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。
在这里插入图片描述
????????????????????????????????????????????图2-2 CBC模式

2.2.2 初始化向量

????当加密第一个明文分组时,由于不存在"前一个密文分组",因此需要事先准备一个长度为一个分组的比特序列来代替"前一个密文分组",这个比特序列称为初始化向量(initialization vector),通常缩写为IV。一般来说,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。

3 具体实现

3.1 DES算法步骤

????DES算法的步骤包括IP置换、密钥置换、扩展置换、S盒代替、P盒置换和末置换。
算法流程图:
在这里插入图片描述
????????????????????????????????????????图3-1 DES算法流程图

3.2 IP置换

????IP置换目的是将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位。
????IP置换表:

const uint8_t IPP[64] = {
	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,
	56, 48, 40, 32, 24, 16, 8, 0,  58, 50, 42, 34, 26, 18, 10, 2,
	60, 52, 44, 36, 28, 20, 12, 4,  62, 54, 46, 38, 30, 22, 14, 6
};

????IP置换表如上定义,表中的数字代表重新组合后的数据块中,在此位置的数据在原数据块中的位置
????即原数据块的第57位放到新数据的第0位,第49位放到第2位,……依此类推,第6位放到第63位。
????注:假设数据块存在数组dataBlock[8]中,那么dataBlock[0]的最高位为第0位,dataBlock[7]的最低位为第63位。

3.3 秘钥置换

秘钥置换的目的是得到56位的秘钥。
????不考虑每个字节的第8位,DES的密钥由64位减至56位,每个字节的第8位作为奇偶校验位;
产生56位秘钥的置换表如下:

const uint8_t Choose56[56] = {
	56, 48, 40, 32, 24, 16, 8,  0, 57, 49, 41, 33, 25, 17, 9, 1,
	58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38,
	30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36,
	28, 20, 12, 4, 27, 19, 11, 3
}; //Choosing the 56bit key from 64bit

????置换规则参照IP置换。

3.4 生成子秘钥

????子秘钥(subkey)为48位的秘钥,参与每次轮函数的计算。
????生成子秘钥步骤:
????1、秘钥循环移位。将56位秘钥分成两部分,每部分28位,根据轮数,将这两部分分别循环左移1 or 2位(加密左移,解密右移);
????2、压缩置换。通过置换表(压缩置换),置换出48bit子秘钥;
综上,可以看出,在秘钥置换阶段,一共发生了两次置换,第一次得到56位秘钥,第二次得到48位的子秘钥。
????分步讲解:
1、秘钥循环移位
移位表如下:

const uint8_t key_round[32] = {
	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
	0, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
}; //shift key left //shift key right

????表中前16位为,加密时,每轮左移的位数。后16位为,解密时,每轮右移的位数。
循环移位示例如下:
在这里插入图片描述
????????????????????????????????????????????图3-2 循环移位

2、压缩置换
????从56位中选出48位。这个过程中,既置换了每位的顺序,又选择了子密钥,因此称为压缩置换。压缩置换表如下:

const uint8_t E[48] = {
	13, 16, 10, 23, 0, 4, 2, 27,  14, 5, 20, 9, 22, 18, 11, 3,
	25, 7, 15, 6, 26, 19, 12, 1,  40, 51, 30, 36, 46, 54, 29, 39,
	50, 44, 32, 47, 43, 48, 38, 55,  33, 52, 45, 41, 49, 35, 28, 31
}; //Compression and permutation for key

3.5 扩展置换

????扩展置换是将IP置换后获得的32位的右半部分R0,扩展为48位。
扩展的目的是,生成与子秘钥长度相同的数据,来进行异或运算。
扩展置换表如下:

const uint8_t Choose48[48] = {
	31, 0, 1, 2, 3, 4, 3, 4,   5, 6, 7, 8, 7, 8, 9, 10,
	11, 12, 11, 12, 13, 14, 15, 16,  15, 16, 17, 18, 19, 20, 19, 20,
	21, 22, 23, 24, 23, 24, 25, 26,  27, 28, 27, 28, 29, 30, 31, 0
}; //expands the right half text of 32 bits to 48 bits

3.6 S盒替代

????把子密钥扩展置换得到的48位右半部分R0进行异或运算,运算的结果作为S盒的输入。
S盒一共8个,把48位数据分为8组,每组6位。一个分组对应一个盒,对对应的S盒做替代操作。
在这里插入图片描述
????????????????????????????????????????????图3-3 S-Box替代图
????一个S盒就是一个4行14列的表,盒中的每一项为一个4bit的数。输入的6位数,取最低和最高bit组合成的数作为行H,中间4bit组合成的数作为列L。在S盒中找到H行L列的数,即为次S盒输出的4bit数据。如输入:000110,则取最高位和最低位为00,中间4位为0011,即0行3列。
????S盒(S-BOX)定义如下:

const uint8_t S[8][64] = { {
		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
	},
	{
		15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
		3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
		0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
		13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
	},
	{
		10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
		13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
		13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
		1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
	},
	{
		7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
		13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
		10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
		3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
	},
	{
		2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
		14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
		4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
		11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
	},
	{
		12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
		10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
		9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
		4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
	},
	{
		4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
		13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
		1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
		6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
	},
	{
		13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
		1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
		7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
		2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
	}
};

3.7 P盒置换

????P盒置换的输入为S盒替换的输出。
????P盒置换的结果,与最初64位分组数据的左半部分L0异或,然后交换左右部分,接着开始下一轮的计算。
????p盒定义如下:

const uint8_t PP[32] = {
	15, 6, 19, 20, 28, 11, 27, 16,  0 , 14, 22, 25, 4, 17, 30, 9,
	1, 7, 23, 13, 31, 26, 2, 8,  18, 12, 29, 5, 21, 10, 3, 24
}; //P-box permutation

????综上,DES算法一共要进行16轮的计算,参与每轮计算的步骤有:

????1、生成子秘钥
????2、扩展置换
????3、S盒替代
????4、P盒置换
????5、P盒置换的结果,与最初64位分组数据的左半部分L0异或,然后交换左右部分,接着开始下一轮的计算

3.8 IP-1末置换

????IP-1末置换是初始IP置换的逆过程,DES最后一轮后,左、右两半部分并未进行交换,而是两部分合并形成一个分组做为末置换的输入。
末置换定义如下:

const uint8_t IPN[64] = {
	39, 7, 47, 15, 55, 23, 63, 31,  38, 6, 46, 14, 54, 22, 62, 30,
	37, 5, 45, 13, 53, 21, 61, 29,  36, 4, 44, 12, 52, 20, 60, 28,
	35, 3, 43, 11, 51, 19, 59, 27,  34, 2, 42, 10, 50, 18, 58, 26,
	33, 1, 41, 9, 49, 17, 57, 25,  32, 0, 40, 8, 48, 16, 56, 24
}; //Inverse permutation

????末置换得到的结果即为,一次DES计算得到的密文输出(64bit)。

4 源代码

头文件,包含des加密和解密函数的声明。


#pragma once
#ifndef __DES_H__
#define __DES_H__

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <stdint.h>

#define DES_BLOCK_LEN 		8       // 8bytes

#define DES_KEY_LEN_8		8		// key-length: 8bytes  -> single DES

typedef enum _des_mode_e {
	DES_MODE_ECB,
	DES_MODE_CBC
} des_mode_e;

int32_t crypto_des_encrypt(const uint8_t *data, uint32_t data_len, uint8_t *out, const uint8_t *iv,
	const uint8_t *key, uint32_t key_len, des_mode_e mode);
int32_t crypto_des_decrypt(const uint8_t *data, uint32_t data_len, uint8_t *out, const uint8_t *iv,
		const uint8_t *key, uint32_t key_len, des_mode_e mode);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /*__DES_H__*/

源文件,包含des加密和解密函数的实现。仅支持CBC模式。

// des_cbc.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<stdlib.h>
#include <stdio.h>
#include <string.h>

#include "des_cbc.h"

#define DES_TRUE                        0x01
#define DES_FALSE                       0x00
#define DES_DO_INITIAL_PERMUTATION      0x10
#define DES_ENCRYPTION_MODE             0x20
#define DES_DECRYPTION_MODE             0
#define DES_DO_FINAL_PERMUTATION        0x40
#define DES_ENCRYPT_BLOCK               (DES_DO_INITIAL_PERMUTATION|DES_ENCRYPTION_MODE|DES_DO_FINAL_PERMUTATION)
#define DES_DECRYPT_BLOCK               (DES_DO_INITIAL_PERMUTATION|DES_DECRYPTION_MODE|DES_DO_FINAL_PERMUTATION)

const uint8_t IPP[64] = {
	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,
	56, 48, 40, 32, 24, 16, 8, 0,  58, 50, 42, 34, 26, 18, 10, 2,
	60, 52, 44, 36, 28, 20, 12, 4,  62, 54, 46, 38, 30, 22, 14, 6
};

const uint8_t IPN[64] = {
	39, 7, 47, 15, 55, 23, 63, 31,  38, 6, 46, 14, 54, 22, 62, 30,
	37, 5, 45, 13, 53, 21, 61, 29,  36, 4, 44, 12, 52, 20, 60, 28,
	35, 3, 43, 11, 51, 19, 59, 27,  34, 2, 42, 10, 50, 18, 58, 26,
	33, 1, 41, 9, 49, 17, 57, 25,  32, 0, 40, 8, 48, 16, 56, 24
}; //Inverse permutation

const uint8_t Choose56[56] = {
	56, 48, 40, 32, 24, 16, 8,  0, 57, 49, 41, 33, 25, 17, 9, 1,
	58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38,
	30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36,
	28, 20, 12, 4, 27, 19, 11, 3
}; //Choosing the 56bit key from 64bit

const uint8_t key_round[32] = {
	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
	0, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
}; //shift key left //shift key right

const uint8_t Choose48[48] = {
	31, 0, 1, 2, 3, 4, 3, 4,   5, 6, 7, 8, 7, 8, 9, 10,
	11, 12, 11, 12, 13, 14, 15, 16,  15, 16, 17, 18, 19, 20, 19, 20,
	21, 22, 23, 24, 23, 24, 25, 26,  27, 28, 27, 28, 29, 30, 31, 0
}; //expands the right half text of 32 bits to 48 bits

const uint8_t E[48] = {
	13, 16, 10, 23, 0, 4, 2, 27,  14, 5, 20, 9, 22, 18, 11, 3,
	25, 7, 15, 6, 26, 19, 12, 1,  40, 51, 30, 36, 46, 54, 29, 39,
	50, 44, 32, 47, 43, 48, 38, 55,  33, 52, 45, 41, 49, 35, 28, 31
}; //Compression and permutation for key

const uint8_t PP[32] = {
	15, 6, 19, 20, 28, 11, 27, 16,  0 , 14, 22, 25, 4, 17, 30, 9,
	1, 7, 23, 13, 31, 26, 2, 8,  18, 12, 29, 5, 21, 10, 3, 24
}; //P-box permutation

const uint8_t S[8][64] = { {
		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
	},
	{
		15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
		3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
		0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
		13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
	},
	{
		10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
		13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
		13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
		1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
	},
	{
		7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
		13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
		10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
		3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
	},
	{
		2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
		14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
		4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
		11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
	},
	{
		12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
		10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
		9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
		4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
	},
	{
		4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
		13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
		1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
		6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
	},
	{
		13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
		1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
		7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
		2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
	}
};

const uint8_t _bitposition[8] = {
	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};

static void check_table(uint8_t line, uint8_t *text, uint8_t *lasttext, const uint8_t *IDD)
{
	uint8_t i, j, k, temp, temp2;

	for (j = 0, k = 0; j < line; j++, k += 8) {
		lasttext[j] = 0;
		for (i = 0; i < 8; i++) {
			temp2 = IDD[k + i]; 
			temp = text[temp2 / 8]; // get the byte
			temp &= _bitposition[temp2 & 0x7]; // get the bit
			if (temp) {
				lasttext[j] |= _bitposition[i];
			}
		}
	}
}

static void single_des_operation(uint8_t *plaintext, uint8_t *key, uint8_t mode)
{
	static uint8_t prevtext[8];
	uint8_t prevkey[8], Ltext[4], Rtext[4];
	char temp, temp1;
	int32_t i = 0;
	int32_t Round = 0;
	uint8_t j = 0;
	
	if (mode & DES_DO_INITIAL_PERMUTATION) {
		check_table(8, plaintext, prevtext, IPP); //Initial permutation
	}
	//将置换后的数据,分为“左侧”和“右侧”
	for (i = 0; i < 4; i++) {
		Ltext[i] = prevtext[i];
		Rtext[i] = prevtext[i + 4];
	}

	/*
	产生子秘钥过程:
	1、通过置换产生56位秘钥
	2、将56位秘钥分成两部分,每部分28位
	3、根据轮数,将这两部分分别循环左移1 or 2位
	4、通过置换表(压缩置换),置换出48bit子秘钥
	*/
	check_table(7, key, prevkey, Choose56);

	for (Round = 0; Round < 16; Round++) {
		//rotate both 28bits block to left(encrypt) or right(decrypt)
		if (mode & DES_ENCRYPTION_MODE) { // encrypt

			for (j = 0; j < key_round[Round]; j++) {
				temp = prevkey[3] & 0x08;
				for (i = 7; i > 0; i--) {
					temp1 = prevkey[i - 1] & 0x80;
					prevkey[i - 1] <<= 1;
					if (temp) {
						prevkey[i - 1] |= 0x01;
					}
					temp = temp1;
				}

				if (temp) {
					prevkey[3] |= 0x10;
				}
				else {
					prevkey[3] &= 0xEF;
				}
			}
		}
		else { // decrypt			   
			for (j = 0; j < key_round[Round + 16]; j++) {
				temp = prevkey[3] & 0x10;
				for (i = 0; i < 7; i++) {
					temp1 = prevkey[i] & 0x01;
					prevkey[i] >>= 1;
					if (temp) {
						prevkey[i] |= 0x80;
					}
					temp = temp1;
				}

				if (temp) {
					prevkey[3] |= 0x08;
				}
				else {
					prevkey[3] &= 0xF7;
				}
			}			
		}

		check_table(6, prevkey, plaintext, E); //Compression permutation
		check_table(6, Rtext, prevtext, Choose48); //Expansion permutation
												   //Expanded right half xor_operation with the subkey
		prevtext[0] ^= plaintext[0];
		prevtext[1] ^= plaintext[1];
		prevtext[2] ^= plaintext[2];
		prevtext[3] ^= plaintext[3];
		prevtext[4] ^= plaintext[4];
		prevtext[5] ^= plaintext[5];

	


		for (j = 6, i = 8; j > 0; j -= 3, i -= 4) { //S-Box Substitution
			plaintext[i - 1] = prevtext[j - 1];
			plaintext[i - 2] = ((prevtext[j - 1] >> 6) & 0x03) | (prevtext[j - 2] << 2);
			plaintext[i - 3] = ((prevtext[j - 2] >> 4) & 0x0f) | (prevtext[j - 3] << 4);
			plaintext[i - 4] = prevtext[j - 3] >> 2;
		}

		for (i = 0; i < 8; i++) { //Get the S-Box location
			temp = plaintext[i] & 0x21;
			if (temp & 0x01) {
				temp = (temp & 0x20) | 0x10;
			}
			plaintext[i] = temp | ((plaintext[i] >> 1) & 0x0F);
		}

		//Get S-Box output
		plaintext[0] = S[0][plaintext[0]];
		plaintext[1] = S[1][plaintext[1]];
		plaintext[2] = S[2][plaintext[2]];
		plaintext[3] = S[3][plaintext[3]];
		plaintext[4] = S[4][plaintext[4]];
		plaintext[5] = S[5][plaintext[5]];
		plaintext[6] = S[6][plaintext[6]];
		plaintext[7] = S[7][plaintext[7]];



		//Combine 4-bit block to form 32-bit block
		plaintext[0] = (plaintext[0] << 4) | plaintext[1];
		plaintext[1] = (plaintext[2] << 4) | plaintext[3];
		plaintext[2] = (plaintext[4] << 4) | plaintext[5];
		plaintext[3] = (plaintext[6] << 4) | plaintext[7];

		check_table(4, plaintext, prevtext, PP);

		for (i = 0; i < 4; i++) {
			prevtext[i] ^= Ltext[i];
			Ltext[i] = Rtext[i];
			Rtext[i] = prevtext[i];
		}
	}

	for (i = 0; i < 4; i++) {
		prevtext[i] = Rtext[i];
		prevtext[i + 4] = Ltext[i];
	}

	if (mode & DES_DO_FINAL_PERMUTATION) {
		check_table(8, prevtext, plaintext, IPN); //Final permutation
	}
}

static int32_t xor_operation(uint8_t *out, 
							const uint8_t *data1, 
							const uint8_t *data2, 
							uint32_t dwLen)
{
	int32_t ret = DES_TRUE;
	uint32_t i = 0;

	if ((dwLen != 0) && ((data1 == NULL) || (data2 == NULL) || (out == NULL))) {
		ret = DES_FALSE;
	}
	else {
		for (i = 0; i < dwLen; i++) {
			out[i] = data1[i] ^ data2[i];
		}
	}
	return ret;
}

int32_t crypto_des_encrypt(const uint8_t *data, 
						   uint32_t data_len, 
						   uint8_t *out, 
						   const uint8_t *iv,
						   const uint8_t *key, 
						   uint32_t key_len, 
						   des_mode_e mode)
{
	uint32_t i = 0;
	uint32_t j = 0;
	uint32_t k = 0;

	if ((data_len % DES_BLOCK_LEN != 0) || \
		(key_len != DES_KEY_LEN_8) ) {
		return -1;
	}

	for (i = 0; i < data_len / DES_BLOCK_LEN; i++) {
		if (mode == DES_MODE_CBC) {
			if (i > 0) {
				xor_operation(&out[i * DES_BLOCK_LEN], &data[i * DES_BLOCK_LEN], \
					&out[(i - 1) * DES_BLOCK_LEN], DES_BLOCK_LEN);
			}
			else {
				xor_operation(&out[i], &data[i], iv, DES_BLOCK_LEN);
			}
		}

		if (key_len == DES_KEY_LEN_8) { //Single DES
			single_des_operation(&out[i * DES_BLOCK_LEN], (uint8_t *)&key[0], DES_ENCRYPT_BLOCK);
		}


	}

	return 0;
}

int32_t crypto_des_decrypt(const uint8_t *data, 
						   uint32_t data_len, 
						   uint8_t *out, 
						   const uint8_t *iv,
						   const uint8_t *key, 
						   uint32_t key_len, 
						   des_mode_e mode)
{
	int32_t i = 0;

	if ((data_len % DES_BLOCK_LEN != 0) || \
		(key_len != DES_KEY_LEN_8)) {
		return -1;
	}

	for (i = data_len / DES_BLOCK_LEN - 1; i >= 0; i--) {
		memcpy(&out[i * DES_BLOCK_LEN], &data[i * DES_BLOCK_LEN], DES_BLOCK_LEN);
		if (key_len == DES_KEY_LEN_8) { //Single DES            
			single_des_operation(&out[i * DES_BLOCK_LEN], (uint8_t *)&key[0], DES_DECRYPT_BLOCK);
		}

		/*
		解密模式下,是把密文按照des的逆运算后,把得到的输出,与相邻的,编号较小的一个block进行异或。
		*/
		if (mode == DES_MODE_CBC) {
			if (i > 0) {   
				xor_operation(&out[i * DES_BLOCK_LEN], &out[i * DES_BLOCK_LEN], \
					&data[(i - 1) * DES_BLOCK_LEN], DES_BLOCK_LEN);
			}
			else {
				xor_operation(&out[i], &out[i], iv, DES_BLOCK_LEN);
			}
		}
	}

	return 0;
}

测试用例:

#include "stdafx.h"
#include<stdlib.h>
#include <stdio.h>
#include <string.h>

#include "des_cbc.h"



typedef struct _des_test_data_t {
	const char *data;
	const char *key;
	const char *iv;
	_des_mode_e         mode;
} des_test_data_t;

typedef struct _byte_t {
	uint8_t low : 4;
	uint8_t high : 4;
} byte_t;

static des_test_data_t g_des_test_data =
{
	"12623132336261aa6162aa32f1626100",
	"6777696e30383031",
	"6777696e30383031",
	DES_MODE_CBC
};

#define UTILS_ERR   -1
#define UTILS_OK    0

int32_t utils_hex_string_2_bytes(const char *hextring, uint8_t *bytes, uint16_t *length)
{
	const char *p = hextring;
	byte_t tmp;
	uint16_t cnt = 0;

	if ((hextring == NULL) || (bytes == NULL) || (length == NULL)) {
		return UTILS_ERR;
	}

	*length = 0;
	if ((strlen(p) % 2) != 0) {
		return UTILS_ERR;
	}

	while (*p != '\0') {
		if ((*p >= '0') && (*p <= '9')) {
			tmp.high = *p - '0';
		}
		else if ((*p >= 'A') && (*p <= 'F')) {
			tmp.high = *p - 'A' + 10;
		}
		else if ((*p >= 'a') && (*p <= 'f')) {
			tmp.high = *p - 'a' + 10;
		}
		else {
			return UTILS_ERR;
		}

		p++;

		if ((*p >= '0') && (*p <= '9')) {
			tmp.low = *p - '0';
		}
		else if ((*p >= 'A') && (*p <= 'F')) {
			tmp.low = *p - 'A' + 10;
		}
		else if ((*p >= 'a') && (*p <= 'f')) {
			tmp.low = *p - 'a' + 10;
		}
		else {
			return UTILS_ERR;
		}

		p++;
		bytes[cnt++] = *(uint8_t *)& tmp;
	}

	*length = cnt;

	return UTILS_OK;
}

static int des_test(des_test_data_t *data)
{
	uint8_t data_hex[128];
	uint16_t data_len;
	uint8_t key[24];
	uint16_t key_len;
	uint8_t iv[24];
	uint16_t iv_len;
	uint8_t data_calc[128];
	uint8_t data_calc2[128];
	int32_t ret;
	int32_t i;

	utils_hex_string_2_bytes(data->key, key, &key_len);
	utils_hex_string_2_bytes(data->iv, iv, &iv_len);
	utils_hex_string_2_bytes(data->data, data_hex, &data_len);
	if ((data_len % 8) != 0)
	{
		uint8_t fill_zero = (8 - (data_len % 8));
		for (uint8_t i = data_len; i < data_len + fill_zero; i++)
			data_hex[i] = 0;
		data_len += fill_zero;
	}
	ret = crypto_des_encrypt(data_hex, data_len, data_calc, iv, key, key_len, data->mode);

	printf("data_enc:");
	for (i = 0; i < data_len; i++) {
		printf("%02x ", data_calc[i]);
	}
	printf("\n");

	ret = crypto_des_decrypt(data_calc, data_len, data_calc2, iv, key, key_len, data->mode);
	printf("data_dec:");
	for (i = 0; i < data_len; i++) {
		printf("%02x ", data_calc2[i]);
	}
	printf("\n");
	return ret;
}

int main(int argc, const char *argv[])
{
	int ret;
	
	ret = des_test(&g_des_test_data);

	system("pause");
	return 0;
}

输出结果演示,在Visual Studio 2015上编译运行结果:

参考文章:
DES算法详解
【安全算法之DES】DES算法(支持ECB/CBC模式)的C语言源码实现

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:27:36  更:2022-05-08 08:27:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 9:42:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码