程序猿成长之路之密码学篇-DES算法详解

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算法实现思路:

  1. 进行编码工具类的设计,主要设计以下几个方法:
    1. 字符串转二进制,用于明文加密
    2. 二进制转字符串,用于对解密二进制流与文本的转换
    3. 二进制转十六进制字符串,用于对加密后的文本进行传输和保存,防止出现乱码
    4. 十六进制转二进制,用于对加密后的文本进行解密
    5. 其他有需要的方法(如s盒的二进制转十进制)
  2. 进行密钥获取工具类的设计
  3. 进行des算法工具类的设计

密钥获取工具类的设计:

  1. 设计一个初始化方法和两个数组,用于密钥第一次选择置换和接收左右半部分的32位二进制结果。
  2. 初始化后进行16轮密钥的获取,最好能够存储在一个私有全局变量里,然后通过一个接口方法对外发布。(我这步没有做)

DES算法工具类的设计:

注意点:

  1. 将获取的16轮密钥保存在一个全局变量,解密需要使用(只需要逆置即可)
  2. 注意分组问题
  3. !!很重要的一点!!在加密后要将所有的十六进制流小写,大写的话会解密出现乱码。

最后上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));
	}
}

存在的不足

  1. 密钥较短(64位),安全性能较差,很容易被破解
  2. 加密后文本较长
  3. 处理速度较慢

后续改进方案

  1. 缩短加密后文本长度
  2. 尝试使用多线程处理,加快处理速度。
  3. 尝试更长密钥的版本。

你可能感兴趣的:(笔记,算法,密码学,java)