目录
一、BitSet类的介绍
BitSet类实现了一个按需增长的位向量。位Set的每一个组件都有一个boolean值,值只有0或1(即true 和 false)。用非负的整数将BitSet的位编入索引,可以对每个编入索引的位进行测试、设置或者清除。通过逻辑与、逻辑或和逻辑异或操作,可以使用一个 BitSet修改另一个 BitSet的内容。
默认情况下,set 中所有位的初始值都是false。
BitSet内部维护一个long数组,默认大小是64位,初始化只有一个long segement,所以BitSet最小的size是64。如果你要用的位超过了默认size,它会再申请64位,而不是报错。随着存储的元素越来越多,BitSet内部会自动扩充,一次扩充64位,最终内部是由N个long segement 来存储。
下面是BitSet类的简单使用:
BitSet bitSet = new BitSet(16); // 使用long数组作为内部存储结构,64位
// 申请的位都是以64为倍数的,申请不超过64按64算,超过64按128算,大小可动态改变
bitSet.set(1);
bitSet.set(3);
bitSet.set(11);
System.out.println(bitSet.length()); // 最高位为1到最低位的长度
for (int i = 0; i < bitSet.size(); i++) { // 实际使用空间的位数
if (bitSet.get(i) == true) // 每一位相当于boolean类型
System.out.print("1");
else
System.out.print("0");
}
运行结果:
12
0101000000010000000000000000000000000000000000000000000000000000
在后面的DES实现中我们会用到下面两个函数
/**
* BitSet ---> long
* BitSet使用long数组作为内部存储结构,大小可动态改变
* @param bitSet
* @return
*/
public static long BitSetToLong(BitSet bitSet) {
return bitSet.toLongArray()[0];
}
/**
* long ---> BitSet
* @param num
* @return
*/
public static BitSet LongToBitSet(long num) {
return BitSet.valueOf(new long[] {num});
}
BitSet类可用于对海量数据的操作。
更多关于BitSet类的介绍可以参考下面博主的文章
Java BitSet使用场景和示例:https://www.cnblogs.com/xupengzhang/p/7966755.html
JavaBitSet学习:https://www.cnblogs.com/xujian2014/p/5491286.html#_label2
java中的BitSet学习:https://blog.csdn.net/feihong247/article/details/7849317
二 、DES的实现
基本参数:
- 分组长度:64 bit
- 密钥长度:64 bit
- 有效密钥长度:56 bit(其中8 bit用于校验)
- 迭代圈数:16 圈
- 每圈子密钥长度:48 bit
从上面的参数我们可以看到,用BitSet类和long很容易实现DES。
先看一下DES的流程图,仔细看DES其实不难实现。
1.初始置换IP(initial permutation)
public static final int 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 };
将输入的 64 位明文重新进行排序,即将第 58 位放到第 1 位,第 50 位放到第 2 位……以此类推。初始置换以后得到的是一个 64 位的输出。
/**
* 初始置换
* @param plaintext 64位明文
* @return 64位乱序序列
*/
public static long Initial_Permutation(long plaintext) {
BitSet temp=LongToBitSet(plaintext);
BitSet bitSet=new BitSet();
for(int i=63;i>=0;i--) { //数组的第0位是long数据的最右边的第一位,即最低位
bitSet.set(i, temp.get(64-IP[63-i]));
}
return BitSetToLong(bitSet);
}
2.生成子密钥 Ki(key generation)
子密钥生成的流程图如下:
- 用户输出的密钥是 64 位的,根据密钥置换表PC-1,将 64 位变成 56 位密钥。(去掉了奇偶校验位)
- 将 PC-1 置换得到的 56 位密钥,分为前28位 C0 和后28位 D0,分别对它们进行循环左移,C0左移得到 C1,D0 左移得到 D1。
- 将 C1 和 D1 合并成 56 位,然后通过PC-2表进行压缩置换,得到当前这一轮的 48 位子密钥 K1 。
- 然后对 C1 和 D1 进行左移和压缩置换,获取下一轮的子密钥……一共进行16轮,得到 16 个 48 位的子密钥。
这里用到了表PC-1、PC-2:
public static final int PC_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 };
public static final int PC_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 };
/**
* 密钥置换 PC-1
* @param key 64位密钥
* @return 56位密钥
*/
public static BitSet Permutation_Cipher_1(long key) {
BitSet temp=LongToBitSet(key);
BitSet bitSet=new BitSet();
for(int i=63;i>=8;i--) {
bitSet.set(i,temp.get(64-PC_1[63-i]));
}
return bitSet;
}
/**
* 密钥置换 PC-2
* @param temp 56位密钥
* @return 48位密钥
*/
public static long Permutation_Cipher_2(BitSet temp) {
long subkey=0;
BitSet bitSet=new BitSet();
for(int i=63;i>=16;i--) {
bitSet.set(i,temp.get(64-PC_2[63-i]));
}
subkey=BitSetToLong(bitSet)>>>16; //无符号右移16位
return subkey;
}
当我们加密时所用到的子密钥需要每轮左移56位密钥若干位,生成子密钥时,左右部分分别一共左移16次(28位),加密前和加密后的56位密钥相同:
public static final int shiftBits[] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
/**
* 循环左移
* 左右部分分别一共左移16次、28位,加密前和加密后的56位密钥相同
* @param bitSet 56位密钥
* @param round 加密的圈数
* @return 56位左移后的密钥
*/
public static BitSet LeftShift(BitSet bitSet,int round) {
boolean temp=false;
for(int i=0;i<shiftBits[round];i++) {
temp=bitSet.get(63);
for(int j=63;j>=36;j--) { //左半部分
bitSet.set(j,bitSet.get(j-1));
}
bitSet.set(36,temp);
temp=bitSet.get(35);
for(int j=35;j>=8;j--) { //右半部分
bitSet.set(j,bitSet.get(j-1));
}
bitSet.set(8,temp);
}
return bitSet;
}
加密和解密可以使用相同的算法,DES算法解密过程是加密的逆运算。加密和解密唯一不同的是秘钥的次序是相反的。就是说如果每一轮的加密秘钥分别是K1、K2、K3…K16,那么解密秘钥就是K16、K15、K14…K1。加密是秘钥循环左移,解密是秘钥循环右移。
解密秘钥每次移动的位数是:0、1、2、2、2、2、2、2、1、2、2、2、2、2、2、1。
/**
* 循环右移
* @param bitSet 56位密钥
* @param round 加密的圈数
* @return 56位左移后的密钥
*/
public static BitSet RightShift(BitSet bitSet,int round) {
boolean temp=false;
for(int i=0;i<shiftBits[round];i++) {
temp=bitSet.get(8);
for(int j=8;j<36;j++) { //左半部分
bitSet.set(j,bitSet.get(j+1));
}
bitSet.set(35,temp);
temp=bitSet.get(36);
for(int j=36;j<64;j++) { //右半部分
bitSet.set(j,bitSet.get(j+1));
}
bitSet.set(63,temp);
}
return bitSet;
}
/**
* 子密钥生成
* @param bitSet 56位密钥
* @return 48位密钥
*/
public static long Subkey_Generation(BitSet bitSet) {
return Permutation_Cipher_2(bitSet);
}
3.轮函数 f(R,Ki)
f(R,Ki)函数的流程图如下:
密码函数f(R,Ki)接受两个输入:32 位的数据和 48 位的子密钥。然后:
- 通过表 E 进行扩展置换,将输入的 32 位数据扩展为 48 位;
- 将扩展后的 48 位数据与 48 位的子密钥进行异或运算;
- 将异或得到的 48 位数据分成 8 个 6 位的块,每一个块通过对应的一个 S 表产生一个 4 位的输出。其中,每个 S 表都是 4 行 16 列。具体的置换过程如下:A1×2+A6×1 作为 Si 表中的行数(0–3),A2×8+A3×4+A4×2+A5×1 作为 Si 表的列数(0–15)。查出 Si表中行列所对应的整数,将该整数转换为一个 4 位的二进制数。
- 把通过 S 表置换得到的 8 个 4 位连在一起,形成一个 32 位的数据。然后将该 32 位数据通过表 P 进行置换(称为P-置换),置换后得到一个仍然是 32 位的结果数据,这就是f(R,Ki)函数的输出。
public static final int 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 };
public static final int S_BOX[][][] = {
{ {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} } };
public static final int 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 };
/**
* f函数-E盒扩展置换
* @param R 数据的右32位
* @return 48位数据
*/
public static long E_Permutation(long R) {
BitSet bitSet=LongToBitSet(R);
BitSet bitSet2=new BitSet();
for(int i=47;i>=0;i--) {
bitSet2.set(i,bitSet.get(32-E[47-i]));
}
return BitSetToLong(bitSet2);
}
/**
* f函数-S盒压缩
* @param num 48位数据
* @return 32位数据
*/
public static long S_Box_Compression(long num) {
long result=0;
BitSet bitSet=LongToBitSet(num);
for(int i=0,j=47;i<8;i++,j-=6) {
int a,b,c,d,e,f;
if(bitSet.get(j)==true) a=1; else a=0;
if(bitSet.get(j-1)==true) b=1; else b=0;
if(bitSet.get(j-2)==true) c=1; else c=0;
if(bitSet.get(j-3)==true) d=1; else d=0;
if(bitSet.get(j-4)==true) e=1; else e=0;
if(bitSet.get(j-5)==true) f=1; else f=0;
result=(result<<=4)|(S_BOX[i][a*2+f][b*8+c*4+d*2+e]);
}
return result;
}
/**
* f函数-P盒置换
* @param num 32位数据
* @return 32位数据
*/
public static long P_Permutation(long num) {
BitSet temp=LongToBitSet(num);
BitSet bitSet=new BitSet();
for(int i=31;i>=0;i--) {
bitSet.set(i,temp.get(32-P[31-i]));
}
return BitSetToLong(bitSet);
}
/**
* f函数
* @param R 数据的右32位
* @param subkey 48位密钥
* @return 32位数据
*/
public static long f(long R,long subkey) {
return P_Permutation(S_Box_Compression(E_Permutation(R)^subkey));
}
4.末置换
public static final int 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 };
/**
* 末置换
* @param num 64位数据
* @return 64位数据
*/
public static long Final_Initial_Permutation(long num) {
BitSet temp=LongToBitSet(num);
BitSet bitSet=new BitSet();
for(int i=63;i>=0;i--) {
bitSet.set(i, temp.get(64-IP_1[63-i]));
}
return BitSetToLong(bitSet);
}
5.加密与解密
/**
* 加密
* @param plaintext 64位明文
* @param key 64位密钥
* @return 64位密文
*/
public static long encrypt(long plaintext,long key) {
plaintext=Initial_Permutation(plaintext); //初始置换
long ciphertext=0,temp=0;
long L=(plaintext&0xFFFFFFFF00000000L)>>>32; //数据的左32位
long R=plaintext&0x00000000FFFFFFFFL; //数据的右32位
long subkey=0;
BitSet bitSet=Permutation_Cipher_1(key); //密钥置换 PC-1
for(int i=0;i<16;i++) {
bitSet=LeftShift(bitSet,i); //循环左移shiftBits[i]位
subkey=Subkey_Generation(bitSet); //子密钥生成
temp=R;
R=L^f(R, subkey);
L=temp;
//System.out.println("第"+i+"轮 L= "+L+" R= "+R+" subkey= "+subkey);
}
ciphertext=Final_Initial_Permutation((R<<32)|L);//左右交换,末置换
return ciphertext;
}
/**
* 解密
* @param ciphertext 64位密文
* @param key 64位密钥
* @return 64位明文
*/
public static long decrypt(long ciphertext,long key) {
ciphertext=Initial_Permutation(ciphertext);
long plaintext=0,temp=0;
long R=(ciphertext&0xFFFFFFFF00000000L)>>>32;
long L=ciphertext&0x00000000FFFFFFFFL;
long subkey=0;
BitSet bitSet=Permutation_Cipher_1(key);
for(int i=15;i>=0;i--) {
subkey=Subkey_Generation(bitSet);
bitSet=RightShift(bitSet,i);
temp=L;
L=R^f(L, subkey);
R=temp;
//System.out.println("第"+i+"轮 L= "+L+" R= "+R+" subkey= "+subkey);
}
plaintext=Final_Initial_Permutation((L<<32)|R);
return plaintext;
}
下面可以看出DES的对称性:
明文= 12345678
密钥= 12345678
第0轮 L= 16744550 R= 2176033604 subkey= 88152800176834
第1轮 L= 2176033604 R= 1178814781 subkey= 88702421345095
第2轮 L= 1178814781 R= 4261055420 subkey= 229437806642316
第3轮 L= 4261055420 R= 2805545884 subkey= 247004211460043
第4轮 L= 2805545884 R= 2798241892 subkey= 246935491375145
第5轮 L= 2798241892 R= 174267677 subkey= 246919588896098
第6轮 L= 174267677 R= 1824122588 subkey= 181223771908410
第7轮 L= 1824122588 R= 3532822421 subkey= 182876803259984
第8轮 L= 3532822421 R= 577220743 subkey= 42139329993280
第9轮 L= 577220743 R= 133981690 subkey= 52026311493436
第10轮 L= 133981690 R= 2911133244 subkey= 16775489592972
第11轮 L= 2911133244 R= 1702589147 subkey= 34366614433969
第12轮 L= 1702589147 R= 3260427994 subkey= 34125815966253
第13轮 L= 3260427994 R= 2266627553 subkey= 29860989909394
第14轮 L= 2266627553 R= 3943749637 subkey= 27679128879927
第15轮 L= 3943749637 R= 842228128 subkey= 89251780182976
第15轮 L= 2266627553 R= 3943749637 subkey= 89251780182976
第14轮 L= 3260427994 R= 2266627553 subkey= 27679128879927
第13轮 L= 1702589147 R= 3260427994 subkey= 29860989909394
第12轮 L= 2911133244 R= 1702589147 subkey= 34125815966253
第11轮 L= 133981690 R= 2911133244 subkey= 34366614433969
第10轮 L= 577220743 R= 133981690 subkey= 16775489592972
第9轮 L= 3532822421 R= 577220743 subkey= 52026311493436
第8轮 L= 1824122588 R= 3532822421 subkey= 42139329993280
第7轮 L= 174267677 R= 1824122588 subkey= 182876803259984
第6轮 L= 2798241892 R= 174267677 subkey= 181223771908410
第5轮 L= 2805545884 R= 2798241892 subkey= 246919588896098
第4轮 L= 4261055420 R= 2805545884 subkey= 246935491375145
第3轮 L= 1178814781 R= 4261055420 subkey= 247004211460043
第2轮 L= 2176033604 R= 1178814781 subkey= 229437806642316
第1轮 L= 16744550 R= 2176033604 subkey= 88702421345095
第0轮 L= 16742485 R= 16744550 subkey= 88152800176834
明文= 12345678
运行结果:
加密过程如下:
明文(String)= Computer
明文(Decimal)= 4859222852730381682
明文(HEX)= 436F6D7075746572
密钥(String)= software
密钥(Decimal)= 8317979687181709925
密钥(HEX)= 736F667477617265
密文(Decimal)= 1970734137907446511
密文(HEX)= 1B597441CCB582EF
密文(Base64)= MWI1OTc0NDFjY2I1ODJlZg==
------发送base64编码的密文------
解密过程如下:
密文(Base64)= MWI1OTc0NDFjY2I1ODJlZg==
密文(HEX)= 1B597441CCB582EF
密文(Decimal)= 1970734137907446511
密钥(HEX)= 736F667477617265
密钥(Decimal)= 8317979687181709925
密钥(String)= software
明文(HEX)= 436F6D7075746572
明文(Decimal)= 4859222852730381682
明文(String)= Computer
三、源码下载
由于时间问题,该DES的实现只能加密64bit的数据。至于加密任意长度的数据,其实只要分组就行了,每组64bit,不足64bit的后面补0,最后将加密的结果合起来就行了。解密也是分组解密。
同时,该DES的实现只能加密并解密ASCII字符。但如果想实现加密并解密中文字符的话,可以先将字符串转为字节,加密并解密,最后注意一下编码的问题。
参考:
DES加密算法的C++实现:https://blog.csdn.net/lisonglisonglisong/article/details/41777413
Data Encryption Standard:https://www.tutorialspoint.com/cryptography/data_encryption_standard.htm
证明:DES算法解密过程是加密的逆运算:https://www.jianshu.com/p/559bd0fc97c5