Simplified-DES的实现
题目描述
2.1实验目的
1) 学习S-DES密码算法的原理
2) 掌握S-DES密码算法的实现
2.2实验原理
1) 算法原理
Simplified DES方案,简称S-DES方案,是DES算法的简化版。它是一个供教学而非安全的加密算法,它与DES的特性和结构类似,但参数小。
加密算法涉及五个函数:
(1)初始置换IP(initial permutation)
(2)复合函数fk1,它是由密钥K确定的,具有置换和代换的运算。
(3)置换函数SW
(4)复合函数fk2
(5)初始置换IP的逆置换IP-1
2) 算法参数——S-DES所需的几个置换表
① P10={3,5,2,7,4,10,1,9,8,6}
② P8={6,3,7,4,8,5,10,9} 注意这个置换选择输入10位输出8位
③ P4={2,4,3,1}
④ IP={2,6,3,1,4,8,5,7}
⑤ IP-1={4,1,3,5,7,2,8,6}
⑥ EP={4,1,2,3,2,3,4,1} 注意这个是扩展置换,输入4位输出8位
⑦ 两个S盒:
S0:
{1,0,3,2}
{3,2,1,0}
{0,2,1,3}
{3,1,3,2}
S1:
{0,1,2,3}
{2,0,1,3}
{3,0,1,0}
{2,1,0,3}
3) 算法说明
若明文为: m=0001 0110, key选为(01111 11101),给出加密过程和解密过程,并计算出密文和明文。
a)子密钥的生成
① 10位密钥key = 01111 11101
② 对key做P10置换(P10={3,5,2,7,4,10,1,9,8,6})得到 11111 10011
③ 记左半(高位)的为Lk=11111,右半(低位)为Rk=10011
④ LS-1:Lk和Rk均循环左移1位,得到Lk=11111,Rk=00111
⑤ 对Lk和Rk组合得到的11111 00111做P8置换(P8={6,3,7,4,8,5,10,9})选择,得到子密钥K1=0101 1111
⑥ LS-2:Lk和Rk均再次循环左移2位,得到Lk=11111,Rk=11100
⑦ P8:对Lk和Rk组合得到的11111 11100做P8置换选择,得到子密钥K2=1111 1100
以上,通过密钥得到了算法所需的子密钥。
b)加密过程
首先是初始置换
① 对明文m=0001 0110做IP置换(IP={2,6,3,1,4,8,5,7}),得m’=0100 1001
接下来是标准的Feistel密码结构,共有两次循环。
第一次循环
② 记左半(高位)为Lm=0100,右半(低位)为Rm=1001
③ 对Rm做EP扩展置换(EP={4,1,2,3,2,3,4,1}),得Rm’=1100 0011
④ Rm’与子密钥K1按位异或(K1=0101 1111),得Rm’=1001 1100
⑤ Rm’左半1001(A1A2A3A4)进入S0盒替代选择得11(第4行A1A4 /第1列A2A3的数字3,再转成二进制11,A1A4中A1是数位高位),右半1100进入S1盒替代选择的01(第3行10/第3列10的数字1),组合后得Rm’=1101
⑥ 对Rm’做P4置换(P4={2,4,3,1}),得Rm’=1101
⑦ Rm’与Lm按位异或,得Lm’=1001
⑧ Lm’与Rm(最开始的那个Rm)组合得到输出 1001(Lm’) 1001(Rm)
至此完成第一次循环。
⑨ 然后交换高低位,作为第二次循环的输入,即1001(Rm)1001(Lm’)作为输入
开始第二次循环
⑩ 记左半为Ln=1001,右半为Rn=1001
11 对Rn做EP扩展置换(EP={4,1,2,3,2,3,4,1}),得Rn’=1100 0011
12 Rn’与子密钥K2按位异或(K2=1111 1100),得Rn’=0011 1111
13 Rn’左半0011进入S0盒替代选择得10(第2行01/第2列01的数字2,再转成二进制10),右半1111进入S1盒替代选择的11(第4行11/第4列11的数字3,再转成二进制11),组合后得Rn’=1011
14 对Rn’做P4置换(P4={2,4,3,1}),得Rn’=0111
15 Rn’与Ln按位异或,得Ln’=1110
16 Ln’与Rn(最开始的那个Rn)组合得到输出 1110(Ln’) 1001(Rn)
至此完成第二次循环
17 最后进行逆初始置换对上面的输出m’=1110 1001做IP-1置换得到密文m’=0111 0110.
OK,到这里就完成了将明文加密为密文,S-DES加密结束。
c)解密过程
解密过程与加密基本一致,就是密钥使用顺序是相反的,第一次循环使用K2第二次循环使用K1。
首先还是初始置换
① 对密文m=0111 0110做IP置换(IP={2,6,3,1,4,8,5,7}),得m’=1110 1001
Feistel密码结构
第一次循环
② 记左半(高位)为Lm=1110,右半(低位)为Rm=1001
③ 对Rm做EP扩展置换(EP={4,1,2,3,2,3,4,1}),得Rm’=1100 0011
④ Rm’与子密钥K2按位异或(K2=1111 1100),得Rm’=0011 1111
⑤ Rm’左半0011进入S0盒替代选择得10(第2行01/第2列01的数字2,再转成二进制10),右半1111进入S1盒替代选择的11(第4行11/第4列11的数字3,再转成二进制11),组合后得Rm’=1011
⑥ 对Rm’做P4置换(P4={2,4,3,1}),得Rm’=0111
⑦ Rm’与Lm按位异或,得Lm’=1001
⑧ Lm’与Rm(最开始的那个Rm)组合得到输出 1001(Lm’) 1001(Rm)
至此完成第一次循环。
⑨ 然后交换高低位,作为第二次循环的输入,即1001(Rm) 1001(Lm’)作为输入
开始第二次循环
⑩ 记左半为Ln=1001,右半为Rn=1001
11 对Rn做EP扩展置换(EP={4,1,2,3,2,3,4,1}),得Rn’=1100 0011
12 Rn’与子密钥K1按位异或(K1=0101 1111),得Rn’=1001 1100
13 Rn’左半1001进入S0盒替代选择得11(第4行11/第1列00的数字3,再转成二进制11),右半1100进入S1盒替代选择的01(第3行10/第3列10的数字1),组合后得Rn’=1101
14 对Rn’做P4置换(P4={2,4,3,1}),得Rn’=1101
15 Rn’与Ln按位异或,得Ln’=0100
16 Ln’与Rn(最开始的那个Rn)组合得到输出0100(Ln’) 1001(Rn)
至此完成第二次循环。
17 最后进行逆初始置换
18 对上面的输出m’=0100 1001做IP-1置换(IP-1={4,1,3,5,7,2,8,6})得到明文m’=0001 0110.
这样就完成的S-DES的解密。
输入
第一行输入主密钥
第二行输入明文
输入样例1:
1100011110
00101000
0111111101
00010110
输出
输出密文
输出样例1
10001010
01110110
AC代码
(虽然看着很长,但是注释占了快一半),核心逻辑的函数都封装好了,实现起来很简单。
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* Simplified-DES的实现
*
* @author xzx
* @date 2022/11/17
*/
public class Main {
/**
* 算法参数——S-DES所需的几个置换表
*/
static final int[] P10 = new int[]{3, 5, 2, 7, 4, 10, 1, 9, 8, 6};
static final int[] P8 = new int[]{6, 3, 7, 4, 8, 5, 10, 9};
static final int[] P4 = new int[]{2, 4, 3, 1};
static final int[] IP = new int[]{2, 6, 3, 1, 4, 8, 5, 7};
static final int[] IP_1 = new int[]{4, 1, 3, 5, 7, 2, 8, 6};
static final int[] EP = new int[]{4, 1, 2, 3, 2, 3, 4, 1};
static final int[][] S0 = new int[][]{
{1, 0, 3, 2},
{3, 2, 1, 0},
{0, 2, 1, 3},
{3, 1, 3, 2},
};
static final int[][] S1 = new int[][]{
{0, 1, 2, 3},
{2, 0, 1, 3},
{3, 0, 1, 0},
{2, 1, 0, 3},
};
public static void main(String[] args) {
String m, key;
// m = "00010110";
// key = "0111111101";
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
key = scanner.next();
m = scanner.next();
// a)子密钥的生成
List<String> childKeys = generateChildKeys(key);
// b)加密过程
String encryptedText = encrypt(m, childKeys);
System.out.println(encryptedText);
// c) 解密过程
String decryptedText = decrypt(encryptedText, childKeys);
}
}
/**
* 解密
*
* @param encryptedText
* @param childKeys
* @return
*/
private static String decrypt(String encryptedText, List<String> childKeys) {
//解密过程与加密基本一致,就是密钥使用顺序是相反的,第一次循环使用K2第二次循环使用K1。
String tmp = childKeys.get(0);
childKeys.set(0, childKeys.get(1));
childKeys.set(1, tmp);
return encrypt(encryptedText, childKeys);
}
/**
* 加密
*
* @param m 明文
* @param childKeys 子密钥
* @return
*/
private static String encrypt(String m, List<String> childKeys) {
// 首先是初始置换
// ① 对明文m=0001 0110做IP置换(IP={2,6,3,1,4,8,5,7}),得m’=0100 1001
String m_ = SW(m, IP);
String res1 = loop(getLeft(m_), getRight(m_), childKeys.get(0));
// 接下来是标准的Feistel密码结构,共有两次循环。
// 第一次循环
// ② 记左半(高位)为Lm=0100,右半(低位)为Rm=1001
// ③ 对Rm做EP扩展置换(EP={4,1,2,3,2,3,4,1}),得Rm’=1100 0011
// ④ Rm’与子密钥K1按位异或(K1=0101 1111),得Rm’=1001 1100
// ⑤ Rm’左半1001(A1A2A3A4)进入S0盒替代选择得11(第4行A1A4 /第1列A2A3的数字3,再转成二进制11,A1A4中A1是数位高位),
// 右半1100进入S1盒替代选择的01(第3行10/第3列10的数字1),组合后得Rm’=1101
// ⑥ 对Rm’做P4置换(P4={2,4,3,1}),得Rm’=1101
// ⑦ Rm’与Lm按位异或,得Lm’=1001
// ⑧ Lm’与Rm(最开始的那个Rm)组合得到输出 1001(Lm’) 1001(Rm)
// 至此完成第一次循环。
// ⑨ 然后交换高低位,作为第二次循环的输入,即1001(Rm)1001(Lm’)作为输入
// 开始第二次循环
String res2 = loop(getRight(res1), getLeft(res1), childKeys.get(1));
// ⑩ 记左半为Ln=1001,右半为Rn=1001
// 11 对Rn做EP扩展置换(EP={4,1,2,3,2,3,4,1}),得Rn’=1100 0011
// 12 Rn’与子密钥K2按位异或(K2=1111 1100),得Rn’=0011 1111
// 13 Rn’左半0011进入S0盒替代选择得10(第2行01/第2列01的数字2,再转成二进制10),右半1111进入S1盒替代选择的11(第4行11/第4列11的数字3,再转成二进制11),组合后得Rn’=1011
// 14 对Rn’做P4置换(P4={2,4,3,1}),得Rn’=0111
// 15 Rn’与Ln按位异或,得Ln’=1110
// 16 Ln’与Rn(最开始的那个Rn)组合得到输出 1110(Ln’) 1001(Rn)
// 至此完成第二次循环
// 17 最后进行逆初始置换对上面的输出m’=1110 1001做IP-1置换得到密文m’=0111 0110.
return SW(res2, IP_1);
// OK,到这里就完成了将明文加密为密文,S-DES加密结束。
}
/**
* @param left
* @param right
* @param key
* @return
*/
private static String loop(String left, String right, String key) {
// 1.对右半做EP扩展置换
String r_ = SW(right, EP);
// 2.r_与密钥key按位异或
r_ = xor(r_, key);
// 3.r_左半进入S0,右半进入S1
r_ = boxSW(getLeft(r_), S0) + boxSW(getRight(r_), S1);
// 4. r_P4置换
r_ = SW(r_, P4);
// 5.l和r_异或
String l_ = xor(r_, left);
// 6.组合
return l_ + right;
}
/**
* 二维盒置换
*
* @param str
* @param s
* @return
*/
private static String boxSW(String str, int[][] s) {
// ⑤ Rm’左半1001(A1A2A3A4)进入S0盒替代选择得11(第4行A1A4 /第1列A2A3的数字3,再转成二进制11,A1A4中A1是数位高位),
// 右半1100进入S1盒替代选择的01(第3行10/第3列10的数字1),组合后得Rm’=1101
int row = getDEC(str.charAt(0), str.charAt(3));
int col = getDEC(str.charAt(1), str.charAt(2));
StringBuilder res = new StringBuilder(Integer.toBinaryString(s[row][col]));
while (res.length() < 2) {
res.insert(0, '0');
}
return res.toString();
}
/**
* 二进制转十进制
*
* @param a
* @param b
* @return
*/
private static int getDEC(char a, char b) {
String s = String.valueOf(a) + b;
return Integer.valueOf(s, 2);
}
/**
* 按位异或
*
* @param s1
* @param s2
* @return
*/
private static String xor(String s1, String s2) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < s1.length(); i++) {
stringBuilder.append(Character.getNumericValue(s1.charAt(i)) ^ Character.getNumericValue(s2.charAt(i)));
}
return stringBuilder.toString();
}
/**
* 生成子密钥
*
* @param key
* @return
*/
private static List<String> generateChildKeys(String key) {
// ① 10位密钥key = 01111 11101
// ② 对key做P10置换(P10={3,5,2,7,4,10,1,9,8,6})得到 11111 10011
key = SW(key, P10);
// ③ 记左半(高位)的为Lk=11111,右半(低位)为Rk=10011
// ④ LS-1:Lk和Rk均循环左移1位,得到Lk=11111,Rk=00111
String LK = move(getLeft(key), -1);
String RK = move(getRight(key), -1);
// ⑤ 对Lk和Rk组合得到的11111 00111做P8置换(P8={6,3,7,4,8,5,10,9})选择,得到子密钥K1=0101 1111
String K1 = SW(LK + RK, P8);
// LS-2:Lk和Rk均再次循环左移2位,得到Lk=11111,Rk=11100
LK = move(LK, -2);
RK = move(RK, -2);
// P8:对Lk和Rk组合得到的11111 11100做P8置换选择,得到子密钥K2=1111 1100
String K2 = SW(LK + RK, P8);
ArrayList<String> childKeys = new ArrayList<>();
childKeys.add(K1);
childKeys.add(K2);
return childKeys;
}
private static String getRight(String key) {
return key.substring(key.length() / 2);
}
private static String getLeft(String key) {
return key.substring(0, key.length() / 2);
}
/**
* 循环循环移动step位
*
* @param str
* @param step
* @return
*/
private static String move(String str, int step) {
int length = str.length();
step = step % length;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < length; i++) {
int idx = (i - step) % length;
if (idx < 0) idx += length;
stringBuilder.append(str.charAt(idx));
}
return stringBuilder.toString();
}
/**
* 置换
*
* @param key
* @param p p4/p8/p10/IP等
*/
private static String SW(String key, int[] p) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < p.length; i++) {
stringBuilder.append(key.charAt(p[i] - 1));
}
return stringBuilder.toString();
}
}