概述
DES的算法实现原理详情请见
https://blog.csdn.net/qq_31236027/article/details/128209185
DES算法密钥获取详情请见
https://blog.csdn.net/qq_31236027/article/details/129224730
编码工具类获取详见
https://blog.csdn.net/qq_31236027/article/details/128579451
DES算法实现思路:
- 进行编码工具类的设计,主要设计以下几个方法:
- 字符串转二进制,用于明文加密
- 二进制转字符串,用于对解密二进制流与文本的转换
- 二进制转十六进制字符串,用于对加密后的文本进行传输和保存,防止出现乱码
- 十六进制转二进制,用于对加密后的文本进行解密
- 其他有需要的方法(如s盒的二进制转十进制)
- 进行密钥获取工具类的设计
- 进行des算法工具类的设计
密钥获取工具类的设计:
- 设计一个初始化方法和两个数组,用于密钥第一次选择置换和接收左右半部分的32位二进制结果。
- 初始化后进行16轮密钥的获取,最好能够存储在一个私有全局变量里,然后通过一个接口方法对外发布。(我这步没有做)
DES算法工具类的设计:
注意点:
- 将获取的16轮密钥保存在一个全局变量,解密需要使用(只需要逆置即可)
- 注意分组问题
- !!很重要的一点!!在加密后要将所有的十六进制流小写,大写的话会解密出现乱码。
最后上DES算法工具类实现代码:
package des;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import common.EncodeUtil;
import common.IEncrytion;
import common.EncodeUtil.EncodeRadix;
/**
* des加解密工具类
* @author zygswo
*
*/
public class DesUtil implements IEncrytion{
/**
* 加密轮数
*/
public static final int ROUND_NB = 16;
/**
* 字符串长度
*/
public static final int CHAR_LEN = 16;
/**
* 分组长度
*/
public static final int DIV_LEN = 64 / CHAR_LEN;
/**
* 密钥对象
*/
private KeyUtil subKeyObj = new KeyUtil().init();
public List<String> subKeys = Collections.synchronizedList(new ArrayList<>());
/**
* 初始置换PC表
*/
private static int[][] transferTable = {
{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}
};
/**
* 扩展置换PC表
*/
private static int[][] transferExtendTable = {
{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}
};
/**
* p盒置换表
*/
private static int[][] pTransferTable = {
{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}
};
/**
* 逆初始置换PC-1表
*/
private static int[][] transferReverseTable = {
{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}
};
/**
* s盒置换表
*/
private static int[][][] sBox = {
{
{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}
}
};
/**
* 解密
* @param encrytedText 密文
* @return 明文
*/
@Override
public String decrypt(String encrytedText) {
StringBuilder sb = new StringBuilder();
// CHAR_LEN = divLeng * 4 (16进制4位,转16位需要4个16进制位)
int divLeng = 16;
int length = encrytedText.length()% divLeng == 0 ? encrytedText.length()/divLeng : (encrytedText.length()/divLeng + 1);
for (int i = 0; i<length; i++) {
int startIndex = i * divLeng;
int endIndex = (startIndex + divLeng) > encrytedText.length() ? encrytedText.length() : (startIndex + divLeng);
String str1 = encrytedText.substring(startIndex, endIndex);
sb.append(
baseDecrypt(EncodeUtil.toBinary(str1,EncodeRadix.HEX))
);
}
return sb.toString().trim();
}
/**
* 解密(64位一组)
* @param encrytedText 密文
* @return
*/
private String baseDecrypt(String encrytedText) {
//1.初始置换
//获取16组密钥
List<String> reversedSubkeys = new ArrayList<>();
for (int i = subKeys.size() -1; i >=0 ;i--) {
reversedSubkeys.add(subKeys.get(i));
}
//16次循环加密
return EncodeUtil.binaryToStr(
loops(encrytedText, reversedSubkeys,false),CHAR_LEN
).trim();
}
/**
* 分组加密
* @param text 明文
* @return 密文
*/
@Override
public String encrypt(String text) {
StringBuilder sb = new StringBuilder();
// DIV_LEN * CHAR_LEN = 64
// 根据DIV_LEN进行分组,如CHAR_LEN=16位,那么就每4个字符一组
int length = text.length()% DIV_LEN == 0 ? text.length()/DIV_LEN : (text.length()/DIV_LEN + 1);
for (int i = 0; i<length; i++) {
int startIndex = i * DIV_LEN;
int endIndex = (startIndex + DIV_LEN) > text.length() ? text.length() : (startIndex + DIV_LEN);
String str1 = text.substring(startIndex, endIndex);
//尾部填充
while (str1.length() < DIV_LEN) {
str1 += " ";
}
sb.append(baseEncrypt(str1));
}
return sb.toString().toLowerCase().trim(); //注意,加密后一定要为小写,大写后会出错
}
/**
* 加密(每个密文都是64位)
* @param text
* @return
*/
private String baseEncrypt(String text) {
//获取16组密钥
if (subKeys == null || subKeys.isEmpty()) {
subKeys = subKeyObj.generateKey();
}
//16次循环加密
return EncodeUtil.binaryToHexStr(
loops(text, subKeys, true)
).trim();
}
/**
* 16轮循环加密
* @param step1Result 初始置换后的结果
* @param subKeys 16组子密钥
* @param isEncrypt 是否加密
* @return 循环加密结果
*/
private String loops(String text, List<String> subKeys, boolean isEncrypt) {
//1.初始置换
String str64bit = text;
if (isEncrypt) {
str64bit = EncodeUtil.strtoBinary(text,CHAR_LEN);
}
str64bit = swap(str64bit, transferTable);
if (str64bit.length() != 64) {
throw new IllegalArgumentException("初始置换后的结果不为64位");
}
if (subKeys.size() != 16) {
throw new IllegalArgumentException("加密密钥组数不为16组");
}
//主体部分
String leftPart = str64bit.substring(0,32); //高位
String rightPart = str64bit.substring(32);
//低位
for (String subKey: subKeys) {
//右半边参与f函数运算
String temp = rightPart;
//获取f函数结果
String fResult = fFunction(rightPart,subKey);
//f函数结果与左半部分进行异或运算
rightPart = xor(fResult, leftPart);
//左右交换
leftPart = temp;
}
//逆初始置换
return swap(rightPart+leftPart, transferReverseTable);
}
/**
* f函数
* @param str32Bit 右半部分
* @param subKey 子密钥
* @return f函数结果
*/
private String fFunction(String str32Bit, String subKey) {
//1. 扩展运算,将rightPart32位转成48位
String str48Bit = swap(str32Bit, transferExtendTable);
//2. 与密钥进行异或运算
String result = xor(str48Bit, subKey);
char[] res = result.toCharArray();
//3. s盒运算
String[] sBoxTmp = new String[8];
StringBuilder sBoxResult = new StringBuilder();
//p盒运算用
for(int i=0; i<sBoxTmp.length; i++) {
//每6位为一组输入(首位和末尾位为行索引,中间四位为列索引)
int rowNb = Integer.valueOf(
EncodeUtil.binaryToDec(res[i*6] + "" + res[i*6 + 5])
);
int colNb = Integer.valueOf(
EncodeUtil.binaryToDec(res[i*6 + 1] + "" + res[i*6 + 2]+ res[i*6 + 3]+ res[i*6 + 4])
);
sBoxTmp[i] = EncodeUtil.decToBinary(sBox[i][rowNb][colNb]+"",4);
}
for(String res1: sBoxTmp) {
sBoxResult.append(res1);
}
//4. p盒置换运算
return swap(sBoxResult.toString().trim(),pTransferTable);
}
/**
* 异或运算
* @param text1 text1
* @param text2 text2
* @return
*/
private String xor(String text1, String text2) {
if (text1 == null || text2 == null || text1.length() != text2.length()) {
throw new IllegalArgumentException("异或运算失败");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < text1.length();i++) {
char ch1 = text1.charAt(i);
char ch2 = text2.charAt(i);
sb.append((ch1) ^ (ch2));
}
return sb.toString().trim();
}
/**
* 置换运算
* @param text 文本
* @param table 置换运算表
* @return 置换运算后结果
*/
private String swap(String text,int[][] table) {
char[] chs = text.toCharArray();
StringBuilder sb = new StringBuilder();
int rowNb = table.length;
int colNb = table[0].length;
for (int i = 0; i < rowNb * colNb;i++) {
sb.append(chs[table[i/colNb][i%colNb] - 1]);
}
return sb.toString().trim();
}
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
/**
* desutil使用方法
* 1. 使用base64编码字符串
* 2. 使用des加密,获得16进制字符串
*/
DesUtil util = new DesUtil();
String result = util.encrypt("{"code":200,"message":"成功!","data":{"id": "2103813902831"}}");
System.out.println("encrypt result=" + result);
System.out.println("decrypt result = " + util.decrypt(result));
}
}
存在的不足
- 密钥较短(64位),安全性能较差,很容易被破解
- 加密后文本较长
- 处理速度较慢
后续改进方案
- 缩短加密后文本长度
- 尝试使用多线程处理,加快处理速度。
- 尝试更长密钥的版本。
最后
以上就是刻苦电源为你收集整理的程序猿成长之路之密码学篇-DES算法详解的全部内容,希望文章能够帮你解决程序猿成长之路之密码学篇-DES算法详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复