发新帖本帖赏金 3.00元(功能说明)我要提问
12下一页
返回列表
打印
[应用相关]

DES加密C语言实现及讲解

[复制链接]
9030|37
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wangzhihai1986|  楼主 | 2015-7-8 20:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 wangzhihai1986 于 2015-7-9 20:17 编辑

这段时间没有接外包项目了,又加之喜欢的电视剧每周更新太少,闲来无事,把以前学的加密算法分享下。:lol


这些代码都是参考网上资料,并对其进行了整理,所有代码都在STM32F407开发板上测试通过。

先贴出用到的常量表,如下。

static uint8_t Sbox[8][64]={ /* 选择函数表 */
{
  14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7, /* S1 */
  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, /* S2 */
  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, /* S3 */
  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, /* S4 */
  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, /* S5 */
  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, /* S6 */
  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, /* S7 */
  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, /* S8 */
  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  
}
};
static uint8_t PC1[56]={ /* 转换选择1--用于密钥处理 */
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
};
static uint8_t PC2[48]={ /* 转换选择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
};
static uint8_t vIP[64]={ /* 初始转换表IP */
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  
};  
static uint8_t invIP[64]={ /* 初始转换表的逆IP-1 */
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  
};
static uint8_t vE[48]={ /* 扩展表E */
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  
};  
static uint8_t vP[32]={ /* 转换函数表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 };

以下为子函数实现。

/** 将1字节转换成8位
pIn@指向要转换成64个位的8个字节;
pOut@指向转换后返回的64个位。
**/
static void Byte2Bits(uint8_t *pIn, uint8_t *pOut)
{
int8_t i, j, tmp;

for(i=0; i<8; i++)
{
  tmp = pIn;
  for(j=i*8+7; j>=i*8; j--)
  {
   pOut[j] = tmp & 0x01; /* pOut[7]->LSB, pOut[0]->MSB */
   tmp >>= 1;
  }
}
}
/** 8位转换成1字节
pIn@指向要转换成8个字节的64个位;
pOut@指向转换后返回的8个字节。
**/
static void Bits2Byte(uint8_t *pIn, uint8_t *pOut)
{
uint8_t i, tmp;

for(i=0; i<8; i++)
{
  tmp = i * 8;
  pOut = (pIn[tmp]<<7) + (pIn[tmp+1]<<6) + (pIn[tmp+2]<<5) + (pIn[tmp+3]<<4)+
     (pIn[tmp+4]<<3) + (pIn[tmp+5]<<2) + (pIn[tmp+6]<<1) + pIn[tmp+7]; /* pIn[0]->MSB, pIn[7]->LSB */
}
}
/** 异或操作
pIn1@指向参与异或操作的第一个变量;
pIn2@指向参与异或操作的第二个变量;
pOut@指向异或操作后的返回结果;
num@进行异或操作的个数。
**/
static void ByteXor(uint8_t *pIn1, uint8_t *pIn2, uint8_t *pOut, uint8_t num)
{
uint8_t i;

for(i=0; i<num; i++)
{
  pOut = pIn1 ^ pIn2;
}
}
/** 将输入数据用已知表进行置换
pIn@指向要进行置换的数据;
pOut@指向置换后返回的数据;
pTable@指向定义好的置换表;
num@要置换的数据个数。
**/
static void Permutation(uint8_t *pIn, uint8_t *pOut, uint8_t *pTable, uint8_t num)
{
uint8_t i, tmp;

for(i=0; i<num; i++)
{
  tmp = pTable - 1; /* 因为置换表是从1开始的,所以要减1 */
  pOut = pIn[tmp];
}
}
/** 将64位数据转换成左右各32位数据
pIn@指向要分成左右两部分的数据;
pL@指向返回的左半部分数据;
pR@指向返回的右半部分数据;
num@要分半的数据个数,必须为偶数。
**/
static void DivideLR(uint8_t *pIn, uint8_t *pL, uint8_t *pR, uint8_t num)
{
uint8_t i, tmp;

tmp = num >> 1;
for(i=0; i<tmp; i++)
{
  pL = pIn;
  pR = pIn[i+tmp];
}
}
/** 置换Sbox
pIn@指向要进行选择表置换的数据;
pOut@指向置换后返回的数据。
**/
static void PermuSbox(uint8_t *pIn, uint8_t *pOut)
{
uint8_t i, j, tmp, t05, t14;

for(i=0; i<8; i++)
{
  tmp = 6 * i;
  t05 = (pIn[tmp] << 1) + pIn[tmp+5]; /* 取6位中的最高位和高低位组成新的2位,代表Sbox的行 */
  t14 = (pIn[tmp+1] << 3) + (pIn[tmp+2] << 2) + (pIn[tmp+3] << 1) + pIn[tmp+4]; /* 6位的中间4位组成新的4位,代表Sbox的列 */
  tmp = (t05 << 4) + t14; /* 4行16列--从0至63 */
  tmp = Sbox[tmp]; /* 把6位输入按Sbox置换成4位输出(48->32) */
  
  j = 4 * i;
  pOut[j+0] = (tmp >> 3) & 0x01;
  pOut[j+1] = (tmp >> 2) & 0x01;
  pOut[j+2] = (tmp >> 1) & 0x01;
  pOut[j+3] = tmp & 0x01; /* 转换成1位形式 */
}
}



/** 8字节块加密
pEncDat@指向要加密的数据,并返回它;
pKey@指向处理后的密钥。
**/
static void BlockEncrypt(uint8_t *pEncDat, uint8_t *pKey)
{
uint8_t i, j, EncTmp1[64], EncTmp2[64], pL[32], pR[32];

Byte2Bits(pEncDat, EncTmp1);
Permutation(EncTmp1, EncTmp2, vIP, 64); /* 按IP进行初始置换 */
DivideLR(EncTmp2, pL, pR, 64);

for(i=0; i<16; i++)
{
  Permutation(pR, EncTmp1, vE, 48); /* 对右32位按E扩展到48位 */
  ByteXor(EncTmp1, &pKey[i*48], EncTmp2, 48); /* 扩展后和第i轮密钥进行异或 */
  PermuSbox(EncTmp2, EncTmp1); /* 按选择表进行置换 */
  Permutation(EncTmp1, EncTmp2, vP, 32); /* 按P函数表把48位转换成32位 */
  ByteXor(EncTmp2, pL, EncTmp1, 32); /* 和左32位进行异或 */
  for(j=0; j<32; j++)
  {
   pL[j] = pR[j]; /* 上轮的右半部分作为下轮的左半部分 */
   pR[j] = EncTmp1[j]; /* 处理后的32位作下轮的右半部分 */
  }  
}

for(j=0; j<32; j++) /* 合并成64位 */
{
  EncTmp1[j] = pR[j];
  EncTmp1[j+32] = pL[j];
}
Permutation(EncTmp1, EncTmp2, invIP, 64); /* 按IP的逆表进行置换 */
Bits2Byte(EncTmp2, pEncDat); /* 转换成8字节返回 */
}

/** 8字节块解密
pDecDat@指向要解密的数据,并返回它;
pKey@指向处理后密钥,同加密一样。
**/
static void BlockDecrypt(uint8_t *pDecDat, uint8_t *pKey)
{
uint8_t i, j, DecTmp1[64], DecTmp2[64], pL[32], pR[32];

Byte2Bits(pDecDat, DecTmp1);
Permutation(DecTmp1, DecTmp2, vIP, 64);
DivideLR(DecTmp2, pL, pR, 64);

for(i=16; i>0; i--)
{
  Permutation(pR, DecTmp1, vE, 48);
  ByteXor(DecTmp1, &pKey[(i-1)*48], DecTmp2, 48); /* 解密时密钥是从第16轮至第一轮进行的,同加密相反 */
  PermuSbox(DecTmp2, DecTmp1);
  Permutation(DecTmp1, DecTmp2, vP, 32);
  ByteXor(DecTmp2, pL, DecTmp1, 32);
  for(j=0; j<32; j++)
  {
   pL[j] = pR[j];
   pR[j] = DecTmp1[j];
  }  
}

for(j=0; j<32; j++)
{
  DecTmp1[j] = pR[j];
  DecTmp1[j+32] = pL[j];
}
Permutation(DecTmp1, DecTmp2, invIP, 64);
Bits2Byte(DecTmp2, pDecDat);
}


加密流程:
1.输入8字节要加密数据,然后把8字节转换成位形式存储于64个字节中(注:高位存低字节,低位存高字节中,详情参看下面贴出的代码Byte2Bits函数);
2.按IP表对64位进行转换,得到新的64位。(参:Permutation函数)
3.把64位分成左右各32位。(
参:DivideLR函数)
注:4至9步共循环16次(i<16)
4.按E表对R(右32位)扩展到48位。
5.把扩展后的R(48位)与key
(密钥)进行异或操作。(参:ByteXor函数)
6.按Sbox(s1-s8)对第5步得到的48位进行转换,得到最终的32位。(参:PermuSbox函数)
7.把这个32位和P表置换。
8.然后再与L(左32位)进行异或。
9.组合LR为新的64位。
10.4-9步的16次循环后得到最终的64位,然后与IP-1表进行置换,完成一次加密。
11.把64位转为8字节,这就是加密后的8字节。 (参:Bits2Byte函数)


(解密时,4-9步倒着执行即可。)



FIPS 46-3 DES.pdf

178.92 KB

DES算法原理

打赏榜单

21ic小喇叭 打赏了 3.00 元 2015-07-10

沙发
wangzhihai1986|  楼主 | 2015-7-8 20:25 | 只看该作者
本帖最后由 wangzhihai1986 于 2015-7-9 20:33 编辑

先贴出密钥扩展算法,下面有讲解。

/** 对输入64位密钥进行处理
pIn@指向输入的8字节密钥;
pOut@指向处理后返回的密钥。
**/
static void ProcKey(uint8_t *pIn, uint8_t *pOut)
{
uint8_t i, j, tmp[4], KeyBits[64], KeyPC1[56], pC[28], pD[28];

Byte2Bits(pIn, KeyBits); /* 8字节转换成64位 */
Permutation(KeyBits, KeyPC1, PC1, 56); /* 按PC1把64位置换成56位 */
DivideLR(KeyPC1, pC, pD, 56); /* 分成左右各28位 */

for(i=0; i<16; i++)
{
  switch(i)
  {
   case 0:
   case 1:
   case 8:
   case 15: /* 循环左移1位 */
   {
    tmp[0] = pC[0];
    tmp[1] = pD[0];
    for(j=0; j<27; j++)
    {
     pC[j] = pC[j+1];
     pD[j] = pD[j+1];
    }
    pC[27] = tmp[0];
    pD[27] = tmp[1];     
    break;
   }
   default: /* 循环左移2位 */
   {
    tmp[0] = pC[0];
    tmp[1] = pC[1];
    tmp[2] = pD[0];
    tmp[3] = pD[1];
    for(j=0; j<26; j++)
    {
     pC[j] = pC[j+2];
     pD[j] = pD[j+2];
    }
    pC[26] = tmp[0];
    pC[27] = tmp[1];
    pD[26] = tmp[2];
    pD[27] = tmp[3];
    break;
   }
  } /* end switch */
  
  for(j=0; j<28; j++) /* 合并成56位 */
  {
   KeyPC1[j] = pC[j];
   KeyPC1[j+28] = pD[j];
  }
  Permutation(KeyPC1, &pOut[i*48], PC2, 48); /* 按PC2转换成48位返回,共进行16轮操作 */
}
}


key扩展算法:
1.8字节转为64位存储。
2.按PC1表把64位置换成56位。
3.把56位分成左右各28位。
注:共循环16次,得到16*48字节的扩展密钥
4.第0、1、8、15次循环时,对左右28位采用循环左移一位,其它次循环则采用循环左移2位(参见上面代码)
5.把每次循环得到的左右28位组合成56位,然后采用PC2进行置换,得到一轮48位密钥。



使用特权

评论回复
板凳
wangzhihai1986|  楼主 | 2015-7-8 20:25 | 只看该作者
本帖最后由 wangzhihai1986 于 2016-1-13 17:55 编辑

前两楼介绍了加密算法的核心,现在介绍加密的模式。
DES共有四种加密模式,分别为ECB、CBC、CFB、OFB。详情参看附件。

下面只贴出ECB实现代码,其它方式参见附件。

/** ECB模式加密
pPlain@指向要加密的明文数据;
pEnc@指向加密后返回的密文数据;
pKey@指向加密用的密钥;
nBlock@要进行加密的块数,1块=8字节。
**/
void DES_ECB_Encrypt(uint8_t *pPlain, uint8_t *pEnc, uint8_t *pKey, uint32_t nBlock)
{
uint8_t i, tmpkey[16*48];

ProcKey(pKey, tmpkey); /* 产生16轮密钥 */

for(; nBlock>0; nBlock--)
{
  for(i=0; i<8; i++)
  {
   pEnc = pPlain;
  }
  BlockEncrypt(pEnc, tmpkey);
  
  pPlain += 8;
  pEnc += 8;
}
}
/** ECB模式解密
pDec@指向要解密的密文数据;
pPlain@指向解密后返回的明文数据;
pKey@指向解密用的密钥;
nBlock@要进行解密的块数,1块=8字节。
**/
void DES_ECB_Decrypt(uint8_t *pDec, uint8_t *pPlain, uint8_t *pKey, uint32_t nBlock)
{
uint8_t i, tmpkey[16*48];

ProcKey(pKey, tmpkey);

for(; nBlock>0; nBlock--)
{
  for(i=0; i<8; i++)
  {
   pPlain = pDec;
  }
  BlockDecrypt(pPlain, tmpkey);
  
  pPlain += 8;
  pDec += 8;
}
}

到此DES加密算法介绍完毕,不明白的可以贴出来共同讨论学习。有机会再把AES介绍下。感谢大家的关注

FIPS 81--Des Modes of Operation.pdf

276.31 KB

DES加密模式说明

使用特权

评论回复
地板
dirtwillfly| | 2015-7-8 20:41 | 只看该作者
围观学习

使用特权

评论回复
5
lxyppc| | 2015-7-8 20:59 | 只看该作者
楼主可以看一下mbed tls
支持的算法很多

使用特权

评论回复
6
wangzhihai1986|  楼主 | 2015-7-8 21:40 | 只看该作者
lxyppc 发表于 2015-7-8 20:59
楼主可以看一下mbed tls
支持的算法很多

谢谢分享,又了解个新东西。:lol

使用特权

评论回复
7
yxycdz| | 2015-7-8 21:43 | 只看该作者
学习!

使用特权

评论回复
8
mark0668| | 2015-7-9 01:12 | 只看该作者

学习了!

使用特权

评论回复
9
Leeone| | 2015-7-9 09:14 | 只看该作者
偷偷告诉我哪里接的外包

使用特权

评论回复
10
lgq1542380129| | 2015-7-9 13:21 | 只看该作者
赞一个

使用特权

评论回复
11
hwl1023| | 2015-7-9 16:58 | 只看该作者
学习了,楼主继续分享

使用特权

评论回复
12
wangzhihai1986|  楼主 | 2015-7-9 19:55 | 只看该作者
Leeone 发表于 2015-7-9 09:14
偷偷告诉我哪里接的外包

都是偶然机会,慢慢就积累客户了。

使用特权

评论回复
13
pkuzhx| | 2015-7-10 13:53 | 只看该作者
但是DES不是已经被证明不安全了吗?即便是3DES也不安全,还是AES搞起吧

使用特权

评论回复
14
wangzhihai1986|  楼主 | 2015-7-10 19:20 | 只看该作者
pkuzhx 发表于 2015-7-10 13:53
但是DES不是已经被证明不安全了吗?即便是3DES也不安全,还是AES搞起吧

还是值得学习的;P

使用特权

评论回复
15
lkl0305| | 2015-7-10 23:53 | 只看该作者
好帖,前来学习

使用特权

评论回复
16
pkuzhx| | 2015-7-11 08:43 | 只看该作者

确实,经典的对称加密算法,当年我就没搞懂。。

使用特权

评论回复
17
wangzhihai1986|  楼主 | 2015-7-11 11:12 | 只看该作者
pkuzhx 发表于 2015-7-11 08:43
确实,经典的对称加密算法,当年我就没搞懂。。

相比AES来说,就是表太多了。

使用特权

评论回复
18
豆腐块| | 2015-7-11 21:32 | 只看该作者
DES算法还是值得学习的,有些思想还是比较经典的

使用特权

评论回复
19
FireRiver9| | 2015-7-12 13:31 | 只看该作者
谢谢分享

使用特权

评论回复
20
尤彼卡| | 2015-7-12 22:15 | 只看该作者
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

使用特权

评论回复
发新帖 本帖赏金 3.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:熟练掌握STM32F系列芯片,USB和以太网都可以做,要外包的可以联系我(QQ:594378180)。

21

主题

333

帖子

2

粉丝