分组密码实验
写在前言:这个实验使用C语言实现了DES算法,基于DES算法实现了3DES算法,并在此基础上,实现了五种分组密码工作模式:ECB、CBC、CFB、OFB、CTR,运行的环境是Linux,这个代码在Windows系统跑会出现Bug,因为Windows的long类型是4个字节,而Linux的long是8字节,编译的时候会有一些Warnings。
实验说明
分组加密实验要求我们使用3DES加解密文件,理论上任何文件都可以加解密,因为文件的本质就是二进制序列,DES是基于Feistel密码结构体系的一个具体实现方案,它是一个很经典的加密算法,任何加密算法都是公开的,DES也不例外,这里有一个DES算法的一个具体例子,可以帮助理解DES的操作:🔗DES Example。
Feistel密码结构
如下图所示,左半部分为Feistel为加密,右半部分为解密,实际上,Feistel密码结构有16轮的迭代过程,每一轮迭代具有相同的操作:
- 先把数据分为大小相同的左右两部分L、R;
- 把右半部分直接复制到下一轮的左半部分;
- 右半部分R进入一个轮函数F,这个函数还有一个参数,就是轮密钥K;
- 第三步输出的结果与左半部分L进行异或操作就得到了下一轮迭代的右半部分。
- 16轮迭代结束后交换左右两部分,得到最终结果。
加密的迭代过程的伪代码如下:
# Encrypt
for i=1 to 16
LE[i]=RE[i-1]
RE[i]=LE[i-1] xor F(RE[i-1], K[i])
LE[17]=RE[16]
RE[17]=LE[16]
# Decrypt
LD[0]=RE[16]
RD[0]=LE[16]
for i=1 to 16
LD[i]=RD[i-1]
RD[i]=LD[i-1] xor F(RD[i-1], K[16-i+1])
加密过程和解密过程是一一对应的,加密的最后左右互换,解密的开始左右互换,加密的过程的密钥使用顺序与解密时相反,值得注意的是,Feistel密码结构中的F函数,不要求是可逆函数,因为加解密过程中,没有用到其逆函数。
Feistel密码结构式许多密码算法采用的基础结构,其密码设计要素如下:
- 分组大小:分组越大,安全性越高,加解密速率越慢。
- 密钥大小:密钥越长,安全性越高,加解密速率也许会减慢。
- 迭代次数:多轮迭代次数能提供更高的安全性。
- 子密钥产生算法:算法复杂度越高,密码破译难度越高。
- 轮函数:越高的复杂度意味着对破译阻力越大。
- 快速软件加密/解密:容易嵌入到现有的应用程序或者工具中。
- 容易分析:容易分析该算法的弱点并给出强度更高的保障。
DES算法
DES(Data Encryption Standard),数据加密标准,是最广泛使用的对称加密方案,明文分组长度为64 bit,密钥有效长度为56 bit,在基于Feistel体系结构基础上,采用16轮迭代,由原始56 bit密钥产生16组子密钥,每一轮迭代使用一个子密钥。
下面介绍DES的三个操作:
- 初始(逆初始)置换
- 轮函数F的操作
- 子密钥产生算法
下图是DES算法框架
初始(逆初始)置换
初始(逆初始)置换:置换操作,就是通过一张置换表将原始的数据进行换位操作。而DES的加解密过程开始都要经过初始置换,最终进行逆初始置换。若对同一个64-bit依次进行置换、逆置换那么得到的结果与原始数据一致。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xIygjd0l-1650028820073)(.assets/image-20220415195602549.png)]
轮函数F
下图是轮函数F的框架结构,轮函数接受一个32 bit的数据以及一个48 bit的子密钥,操作有4个:
- 扩展置换
- 轮密钥加
- 压缩置换
- 直接置换
扩展置换
由于子密钥是48 bit,轮密钥加操作需要两个数据的长度一致,才可以进行,因此,这一步使用一个扩展置换表将一个32 bit的数据扩展成48 bit的数据,下图为扩展置换的具体操作,实际上,它将32 bit的数据分成8块,每块4 bit,在每一块之间插入两个bit,是很有规律的,某一块扩展之后,它的第一个bit一定是前一块的最后一个bit,它的最后一个bit一定是后一块的第一个bit。
轮密钥加
轮密钥加很简单,就是将扩展置换得到的48 bit数据直接和轮密钥进行简单的异或操作。
压缩置换
压缩置换是DES所有操作中唯一一个非线性的操作,并且它是不可逆的,压缩置换将48 bit的数据最终压缩成32 bit的数据,具体的操作就是将这48 bit的数据分成8个块,每一个块6 bit,然后通过这6 bit的数据,计算出一个坐标值,从对应的S盒压缩置换表中获取相应的4 bit数据,每一块都进行相同的操作,最终就可以得到32 bit的压缩值。
下面给出一个压缩置换的一个例子:
上图是一个压缩置换表,4行16列,表中的值的范围是[0, 15]共16种值,每一种值出现的概率相同,即在表中每一种值出现4次,不同的压缩置换表具有相同的属性,只不过位置不一样。
将6 bit数据以二进制形式表为 b 1 b 2 b 3 b 4 b 5 b 6 b_1b_2b_3b_4b_5b_6 b1b2b3b4b5b6,那么这个数据对应置换表中的坐标值为 ( b 1 b 6 , b 2 b 3 b 4 b 5 ) (b_1b_6,b_2b_3b_4b_5) (b1b6,b2b3b4b5),比如将6 bit二进制值 011001 011001 011001通过上面的压缩置换表进行压缩,首先计算其坐标值为: ( 1 , 12 ) (1, 12) (1,12),对应压缩值为0,因此压缩结果为: 0000 0000 0000。
直接置换
直接置换很简单,就是通过一个32 bit的直接置换表,最终置换结果也为32 bit
子密钥生成算法
下图所示,为DES子密钥生成算法的图解,
首先64 bit的密钥经过一个置换选择PC1,变为56 bit,实际上,这个置换去掉了第8、16、24、32、40、48、56、64比特位,这就是为什么DES算法的有效密钥长度才为56 bit。
之后每一轮的处理根据一个子密钥循环移位表,进行循环移位,注意每一轮循环移位的对象不是整个56 bit数据,而是分别对左右28 bit数据的进行循环左移,然后将循环左移的结果合并得到一个56 bit数据,再通过一个置换选择PC2得到56 bit的子密钥,迭代16次就得到了DES的16轮迭代子密钥。
3DES算法
3DES,也叫三重DES,相比DES,它的安全性更强,因为它的密钥空间扩大了很多,从DES的 2 56 2^{56} 256,扩展到了 2 168 2^{168} 2168,使得穷举搜索变得不切实际,3DES的出现是因为计算机的计算速度越来越快,导致DES无法承受穷举搜索攻击,此时3DES就被提出来了,实际上,任何对称加密算法都可以进行多重的加解密操作,使得它更安全,但是这样做会使得加解密的效率降低,3DES的加解密做了3次,才完成64bit的加解密操作,相同的时间DES可以加解密192 bit的数据。
3DES算法有四种模式:
- EDE3
- 加密过程: C = E K 3 ( D K 2 ( E K 1 ( M ) ) ) C=E_{K_3}(D_{K_2}(E_{K_1}(M))) C=EK3(DK2(EK1(M)))
- 解密过程: M = D K 1 ( E K 2 ( D K 3 ( C ) ) ) M=D_{K_1}(E_{K_2}(D_{K_3}(C))) M=DK1(EK2(DK3(C)))
- EEE3
- 加密过程: C = E K 3 ( E K 2 ( E K 1 ( M ) ) ) C=E_{K_3}(E_{K_2}(E_{K_1}(M))) C=EK3(EK2(EK1(M)))
- 解密过程: M = D K 1 ( D K 2 ( D K 3 ( C ) ) ) M=D_{K_1}(D_{K_2}(D_{K_3}(C))) M=DK1(DK2(DK3(C)))
- EDE2
- 加密过程: C = E K 1 ( D K 2 ( E K 1 ( M ) ) ) C=E_{K_1}(D_{K_2}(E_{K_1}(M))) C=EK1(DK2(EK1(M)))
- 解密过程: M = D K 1 ( E K 2 ( D K 1 ( C ) ) ) M=D_{K_1}(E_{K_2}(D_{K_1}(C))) M=DK1(EK2(DK1(C)))
- EEE2
- 加密过程: C = E K 1 ( E K 2 ( E K 1 ( M ) ) ) C=E_{K_1}(E_{K_2}(E_{K_1}(M))) C=EK1(EK2(EK1(M)))
- 解密过程: M = D K 1 ( D K 2 ( D K 1 ( C ) ) ) M=D_{K_1}(D_{K_2}(D_{K_1}(C))) M=DK1(DK2(DK1(C)))
在实际的应用中,EDE2模式应用最广泛,因为它兼容DES,当 K 1 = K 2 K_1=K_2 K1=K2时,EDE2模式退化成DES。
分组密码工作模式
分组密码工作模式共有五种:
- 电子密码本模式(ECB)
- 密码分组链接模式(CBC)
- 密码反馈模式(CFB)
- 输出反馈模式(OFB)
- 计数器模式(CTR)
能被任意分组密码使用。
电子密码本模式
消息被独立分成分组进行加密,每一个分组是一个值,将会被替换,就像一个密码本一样,因此叫做电子密码本模式,每一个分组都是独立其它分组进行编码。
64-bit明文分组重复是在密文中也会重复出现,特别是处理高度结构化的消息,很可能被破译;主要是因为这些加密的消息是独立处理而造成的,在发送少量消息分组时可用。
密码分组链接模式
消息被分成多个分组,在加密操作时每一个明文分组与前面密文分组相链接,使得同一明文分组将会产生不同的密文分组。
密码反馈模式
密码反馈模式能将任意分组密码转化成流密码,从而不需要对消息进行填充为分组的整数倍,还可以实时操作,充分利用传输信道。
输出反馈模式
输出反馈模式类似于密码反馈模式,主要区别在于反馈的内容识加密器输出的随机数,而不是密文,因此不具有错误传播的特性。
计数器模式
计数器模式在ATM(异步传输模式)网络安全和IPSEC早有应用。类似于密码反馈模式,但不是加密反馈值,而是加密每个计数值,然后再对铭文分组进行异或操作,每个明文分组必须有不同的密钥和计数值,而且从不重复使用。
DES算法实现
由于DES的算法实现使用的是64 bit的long类型,在对文件进行加解密时,往往只能读取和写入字节流,所以需要编写64 bitlong和字节数组之间的转换函数:
util.h
:
#ifndef __UTIL_H__
#define __UTIL_H__
/* Convert string to long (8 bytes) */
long charstol(char* s);
/* Convert long to string (9 bytes) */
char* ltochars(long l);
/* Convert long to 8 bytes */
char* ltobytes(long l);
#endif
util.c
:实际文件的加解密只用到了charstol
、ltobytes
函数,其中ltochars
函数是用来debug用的,它可以得到为一个长度为8的字符串。
#include "util.h"
#include <stdlib.h>
#include <stdio.h>
/* Only use in Extract key, plaintext and ciphertext */
long charstol(char* s)
{
int i;
int len = 8;
long d;
long res = 0;
for (i = 0; i < len; i++) {
d = (long)s[i] & 0xff;
res |= (d << (i * 8));
}
return res;
}
/* long to chars(outputstring with \0) */
char* ltochars(long l)
{
int i;
int len = 8;
char* s = (char*)malloc((len + 1) * sizeof(char));
for (i = 0; i < len; i++) {
s[i] = (l >> (8 * i)) & 0xff;
}
s[len] = '\0';
return s;
}
char* ltobytes(long l)
{
int i;
int len = 8;
char* s = (char*)malloc(len * sizeof(char));
for (i = 0; i < len; i++) {
s[i] = (l >> (8 * i)) & 0xff;
}
return s;
}
接着,定义一些常量表、宏定义以及DES的函数:
#ifndef __DES_H__
#define __DES_H__
/**
* @brief Des.h, defines Des tables
* and some basic functions.
*
*/
/* 1<= pos <= 64*/
#define SET(val, pos) (val |= ((long)1 << (pos - 1)))
#define GET(val, pos) ((val >> (pos - 1)) & 0x1)
/* Des Block Size- 64 bits */
#define BLOCK_SIZE 64
#define HALF_BLOCK_SIZE BLOCK_SIZE/2
/* Des Cipher key size- 56 bits*/
#define CIPHER_KEY_SIZE 56
#define ROUND_KEY_SIZE 48
#define S_BOX_SIZE 8
/* Des Iteration times */
#define ITERATION_TIMES 16
/* Initial Permutation Table */
static int IP[BLOCK_SIZE] = {
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
};
/* Inverse Initial Permutation Table */
static int INV_IP[BLOCK_SIZE] = {
40, 8, 48, 16, 56, 24, 64, 32,
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
};
/* Expansive Permutation Table- 32 bits -> 48 bits*/
static int EP[ROUND_KEY_SIZE] = {
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
};
/* Straight Permutation- 48 bits -> 32 bits */
static int P[HALF_BLOCK_SIZE] = {
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
};
/* Des S box- each box: 6 bits -> 4 bits */
static int S[S_BOX_SIZE][BLOCK_SIZE] = {
/* Box-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},
/* Box-2 */
{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},
/* Box-3 */
{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},
/* Box-4 */
{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},
/* Box-5 */
{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 19, 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},
/* Box-6 */
{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},
/* Box-7 */
{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},
/* Box-8 */
{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}
};
/* Permutation Choice- 1*/
static int PC_1[CIPHER_KEY_SIZE] = {
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,
};
/* Permutation Choice- 2*/
static int PC_2[ROUND_KEY_SIZE] = {
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
};
/* Rotate Left size table (bits) */
static int RL[ITERATION_TIMES] = {
1, 1, 2, 2, 2, 2, 2, 2,
1, 2, 2, 2, 2, 2, 2, 1
};
/* Generate 16 sub keys by key. */
long* generateKey(long key);
/* Encrypt 64 bits plaintext. */
long encryptdes(long plaintext, long* keys);
/* Decrypt 64 bits ciphertext. */
long decryptdes(long ciphertext, long* keys);
/*
* Round function- 32bits->48bits->32bits
* expansive permuatation(EP Box) ->
* round key add ->
* substitue(S Box) ->
* permutate(P Box)
*/
static long f(long r, long subkey);
#endif
下面是des.c
:轮函数f、子密钥生成函数generateKey、加解密函数encryptdes、decryptdes。
#include "des.h"
#include <stdlib.h>
#include <stdio.h>
/* Extract a number's left or right part, Only Used in Round function */
#define GET_RIGHT32(val) ((val >> 32) & 0xffffffff)
#define GET_LEFT32(val) (val & 0xffffffff)
/* Only use in generateKey. , keys*/
#define ROTATE(val, k) (val = ((1 << 28) - 1) & ((val << k) | (((val << k) & ((1 << k) - 1) << 28) >> 28)))
#define COMBINE(val, a, b, size) (val = ((a) | ((long)b << size)))
#define GET_RIGHT28(val) ((val >> 28) & 0xfffffff)
#define GET_LEFT28(val) (val & 0xfffffff)
/**
* @brief Generate 16 keys by key.
*
* @param key cipher key
* @return long* sub keys array
*/
long* generateKey(long key)
{
int i, j;
int pos;
long pkey = 0;
long subkey;
int l, r;
long* keys = (long*)malloc(ITERATION_TIMES * sizeof(long));
/* PC_1 - Check */
for (i = 0; i < CIPHER_KEY_SIZE; i++) {
if (GET(key, PC_1[i])) {
SET(pkey, (i+1));
}
}
/* Extract left and right 28 bits of pkey.- Check */
l = GET_LEFT28(pkey);
r = GET_RIGHT28(pkey);
/* Generate 16 sub keys */
for (i = 0; i < ITERATION_TIMES; i++) {
/* Rotate left- Check */
ROTATE(l, RL[i]);
ROTATE(r, RL[i]);
/* Combine into 56 bits sub key */
COMBINE(pkey, l, r, 28);
subkey = 0;
/* PC_2- Check */
for (j = 0; j < ROUND_KEY_SIZE; j++) {
if (GET(pkey, PC_2[j])) {
SET(subkey, (j+1));
}
}
keys[i] = subkey;
}
return keys;
}
/**
* @brief Des Round function
*
* @param r the right 28 bits
* @param subkey Des 48 bits sub key
* @param iter Iteration time, to determine use which S Box.
* @return int f result
*/
long f(long r, long subkey) {
int i;
long tmp;
long res;
/* Use in Substitue */
int num;
int row, col;
tmp = 0;
/* Expansive Permutation- Check */
for (i = 0; i < ROUND_KEY_SIZE; i++) {
if (GET(r, EP[i])) {
SET(tmp, (i+1));
}
}
/* Round key add- Check */
res = tmp ^ subkey;
tmp = 0;
/* Substitute-Check */
for (i = 0; i < 8; i++) {
/* Extract 6 bits- Check */
num = (res >> (i * 6)) & 0x3f;
/* Get row and col in S Box- Check */
col = (num >> 1) & 0xf;
row = ((num >> 4) & 0x2) | (num & 0x1);
/* Combine each 4 bits- Check */
tmp |= (S[i][row*16+col] << (i * 4));
}
res = 0;
/* Permutation- Check */
for (i = 0; i < HALF_BLOCK_SIZE; i++) {
if (GET(tmp, P[i])) {
SET(res, (i+1));
}
}
return res;
}
/**
* @brief Encrypt 64 bits data
*
* @param plaintext plaintext 64 bits
* @param keys 16 subkeys (each 48bits)
* @return long ciphertext 64 bits
*/
long encryptdes(long plaintext, long* keys)
{
int i, j;
long l, r, tmp;
long ptext;
long ciphertext;
ptext = 0;
/* Initial Permutation- Check */
for (i = 0; i < BLOCK_SIZE; i++) {
if (GET(plaintext, IP[i])) {
SET(ptext, (i+1));
}
}
/* Extract left and right 32 bits- Check */
l = GET_LEFT32(ptext);
r = GET_RIGHT32(ptext);
/* Iterate 16 times- Check */
for (i = 0; i < ITERATION_TIMES; i++) {
tmp = r;
r = l ^ f(r, keys[i]);
l = tmp;
}
COMBINE(ptext, l, r, 32);
ciphertext = 0;
/* Inverse Initial Permutation */
for (i = 0; i < BLOCK_SIZE; i++) {
if (GET(ptext, INV_IP[i])) {
SET(ciphertext, (i+1));
}
}
/* free memory */
//free(keys);
return ciphertext;
}
long decryptdes(long ciphertext, long* keys)
{
int i, j;
long l, r, tmp;
long ptext;
long plaintext;
ptext = 0;
/* Initial Permutation */
for (i = 0; i < BLOCK_SIZE; i++) {
if (GET(ciphertext, IP[i])) {
SET(ptext, (i+1));
}
}
l = GET_LEFT32(ptext);
r = GET_RIGHT32(ptext);
/* Iterate 16 times */
for (i = 0; i < ITERATION_TIMES; i++) {
tmp = l;
l = r ^ f(l, keys[ITERATION_TIMES - i - 1]);
r = tmp;
}
COMBINE(ptext, l, r, 32);
plaintext = 0;
/* Inverse Initial Permutation */
for (i = 0; i < BLOCK_SIZE; i++) {
if (GET(ptext, INV_IP[i])) {
SET(plaintext, (i+1));
}
}
return plaintext;
}
3DES算法实现
首先是定义3DES的4中模式:
#ifndef __TDES_H__
#define __TDES_H__
#include "des.h"
typedef enum
{
EDE2, EDE3, EEE2, EEE3
} TripleDesMode;
/* Encrypt plaintext by 3DES */
long encrypt3des(long plaintext, long** keys, TripleDesMode mode);
/* Decrypt plaintext by 3DES */
long decrypt3des(long ciphertext, long** keys, TripleDesMode mode);
#endif
tdes.c
:根据mode判断执行哪一种3DES模式,3DES很简单,就是基于DES的3重加解密。
#include "tdes.h"
/**
* @brief 3DES
* E(K1, D(K2, E(K1, M)))
*
*/
/**
* @brief Encrypt plaintext by 3DES
*
* @param plaintext plaintext
* @param keys keys
* @param mode 3DES Mode
* @return long ciphertext
*/
long encrypt3des(long plaintext, long** keys, TripleDesMode mode)
{
if (mode == EDE2) {
return encryptdes(decryptdes(encryptdes(plaintext, keys[0]), keys[1]), keys[0]);
} else if (mode == EDE3) {
return encryptdes(decryptdes(encryptdes(plaintext, keys[0]), keys[1]), keys[2]);
} else if (mode == EEE2) {
return encryptdes(encryptdes(encryptdes(plaintext, keys[0]), keys[1]), keys[0]);
} else {
/* EEE3_MODE */
return encryptdes(encryptdes(encryptdes(plaintext, keys[0]), keys[1]), keys[2]);
}
}
/**
* @brief decrypt ciphertext by 3DES
*
* @param ciphertext plaintext
* @param key1 keys
* @param key2
* @return long
*/
long decrypt3des(long ciphertext, long** keys, TripleDesMode mode)
{
if (mode == EDE2) {
return decryptdes(encryptdes(decryptdes(ciphertext, keys[0]), keys[1]), keys[0]);
} else if (mode == EDE3) {
return decryptdes(encryptdes(decryptdes(ciphertext, keys[2]), keys[1]), keys[0]);
} else if (mode == EEE2) {
return decryptdes(decryptdes(decryptdes(ciphertext, keys[0]), keys[1]), keys[0]);
} else {
/* EEE3_MODE */
return decryptdes(decryptdes(decryptdes(ciphertext, keys[2]), keys[1]), keys[0]);
}
}
分组密码工作模式的实现
blockcipher.h
:定义五种分组密码工作模式的加解密函数,以及五种工作模式类型BlockCipherMode
,以及定义一个初始向量值IV=0x123456789abcdef0
,这个初始向量的值是可以改变的。
#ifndef __BLOCKCIPHER_H__
#define __BLOCKCIPHER_H__
#include <stdio.h>
#include "tdes.h"
/**
* @brief Block Cipher Working Mode:
* ECB: Electronic CodeBook Mode (Basic)
* CBC: Code Block cipher
* CFB: Code FeedBack
* OFB: Ouput FeeBack
* CTR: Counter Mode
*/
/* Define some */
typedef enum
{
ECB, CBC, CFB, OFB, CTR
} BlockCipherMode;
/* Initial Vector, 64 bits */
#define IV 0x123456789abcdef0
/* Encrypt file by 3DES using block cipher mode- ECB/CBC/CFB/OFB/CTR */
void encrypt(FILE* src, FILE* dst, BlockCipherMode bcmode, long** keys, TripleDesMode tripleDesMode);
/* Decrypt file by 3DES using block cipher mode- ECB/CBC/CFB/OFB/CTR */
void decrypt(FILE* src, FILE* dst, BlockCipherMode bcmode, long** keys, TripleDesMode tripleDesMode);
/* Encrypt file in ECB Mode(Basic) */
void ecbEncrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Decrypt file in ECB Mode(Basic) */
void ecbDecrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Encrypt file in CBC Mode */
void cbcEncrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Decrypt file in CBC Mode */
void cbcDecrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Encrypt file in CFB Mode */
void cfbEncrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Decrypt file in CFB Mode */
void cfbDecrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Encrypt file in OFB Mode */
void ofbEncrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Decrypt file in OFB Mode */
void ofbDecrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Encrypt file in CTR Mode */
void ctrEncrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
/* Decrypt file in CTR Mode */
void ctrDecrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode);
#endif
接下来是blockcipher.c
,由于这个文件的代码量比较大,并且实际上每一种工作模式的某些操作都是类似的,因此下面只展示ECB的加解密实现:
#include "util.h"
#include "blockcipher.h"
#include <stdlib.h>
#include <math.h>
/* Encrypt in Block Cipher Mode */
void encrypt(FILE* src, FILE* dst, BlockCipherMode bcmode, long** keys, TripleDesMode tripleDesMode)
{
switch (bcmode)
{
case CBC:
cbcEncrypt(src, dst, keys, tripleDesMode);
break;
case CFB:
cfbEncrypt(src, dst, keys, tripleDesMode);
break;
case OFB:
ofbEncrypt(src, dst, keys, tripleDesMode);
break;
case CTR:
ctrEncrypt(src, dst, keys, tripleDesMode);
break;
default:
ecbEncrypt(src, dst, keys, tripleDesMode);
break;
}
}
/* Decrypt in Block Cipher Mode */
void decrypt(FILE* src, FILE* dst, BlockCipherMode bcmode, long** keys, TripleDesMode tripleDesMode)
{
switch (bcmode)
{
case CBC:
cbcDecrypt(src, dst, keys, tripleDesMode);
break;
case CFB:
cfbDecrypt(src, dst, keys, tripleDesMode);
break;
case OFB:
ofbDecrypt(src, dst, keys, tripleDesMode);
break;
case CTR:
ctrDecrypt(src, dst, keys, tripleDesMode);
break;
default:
ecbDecrypt(src, dst, keys, tripleDesMode);
break;
}
}
/* Encrypt in ECB Mode(Basic) */
void ecbEncrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode)
{
/* Buffer 8 bytes*/
char* buf;
long fsize;
long ciphertext;
long plaintext;
/* Allocate Memory */
buf = (char *)malloc(sizeof(char)*8);
// obtain file size:
fseek(src , 0 , SEEK_END);
fsize = ftell(src);
rewind(src);
/* Print initial file size. */
fwrite(ltobytes(fsize), 1, 8, dst);
/* Read and Encrypt 8 bytes data each time. */
while(!feof(src)) {
fread(buf, 1, 8, src);
plaintext = charstol(buf);
/* Encrypt plaintext by 3DES. */
ciphertext = encrypt3des(plaintext, keys, tripleDesMode);
/* Convert ciphertext in bytes and write to dst file. */
fwrite(ltobytes(ciphertext), 1, 8, dst);
}
free(buf);
}
void ecbDecrypt(FILE* src, FILE* dst, long** keys, TripleDesMode tripleDesMode)
{
/* Buffer 8 bytes*/
char* buf;
int i;
int count;
long fsize;
long lastBlockBytes;
long ciphertext;
long plaintext;
/* Allocate Memory */
buf = (char *)malloc(sizeof(char)*8);
/* Get Decyrpted(initial) file size. and block amounts. */
fread(buf, 1, 8, src);
fsize = charstol(buf);
lastBlockBytes = fsize - 8*(fsize/8);
count = lastBlockBytes == 0 ? (fsize/8) : (fsize/8 + 1);
i = 0;
/* Read and Decrypt 8 bytes data each time. */
fread(buf, 1, 8, src);
while(!feof(src)) {
ciphertext = charstol(buf);
/* Encrypt plaintext by 3DES. */
plaintext = decrypt3des(ciphertext, keys, tripleDesMode);
/* Convert ciphertext in bytes and write to dst file. */
if (++i == count) {
/* Reach the last block */
fwrite(ltobytes(plaintext), 1, lastBlockBytes, dst);
} else {
fwrite(ltobytes(plaintext), 1, 8, dst);
}
fread(buf, 1, 8, src);
}
free(buf);
}
完整代码
DES的算法实现部分只展示了部分的代码,完整的代码在我的github里,可以点击这个链接🔗获取:DES Code,里面实现了一个最基本的3DES加解密算法,里面还有一个可执行程序des
,在linux环境下,下面展示如何使用des
这个程序进行加解密操作,由于实现的简便性,加解密都会同时做,如果想要分开,可以修改main.c
代码,重新编译。
[struggle@localhost Des]$ ./des -h
Usage: ./des [-h] [-t] -dm <3DES-Mode> -bm <Block-Cipher-Mode> -k2 <key1> <key2> -k3 <key1> <key2> <key3> -f <srcfile> -ef <encryptfil0e> -df <decryptfile>
Options:
-h Print Usage
-t Print encrypt time
-dm <3DES-Mode> Select 3des mode[EDE2, EDE3, EEE2, EEE3
-bm <Block-Cipher-Mode> Select Block cipher mode[ECB, CBC, CFB, OFB, CTR]
-k2 <key1> <key2> input 2 key
-k3 <key1> <key2> <key3> input 3 keys
-f <srcfile> input srcfile path
-ef <encryptfile> input encryptfile path
-df <decryptfile> input decryptfile path
[struggle@localhost Des]$ ./des -t -dm EDE2 -bm CTR -k2 shenzhen facebook -f videos.mp4 -ef encrypt.mp4 -df decrypt.mp4
srcfile size: 22249980 bytes.
Encrypt time: 73.269997 s
srcfile size: 22249992 bytes.
Decrypt time: 74.330002 s
上面这个例子是加解密一个20MB大小的一个视频文件,加解密的平均时间为74秒左右,还是比较慢的,如果使用3DES加解密更大的文件就需要大量的时间,此时可以选择一些其它加密算法,如AES,AES的加解密速度比3DES块很多,并且其安全性与3DES相当。
实验总结
DES作为一个经典的分组加密算法,基于Feistel密码结构,但是由于超级计算机的诞生,计算机的算力提升了不止一个档次,DES面对超算的穷举搜索攻击,其安全性变得很差,很容易就被攻破,这时候使用多重加密的方式,于是出现了3DES,它的强度是足够的,密钥可以有92位,甚至168位,它的安全性上升了几十个上百个数量级,就是因为密钥空间变大了很多,但是随之而来的就是加密的效率低的问题。因此,现代加密算法出现了一个新的算法,AES(高级加密标准),它基于字节的运算,编程实现起来更加简单,但是其安全强度却与3DES相当。