3DES算法及说明

来源:互联网 发布:opensns案例项目源码 编辑:程序博客网 时间:2024/06/11 18:08

1977年1月,美国政府颁布:采纳IBM公司设计的方案作为非机密数据的正式数据加密标准(Data Encryption Standard)。上周帮leojay看3DES算法,讲讲这个算法是如何进行的:)

先来讲讲DES加密:
首先,这个算法全程是按位运算的,每次计算64位的内容,也就是8个字节。密钥Key,为64位。
我们把内容经过一个初始置换,就是每位按如下表格进行位置交换:

Initial Permutation
58,50,12,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,


然后把换好的东西经过16轮叫做DES的算法

每一轮这个算法是这样的:

我们先来生成子密钥:
把密钥Key的每个字节的最后一位扔掉,经过第1次缩小换位:

Permuted Choice One
左: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


这样就从64位减少到56位了,把这56位分为左28位和右28位,分别称为Ci和Di。

将Ci和Di分别进行左移,左移的位数由第几论来决定:

Schedule of Left Shifts:
Round number: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Bits rotated:      1 1 2 2 2 2 2 2 1  2   2   2   2   2   2  1


把左移好的Ci和Di,通过第2次缩小换位:

Permuted Choice Two
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


这样生成了一组48位的子密钥Ki。
而刚刚左移好的Ci和Di将等待着下一轮的计算。

下面处理加密的内容:
把加密的内容分为左32位和右32位,分别称为Li和Ri,
Li先放着不管,先来看Ri,把这个Ri经过一个F函数的变化:
先通过放大换位表:

Expansion Permutation
  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,


将这32位变成48位,然后和刚刚生成的子密钥Ki进行一次异或运算,再经过S-box这样一个选择运算将这48位变为32位。

S-box的过程如下:
将输入的48位分为8组,每组对应一个S-box表,每个表里有4行16列。
这样每组有6位,取2、3、4、5位为列数,1、6位为行数,在所对应的表中寻找这个数字,将找到的数字转换为4位二进制数输出。这样一共8组,就将48位转换为32位了。

Definition of DES S-boxes:
S1:
  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,
S2:
  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,
S3:
  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,
S4:
  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,
S5:
  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,
S6:
  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,
S7:
  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,
S8:
  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,



然后通过置换函数P的变化:

Permutation Function
  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位的转换后的Ri与Li进行一次异或运算后放入Ri,
而将没有经过转换的那个Ri放入Li,这样就进行完一轮DES算法。

连续进行16轮后,再进行一次逆置换:

Inverse initial permutation:
  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,


这样就完成了一次DES加密过程,解密过程刚好与其相反:)

下面说说3DES:
共有两个密钥key1和key2:
加密过程:用key1加密,用key2解密再用key1加密
解密过程:用key1解密,用key2加密再用key1解密

下面是算法的实现程序:

//3DES加密解密算法 - SJW
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <memory.h>
using namespace std;

typedef bool
(*PSubKey)[16][48];

// 初始置换
const static char Table_IP[64] =
{
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
};

// 末置换
const static char Table_InverseIP[64] =
{
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 const char Table_E[48] =
{
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
};

// 密钥初始置换
const static char Table_PC1[56] = {
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
};

// 左移运算
const static char Table_Moveleft[16] =
{
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
};

// 密钥压缩置换
const static char Table_PC2[48] =
{
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
};

// S盒
const static char Box_S[8][4][16] =
{
// S1
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,
// S2
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,
// S3
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,
// S4
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,
// S5
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,
// S6
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,
// S7
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,
// S8
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
};

// P盒置换
const static char Table_P[32] =
{
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
};

static bool SubKey[2][16][48];//两个密钥的16轮子密钥
static char Tmp[256], deskey[16];

#define ENCRYPT 0
#define DECRYPT 1

//通用置换函数
void Transform(bool *Out, bool *In, const char *Table, int len)
{
for(int i = 0; i < len; ++ i)
Tmp[i] = In[ Table[i] - 1 ];
memcpy(Out, Tmp, len);
}

//异或运算
void Xor(bool *InA, const bool *InB, int len)
{
for(int i = 0; i < len; ++ i)
InA[i] ^= InB[i];
}

//循环左移
void MoveLeft(bool *In, int len, int loop)
{
memcpy(Tmp, In, loop);
memcpy(In, In + loop, len - loop);
memcpy(In + len - loop, Tmp, loop);
}

//字节转换成位
void Byte2Bit(bool *Out, const char *In, int bits)
{
for(int i = 0; i < bits; ++ i)
Out[i] = (In[i >> 3] >> (i & 7)) & 1;
}

//位转换字节
void Bit2Byte(char *Out, const bool *In, int bits)
{
memset(Out, 0, bits >> 3);
for(int i = 0; i < bits; ++ i)
Out[i >> 3] |= In[i] << (i & 7);
}

//S 盒置换
void funS(bool Out[32], const bool In[48])
{
for(char i = 0, j, k; i < 8; ++ i, In += 6, Out += 4)
{
j = (In[0] << 1) + In[5];
k = (In[1] << 3) + (In[2] << 2) + (In[3] << 1) + In[4];
Byte2Bit(Out, &Box_S[i][j][k], 4);
}
}

//F 函数
void funF(bool In[32], const bool Ki[48])
{
static bool MR[48];
Transform(MR, In, Table_E, 48);
Xor(MR, Ki, 48);
funS(In, MR);
Transform(In, In, Table_P, 32);
}

//生成子密钥
void MakeSubKey(PSubKey pSubKey, const char Key[8])
{
static bool K[64], *KL = &K[0], *KR = &K[28];
Byte2Bit(K, Key, 64);
Transform(K, K, Table_PC1, 56);
for(int i = 0; i < 16; ++ i)
{
MoveLeft(KL, 28, Table_Moveleft[i]);
MoveLeft(KR, 28, Table_Moveleft[i]);
Transform((*pSubKey)[i], K, Table_PC2, 48);
}
}

//生成密钥
void MakeKey(const char* Key, int len)
{
memset(deskey, 0, 16);
memcpy(deskey, Key, len > 16 ? 16 : len);
MakeSubKey(&SubKey[1], &deskey[8]);
}

//一重DES加/解密
void DES(char Out[8], char In[8], const PSubKey pSubKey, bool Type)
{
static bool M[64], tmp[32], *Li = &M[0], *Ri = &M[32];
Byte2Bit(M, In, 64);
Transform(M, M, Table_IP, 64);
if( Type == ENCRYPT ){ //加密

for(int i = 0; i < 16; ++ i)
{
memcpy(tmp, Ri, 32);
funF(Ri, (*pSubKey)[i]);
Xor(Ri, Li, 32);
memcpy(Li, tmp, 32);
}
}
else //解密
{
for(int i = 15; i >= 0; -- i)
{
memcpy(tmp, Li, 32);
funF(Li, (*pSubKey)[i]);
Xor(Li, Ri, 32);
memcpy(Ri, tmp, 32);
}
}
Transform(M, M, Table_InverseIP, 64);
Bit2Byte(Out, M, 64);
}

bool DoDES(char *Out, char *In, long datalen, const char *Key, int keylen, bool Type)
{
if( !( Out && In && Key && (datalen=(datalen+7)&0xfffffff8) ) )
return false;

MakeKey(Key, keylen);
// 3次DES 加密:加(key0)-解(key1)-加(key0) 解密:解(key0)-加(key1)-解(key0)
for(long i = 0, j = datalen >> 3; i < j; ++ i, Out += 8, In += 8)
{
DES(Out, In,  &SubKey[0], Type);
DES(Out, Out, &SubKey[1], !Type);
DES(Out, Out, &SubKey[0], Type);
}
return true;
}

int main(int argc,char *argv[])
{
int i;
char key[255];
char buf[255];
char str[255];
char key1[15];
char key2[8];

cout<<"Please input The string before encrypting: /n";
cin.getline(str, 255, '/n');
do
{
cout<<"Please input Key1(8 chars): /n";
cin>>key1;
} while (strlen(key1) != 8); //key1为8个字符

cout<<"Please input Key2: /n";
cin>>key2;

memset(key, 0, sizeof(key));
for (i = 0; i < 8; i ++)
{
key[i] = key1[i];
key[i + 8] = key2[i];
}

memset(buf, 0, sizeof(buf));
strcpy(buf, str);
cout<<"/nBefore encrypting/n";
cout<<buf<<endl;

DoDES(buf, buf, sizeof(str), key, sizeof(key), ENCRYPT);
cout<<"/nAfter encrypting/n";
cout<<buf<<endl;

DoDES(buf, buf, sizeof(str), key, sizeof(key), DECRYPT);
cout<<"/nAfter decrypting/n";
cout<<buf<<endl;

return 0;
}

 

3DES加密模式

    3DES(Triple DES)是DES向AES过渡的加密算法,它使用3条64位的密钥对数据进行三次加密。是DES的一个更安全的变形。它以DES为基本模块,通过组合分组方法设计出分组加密算法。比起最初的DES,3DES更为安全。

    相信3DES的算法都不太陌生,在此不用多多介绍!下面介绍几种其它模式的3DES。 

  • ECB(Electronic Codebook)模式

    ECB其实非常简单,就是将数据按照8个字节一段进行DES加密或解密得到一段8个字节的密文或者明文,最后一段不足8个字节,按照需求补足8个字节(通常补00或者FF,根据实际要求不同)进行计算,之后按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。

  • OFB(Output Feedback)模式


  •  
    • CFB(Cipher Feedback)模式

        稍后添加...

    • CBC(Cipher Block Chaining)模式

    加密步骤如下:

    1)首先将数据按照8个字节一组进行分组得到D1D2......Dn(若数据不是8的整数倍,用指定的PADDING数据补足8的整数倍)

    2)第一组数据D1与初始化向量I异或后的结果进行加密得到第一组密文C1

    3)第二组数据D2与第一组的加密结果C1异或以后的结果进行加密,得到第二组密文C2

    4)之后的数据以此类推,得到Cn

    5)按顺序连为C1C2C3......Cn即为加密结果。

    解密是加密的逆过程,步骤如下:

    1)首先将数据按照8个字节一组进行分组得到C1C2C3......Cn(因为加密的时候是补足了8的整数倍的,所以密文应该为8的整数倍而不用填充,如果需要填充,那数据就肯定不对)

    2)将第一组数据进行解密后与初始化向量I进行异或得到第一组明文D1

    3)将第二组数据C2进行解密后与第一组密文数据进行异或得到第二组数据D2

    4)之后依此类推,得到Dn

    5)按顺序连为D1D2D3......Dn即为解密结果。

 

3DES的一些说明

 

3DES:是在DES的基础上采用三重DES,即用两个56位的密钥K1,K2,发送方用K1加密,K2解密,再使用K1加密.接收方使用K1解密,K2加密,再使用K1解密,

3DES实现: 主要有CBC,ECB实现,java默认是ECB
对于待加密解密的数据的填充方式:NoPadding、PKCS5Padding、SSL3Padding,默认填充方式为,PKCS5Padding

java中要求key的size必须为24;对于CBC模式下的向量iv的size两者均要求必须为8, 所以在处理8字节的key的时候,直接使用DES三次, 加密时候为(加密--解密--加密),解密时候为:(解密--加密--解密)

所以,你在使用8字节的key做3des的使用,使用对应的上面类来做3次动作,解密肯定是正确的

 

 

JAVA中3DES加密解密与其他语言(C/C++)通信

 

    3DES(即Triple DES)是DES向AES过渡的加密算法(1999年,NIST将3-DES指定为过渡的加密标准),是DES的一个更安全的变形。它以DES为基本模块,通过组合分组方法设计出分组加密算法,

其具体实现如下:

设Ek()和Dk()代表DES算法的加密和解密过程,K代表DES算法使用的密钥,P代表明文,C代表密表,这样,

3DES加密过程为:C=Ek3(Dk2(Ek1(P)))

3DES解密过程为:P=Dk1((EK2(Dk3(C)))


       K1、K2、K3决定了算法的安全性,若三个密钥互不相同,本质上就相当于用一个长为168位的密钥进行加密。多年来,它在对付强力攻击时是比较安全的。若数据对安全性要求不那么高,K1可以等于K3。在这种情况下,密钥的有效长度为112位。

 

 

当然,只用Java语言编写程序进行3DES的加密解密不用考虑任何问题,因为加密和解密是逆向过程,用的model和padding都是相同的,考虑到Java与其他语言通信时,就必须两种语言的model和padding是相同的才可以。

 

从一下的说明可以看出这一点:

 

写道
使用cipher可以很容易的实现3des加密,但是跟其他平台开发的3des加密对接来说,通常会有一些问题。基本的程序如下:

public static byte[] desEncrypt(String message, String key) throws Exception {
Cipher cipher = Cipher.getInstance("DESede");

DESKeySpec desKeySpec = new DESKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

return cipher.doFinal(message.getBytes("UTF-8"));
}

我们跟其他平台对接发现对同样输入加密以后结果不同,看看jdk的文档,有如下描述:

A transformation is a string that describes the operation (or set of operations) to be performed on the given input, to produce some output.
A transformation is of the form:
•"algorithm/mode/padding" or
•"algorithm"
(in the latter case, provider-specific default values for the mode and padding scheme are used).

根据前面的代码,我们已经选择了正确的算法,那么加密不同的原因应该就是mode和padding了。

he SunJCE provider uses ECB as the default mode, and PKCS5Padding as the default padding scheme for DES, DES-EDE and Blowfish ciphers. This means that in the case of the SunJCE provider,
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.

对于其他语言开发的3des,一定要采用相同的mode和padding才能保证通信。