前言
在csdn上找了几份关于des的源码,要么不支持cbc要么转换出来结果不对(观看源码大多是填补的方式不同,多样)。所以自己用c写了一个支持大数据的cbc-des库。
使用时候修改.h,进行裁剪,在文件里放入明文就行了。亲测可行,链接https://download.csdn.net/download/qq_35651984/12447007
/*
======================SIMPLE_DES======================
------------------------------------------------------
----------------------vision:1.0----------------------
----------------------edtor:川大门口贴膜小哥 ----------
----------------------time:19/4/10--------------------
功能(1.0):
支持des的cbc,ecb模式
支持大数据加密(文件操作)
支持十进制字符和十六进制字符的加密输出/解密输入
操作流程:
配置#define
DES_Contr con;
DES_Init(&con);
DES_EN_File(&con);
DES_DE_File(&con);
注意事项:
使用文件加密/解密时候,需在当前目录创建需要加密/解密的文件
DES_EN_File( )---> EN.txt---加密---DE.txt
DES_DE_File( )---> DE.txt---解密---EN2.txt
*/
#define DES_MODE CBC //CBC OR ECB
#define OUTPUT_MODE 16//16 OR 10 10:10_ENTO_10,10_DETO_10,16:10_ENTO_16,16_DETO_10
#define _IV "\0\0\0\0\0\0\0\0"//CBC模式下偏移量
#define _KEY "12345678" //key
typedef struct st
{
int status;//控制器状态
char subkey[16][64];//子密钥
char en_again[8];//加密时候,上一分组加密数据
char de_again[8];//解密时候,上一分组解密数据
}DES_Contr;
//PUBLIC API
int DES_Init(DES_Contr *des_contr);
int DES_EN_File(DES_Contr *con);//大数据加密
int DES_DE_File(DES_Contr *con);//大数据解密
int DES_EN(DES_Contr *des_contr,char data[8],char en_data[8]);//一个分组加密,data:输入,endata:输出密文
int DES_DE(DES_Contr *des_contr,char data[8], char de_data[8]);//一个分组解密,data:输入密文,endata:输出明文
DES 原理
一输入输出
加密:
输入: 一组不定长n的明文
输入:8字节的key
可选输入:模式,IV,填充方式等
输出:一组不定长n+i (i<=8,n+i能被8整除)的密文
解密:
同理,输入一组不定长n+i (i<=8,n+i能被8整除)的密文,输出 一组不定长n的明文
二 运算步骤
des分为三个步骤:
一:密钥步骤
首先通过一个8字节的密钥生成16个48字节的子密钥。(本质上是64位密钥生成16个48位的子密钥,我程序中将bit转换位字符利于计算)
二:整体步骤
将一个不定长的明文,填充一定的字节(小于9字节),使其能够被8字节整除。每8字节为一个分组,每一个分组运行一次分组步骤。(在ecb模式下,每个分组相互独立运算,互不影响。而cbc模式也叫密文分组链接方式,相互分组有联系,一个分组出错,则后面全部运算错误)
所以cbc模式下在整体步骤中会多一个分组链接的功能,具体实现方式:
加密:明文分组N(n>1)与上一个密文分组N-1进行异或后,替代明文分组N,N再进行加密。
解密:解密后得到明文N(n>1),与上一个密文分组N-1进行异或后,替代明文分组N。
当N=1时:与IV进行异或,IV基本都时"\0\0\0\0\0\0\0\0",异或后等于分组1本身
三:分组步骤
分组步骤是des算法的核心,也就是通过置换,迭代,移位等操作,输入8字节数据,输出8字节密文。
具体运算步骤
具体运算步骤我就不重复造轮子了,可以参考此处参考博文
移位
const int MOV[16] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 };
通过MOV表左移一位或者两位获得16个子密钥
扩展/替换
const int PC_2[56] = { 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 };
const int IP[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 };
通过pc,ip,e,s盒等,替换对应位的数据,如48位扩展为64位,48位替换为32位。
异或
在分组链接和f()函数中均有异或操作
for (int i = 0; i < 8; i++)
{
de_buffer[i] = de_buffer[i] ^ con->de_again[i];
}
填充方式
填充方式也就是补齐为8字节该怎么填充。
Zero padding
网上找的源码大多都错在这里,Zero padding是在后面填充0,但不确定正确数据后面是否有零,删除填充时候,误删正确数据。
PKCS5Padding
也有的错在这里,填充填充的长度。如“abc”填充“abc55555”,但填充的是‘5’是错误的,正确的填充方式是“abc\5\5\5\5\5”.