Kryptographie zum Wachstum von Programmierern – Detaillierte Erklärung des DES-Algorithmus

Einzelheiten zum Implementierungsprinzip des DES-Algorithmus finden Sie unter
https://blog.csdn.net/qq_31236027/article/details/128209185
. Einzelheiten zum Erhalten des DES-Algorithmusschlüssels finden Sie unter
https://blog.csdn.net /qq_31236027/article/details/129224730
Weitere Informationen zum Erhalt von Codierungstools finden Sie unter
https://blog.csdn.net/qq_31236027/article/details/128579451

Ideen zur Implementierung des DES-Algorithmus:

  1. Zum Entwerfen der Codierungstoolklasse werden hauptsächlich die folgenden Methoden entwickelt:
    1. Konvertieren Sie die Zeichenfolge für die Klartextverschlüsselung in eine Binärdatei
    2. Binär in String, wird zum Konvertieren des entschlüsselten Binärstroms und -texts verwendet
    3. Binäre bis hexadezimale Zeichenfolge, die zum Übertragen und Speichern von verschlüsseltem Text verwendet wird, um verstümmelte Zeichen zu verhindern
    4. Hexadezimal bis binär, wird zum Entschlüsseln von verschlüsseltem Text verwendet
    5. Andere notwendige Methoden (z. B. binäre Konvertierung der S-Box)
  2. Entwerfen Sie die Schlüsselerfassungstoolklasse
  3. Führen Sie den Entwurf der Des-Algorithmus-Toolklasse durch

Das Design der Schlüsselerfassungs-Tool-Klasse:

  1. Entwerfen Sie eine Initialisierungsmethode und zwei Arrays für die erste Schlüsselauswahlpermutation und empfangen Sie die 32-Bit-Binärergebnisse der linken und rechten Hälfte.
  2. Nach der Initialisierung werden 16 Runden der Schlüsselerfassung durchgeführt. Es ist am besten, sie in einer privaten globalen Variablen zu speichern und sie dann extern über eine Schnittstellenmethode zu veröffentlichen. (Ich habe diesen Schritt nicht gemacht)

Design der DES-Algorithmus-Toolklasse:

wichtiger Punkt:

  1. Speichern Sie den erhaltenen 16-Runden-Schlüssel in einer globalen Variablen, die zur Entschlüsselung verwendet werden muss (Sie müssen ihn nur umkehren).
  2. Achten Sie auf das Problem der Gruppierung
  3. ! ! Sehr wichtiger Punkt! ! Nach der Verschlüsselung sollten alle hexadezimalen Streams in Kleinbuchstaben geschrieben werden. Wenn sie in Großbuchstaben geschrieben sind, werden nach der Entschlüsselung verstümmelte Zeichen angezeigt.

Implementieren Sie abschließend den Code der DES-Algorithmus-Toolklasse:

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));
	}
}

Insuffizienz

  1. Der Schlüssel ist kürzer (64 Bit), weniger sicher und leicht zu knacken
  2. Verschlüsselter Text ist länger
  3. langsamere Verarbeitung

Nachfolgender Verbesserungsplan

  1. Verkürzen Sie die Länge des verschlüsselten Textes
  2. Versuchen Sie, Multithreading zu verwenden, um die Verarbeitung zu beschleunigen.
  3. Versuchen Sie es mit einer Version mit längerem Schlüssel.

おすすめ

転載: blog.csdn.net/qq_31236027/article/details/129349411