DES算法
ES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来。
基本原则:
DES设计中使用了分组密码设计的两个原则:混淆(confusion)和扩散(diffusion),其目的是抗击敌手对密码系统的统计分析。混淆是使密文的统计特性与密钥的取值之间的关系尽可能复杂化,以使密钥和明文以及密文之间的依赖性对密码分析者来说是无法利用的。扩散的作用就是将每一位明文的影响尽可能迅速地作用到较多的输出密文位中,以便在大量的密文中消除明文的统计结构,并且使每一位密钥的影响尽可能迅速地扩展到较多的密文位中,以防对密钥进行逐段破译。
加密流程:
(1)输入64位明文数据,并进行初始置换IP;
(2)在初始置换IP后,明文数据再被分为左右两部分,每部分32位,以L0,R0表示;
(3)在秘钥的控制下,经过16轮运算(f);
(4)16轮后,左、右两部分交换,并连接再一起,再进行逆置换;
(5)输出64位密文。
算法特点:
(1)分组加密算法:以64位为分组。64位明文输入,64位密文输出。
(2)对称算法:加密和解密使用同一秘钥。
(3)有效秘钥长度为56位:秘钥通常表示为64位数,但每个第8位用作奇偶校验,可以忽略。
(4)代替和置换:DES算法是两种加密技术的组合:混乱和扩散。先替代后置换。
(5)易于实现:DES算法只是使用了标准的算术和逻辑运算,其作用的数最多也只有64 位,因此用70年代末期的硬件技术很容易实现,算法的重复特性使得它可以非常理想地用在一个专用芯片中。
实现流程(JAVA):
初始IP置换:
/**
* 起始置换,根据置换表,将明文进行置换,记得置换表的数字要减1
*
* @param plainText
* @return
*/
public String innitialPermutation(String plainText) {
char[] plainTextCh = plainText.toCharArray();
char[] permutation = new char[64];
for (int i = 0; i < 64; i++) {
permutation[i] = plainTextCh[innitial[i] - 1];
}
String initPlaintext = String.valueOf(permutation);
return initPlaintext;
}
轮函数:
- 将IP置换后的64bit分为L与R两部分,每部分32bit,进行16轮运算,在轮函数中,需要进行E扩展、将32bit扩展为48bit,接着再将其与每一轮的密钥(48位)进行异或,再经过S盒进行压缩,最终再进行P盒置换。
E扩展:
/**
* 扩展置换E,同样的,记得置换表里面的数字-1
*
* @param Ri 传入Ri
* @return
*/
public String expandChange(String Ri) {
char[] RiCh = Ri.toCharArray();
char[] exRiCh = new char[48];
for (int i = 0; i < 48; i++) {
exRiCh[i] = RiCh[expandTbE[i] - 1];
}
String ERi = String.valueOf(exRiCh);
return ERi;
}
S盒:
/**
* S盒运算,48位得到对照表得到32位
*
* @param oxrRes 异或运算得到的结果
* @return
*/
public String sboxCul(String oxrRes) {
StringBuilder sb = new StringBuilder();
String column = "";
String row = "";
int colNum = 0;
int rowNum = 0;
int boxNum = 0;
String res = "";
for (int i = 0; i < 8; i++) {
row = oxrRes.substring(6*i,6*i+1) + oxrRes.substring(6*i+5,6*i+6);
column = oxrRes.substring(6*i+1,6*i+5);
colNum = Integer.parseUnsignedInt(column, 2); //列
rowNum = Integer.parseUnsignedInt(row, 2); //行
boxNum = s[i][rowNum * 8 + colNum];
//补0
res = Integer.toBinaryString(boxNum);
if (res.length() != 4) {
for (int k = res.length(); k < 4; k++) {
res = "0" + res;
}
}
sb.append(res);
}
String sboxRes = sb.toString();
return sboxRes;
}
P盒置换:
/**
* P置换
*/
public String pboxCul(String sboxRes){
char[] sboxResCh = sboxRes.toCharArray();
char[] pboxResCh = new char[32];
for(int i = 0;i<32;i++){
pboxResCh[i] = sboxResCh[pbox[i]-1];
}
String pboxRes = String.valueOf(pboxResCh);
return pboxRes;
}
IP逆置换:
/**
* 最终逆置换
*
* @param plainText
* @return
*/
public String EndPermutation(String plainText) {
char[] plainTextCh = plainText.toCharArray();
char[] permutation = new char[64];
for (int i = 0; i < 64; i++) {
permutation[i] = plainTextCh[end[i] - 1];
}
String initPlaintext = String.valueOf(permutation);
return initPlaintext;
}
解密流程:
完整代码:
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
/**
* @author the best
* @version 1.0
* @date 2022/8/9 21:31
*/
public class DES {
//起始置换表 长度8X8
public int[] innitial = new int[]{
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
};
//扩展置换表E 长度6X8
public int[] expandTbE = new int[]{
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
};
//生成子密钥 pc1表 长度7X8
public int[] pc1 = new int[]{
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
};
//生成子秘钥 pc2表 长度6X8
public int[] pc2 = new int[]{
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
};
//生成子秘钥 循环左移轮次表 长度 16
public int[] leftRoundTable = new int[]{
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
//S1盒
public int[][] s = new int[][]{
{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}
};
//P盒 长度为32
public int[] pbox = new int[]{
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
};
//逆置置换表
public int[] end = new int[]{
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
};
/**
* 起始置换,根据置换表,将明文进行置换,记得置换表的数字要减1
*
* @param plainText
* @return
*/
public String innitialPermutation(String plainText) {
char[] plainTextCh = plainText.toCharArray();
char[] permutation = new char[64];
for (int i = 0; i < 64; i++) {
permutation[i] = plainTextCh[innitial[i] - 1];
}
String initPlaintext = String.valueOf(permutation);
return initPlaintext;
}
/**
* 最终逆置换
*
* @param plainText
* @return
*/
public String EndPermutation(String plainText) {
char[] plainTextCh = plainText.toCharArray();
char[] permutation = new char[64];
for (int i = 0; i < 64; i++) {
permutation[i] = plainTextCh[end[i] - 1];
}
String initPlaintext = String.valueOf(permutation);
return initPlaintext;
}
/**
* S盒运算,48位得到对照表得到32位
*
* @param oxrRes 异或运算得到的结果
* @return
*/
public String sboxCul(String oxrRes) {
StringBuilder sb = new StringBuilder();
String column = "";
String row = "";
int colNum = 0;
int rowNum = 0;
int boxNum = 0;
String res = "";
for (int i = 0; i < 8; i++) {
row = oxrRes.substring(6*i,6*i+1) + oxrRes.substring(6*i+5,6*i+6);
column = oxrRes.substring(6*i+1,6*i+5);
colNum = Integer.parseUnsignedInt(column, 2); //列
rowNum = Integer.parseUnsignedInt(row, 2); //行
boxNum = s[i][rowNum * 8 + colNum];
//补0
res = Integer.toBinaryString(boxNum);
if (res.length() != 4) {
for (int k = res.length(); k < 4; k++) {
res = "0" + res;
}
}
sb.append(res);
}
String sboxRes = sb.toString();
return sboxRes;
}
/**
* 扩展置换E,同样的,记得置换表里面的数字-1
*
* @param Ri 传入Ri
* @return
*/
public String expandChange(String Ri) {
char[] RiCh = Ri.toCharArray();
char[] exRiCh = new char[48];
for (int i = 0; i < 48; i++) {
exRiCh[i] = RiCh[expandTbE[i] - 1];
}
String ERi = String.valueOf(exRiCh);
return ERi;
}
/**
* DES加密,生成子密钥,返回第X轮次的子秘钥。<br/> 因为字符串都是从0开始,所以pc里面的数字都要减1
*
* @param key 秘钥的字符串
* @param round 生成子秘钥的轮次
* @return
*/
public String roundKeyGenerate(String key, int round) {
char[] keyCh = key.toCharArray();
char[] generateOne = new char[56];
for (int i = 0; i < 56; i++) {
generateOne[i] = keyCh[pc1[i] - 1];
}
key = String.valueOf(generateOne);
String c0 = key.substring(0, 28);
String d0 = key.substring(28, 56);
int bitCount = leftRoundTable[round];
c0 = c0.substring(0 + bitCount, 28) + c0.substring(0, bitCount);
d0 = d0.substring(0 + bitCount, 28) + d0.substring(0, bitCount);
String subKey = c0 + d0;
char[] subKeyCh = subKey.toCharArray();
char[] generateTwo = new char[48];
for (int i = 0; i < 48; i++) {
generateTwo[i] = subKeyCh[pc2[i] - 1];
}
subKey = String.valueOf(generateTwo);
return subKey;
}
/**
* 异或运算
*
* @param
* @param
* @return
*/
public String xorCul(String ERi, String Ki,Integer num) {
char[] ERiCh = ERi.toCharArray();
char[] KiCh = Ki.toCharArray();
char[] resCh = new char[num];
for (int i = 0; i < num; i++) {
if (ERiCh[i] == KiCh[i]) {
resCh[i] = '0';
} else {
resCh[i] = '1';
}
}
String result = String.valueOf(resCh);
return result;
}
/**
* P置换
*/
public String pboxCul(String sboxRes){
char[] sboxResCh = sboxRes.toCharArray();
char[] pboxResCh = new char[32];
for(int i = 0;i<32;i++){
pboxResCh[i] = sboxResCh[pbox[i]-1];
}
String pboxRes = String.valueOf(pboxResCh);
return pboxRes;
}
/**
* 轮函数
*
* @return
*/
public String f(String R, String k,int round) {
//E扩展 将32位扩展为48位
String s = expandChange(R);
// System.out.println("E After the extension ="+s);
//与密钥K进行异或运算
String k1 = roundKeyGenerate(k,round);
// System.out.println("The key generated in each round ="+k1);
String s2 = xorCul(s,k1,48);
//System.out.println("And the key K xor after ="+s2);
//S盒压缩,将48位压缩为32位
String s3 = sboxCul(s2);
// System.out.println("S-box compressed to 32 bits ="+s3);
//P置换
String s4 = pboxCul(s3);
return s4;
}
/**
* 加密算法
* @param
*/
public String encrypt(String plainText,String key){
DES des = new DES();
String s1 = des.changeString(plainText);
String[] strings = des.spiltString(s1);
StringBuffer sb = new StringBuffer();
key = des.changekey(key);
for(int j=0;j<strings.length;j++){
//P置换
String s = des.innitialPermutation(strings[j]);
//System.out.println("Initial P permutation ="+s);
//将字符串分为L和R
String L = s.substring(0, 32);
String R = s.substring(32, 64);
for(int i=0;i<16;i++){
String temp = R;
R = des.xorCul(des.f(R, key,i), L,32);
L=temp;
}
String data = R+L;
String ciphertext = des.EndPermutation(data);
sb.append(ciphertext);
}
System.out.println("ciphertext ="+sb);
return sb.toString();
}
/**
* 解密算法1
* @param
*/
public String decrypt(String ciphertext,String key){
DES des = new DES();
String[] strings = des.spiltString(ciphertext);
StringBuffer sb = new StringBuffer();
key = des.changekey(key);
for(int j=0;j<strings.length;j++){
//P置换
String s = des.innitialPermutation(strings[j]);
//将字符串分为L和R
String L = s.substring(0, 32);
String R = s.substring(32, 64);
for(int i=15;i>=0;i--){
String temp = R;
R = des.xorCul(des.f(R, key,i), L,32);
L=temp;
}
String data = R+L;
String plainText = des.EndPermutation(data);
sb.append(plainText);
}
String abcd = new String(des.string2bytes(sb.toString().replace("00000000", "")));
System.out.println("plainText ="+abcd);
return abcd;
}
/**
* 把byte转化成2进制字符串
* @param b
* @return
*/
public static String getBinaryStrFromByte(byte b) {
String result = "";
byte a = b;
;
for (int i = 0; i < 8; i++) {
byte c = a;
a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。
a = (byte) (a << 1);
if (a == c) {
result = "0" + result;
} else {
result = "1" + result;
}
a = (byte) (a >> 1);
}
return result;
}
/**
* 二制度字符串转字节数组,如 101000000100100101110000 -> A0 09 70
* @param input 输入字符串。
* @return 转换好的字节数组。
*/
static byte[] string2bytes(String input) {
StringBuilder in = new StringBuilder(input);
// 注:这里in.length() 不可在for循环内调用,因为长度在变化
int remainder = in.length() % 8;
if (remainder > 0)
for (int i = 0; i < 8 - remainder; i++)
in.append("0");
byte[] bts = new byte[in.length() / 8];
// Step 8 Apply compression
for (int i = 0; i < bts.length; i++)
bts[i] = (byte) Integer.parseInt(in.substring(i * 8, i * 8 + 8), 2);
return bts;
}
/**
* 将字符串转为二进制
* @param
*/
public String changeString(String temp){
byte[] bytes = temp.getBytes(StandardCharsets.UTF_8);
DES des = new DES();
StringBuffer sb = new StringBuffer();
for(int i=0;i<bytes.length;i++){
sb.append(des.getBinaryStrFromByte(bytes[i]));
}
return sb.toString();
}
/**
* 将字符串按64位分割,不足补0
* @param
*/
public String[] spiltString(String temp){
int n = (temp.length() + 64 - 1) / 64; //获取整个字符串可以被切割成字符子串的个数
String[] split = new String[n];
for (int i = 0; i < n; i++) {
if (i < (n - 1)) {
split[i] = temp.substring(i * 64, (i + 1) * 64);
} else {
split[i] = temp.substring(i * 64);
while (split[i].length()<64){
split[i]+="0";
}
}
}
return split;
}
/**
* 将密钥转成64bit,不足补0
* @param
*/
public String changekey(String key){
byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
DES des = new DES();
StringBuffer sb = new StringBuffer();
for(int i=0;i<bytes.length;i++){
sb.append(des.getBinaryStrFromByte(bytes[i]));
}
while (sb.length()<64){
sb.append("0");
}
return sb.toString();
}
public static void main(String[] args) {
String plainText = "ABCDEFG123456";
System.out.println("plainText="+plainText);
String key = "QWE";
System.out.println("key="+key);
DES des = new DES();
String encrypt = des.encrypt(plainText, key);
des.decrypt(encrypt,key);
}
}
总结:
加密:
首先需要将明文字符串转为字节数组,再将字节数组转为二进制字符串,按64位分割(不足位补0),通过DES算法进行加密,将加密完成的字符串拼接成为密文。
解密:
将密文进行按64位进行分割,分别进行解密,将解密之后的字符串去除补位的0,再将字符串转为字节数组,最终转为明文。