程序猿成长之路之密码学番外篇-----DES算法密钥获取详解

上次介绍了一下DES算法的整体流程,那么下面展开讲讲DES算法中16轮密钥获取的部分

话不多说,先上代码

package common;

import java.math.BigInteger;

/**
 * 进制转换类
 * @author zygswo
 *
 */
public class EncodeUtil {
    
    
	/**
	 * 转2进制(字符串转成二进制)
	 * @param text 要转的文本
	 * @param length 字符长度
	 * @return
	 */
	public static String strtoBinary(String text, int length) {
    
    
		StringBuilder result = new StringBuilder("");
		//取每一位上的数字
		for(char ch : text.toCharArray()) {
    
    
			int res = ch, pre = ch;
			String res1 = "";
			while(res > 0) {
    
    
				res = res >> 1 << 1;
				res1 = ((pre ^ res) == 0 ? "0" : "1") + res1; //异或判断当前位上数字是否为1
				res = res >> 1;
				pre = pre >> 1;
			}
			//扩充至8位
			while (res1.length() < length) {
    
    
				res1 = "0" + res1;
			}
//			System.out.println("res1 = " + res1);
			result.append(res1);
		}
		return result.toString();
	}
}
package des;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import common.EncodeUtil;

/**
 * 子密钥生成为置换选择,因为不仅有位的置换还有位的筛选
 * 而des明文加密中的为初始置换,因为没有位的筛选。
 */
/**
 * des加解密密钥生成工具类
 * @author zygswo
 *
 */
public class KeyUtil {
    
    
	
	/**
	 * 循环移动
	 */
	public static final int[] SHIFT = {
    
    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
	
	/**
	 * 置换选择PC-1表
	 */
	private static int[][] transferTable = {
    
    
			{
    
    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}
	};
	
	/**
	 * 置换选择PC-2表
	 */
	private static int[][] transferTwoTable = {
    
    
			{
    
    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}
	};
	
	/**
	 * leftpart and rightPart
	 */
	private char[] leftPart,rightPart;
	
	/**
	 * keylist
	 */
	private List<String> keylist = Collections.synchronizedList(new ArrayList<>());
	
	/**
	 * 密钥初始化
	 */
	public KeyUtil init() {
    
    
		String randomStr = "12345678";
		String str64bit = EncodeUtil.strtoBinary(randomStr,8);
		leftPart = new char[28]; //左半部分
		rightPart = new char[28]; //右半部分
		//置换选择(pc-1)
		for (int i=0; i< 28;i++) {
    
    
			leftPart[i] = str64bit.charAt(transferTable[i/8][i%8] - 1);
		}
		for (int i=28; i< 56;i++) {
    
    
			rightPart[i-28] = str64bit.charAt(transferTable[i/8][i%8] - 1);
		}
		return this;
	}
	
	/**
	 * 加密生成子密钥
	 * @param round 循环次数
	 * @return
	 */
	public List<String> generateKey() {
    
    
		if (leftPart == null || rightPart == null) {
    
    
			throw new RuntimeException("请先进行密钥初始化");
		}
		for (int i : SHIFT) {
    
    
			//循环左移,1,2,9,16移动一位,其他移动2位
			String leftRotateResult = leftRotate(leftPart, rightPart, i);
			char[] temp = leftRotateResult.toCharArray();
			char[] result = new char[48];
			for (int k=0; k<48; k++) {
    
    
				result[k] = temp[transferTwoTable[k/8][k%8] - 1];
			}
	        keylist.add(new String(result));
        }
		System.out.println(keylist.toString());
		return keylist;
	}
	
	/**
	 * 循环左移
	 * @param leftPart 左边28位
	 * @param rightPart 右边28位
	 * @return 循环后的结果
	 */
	private String leftRotate(char[] leftPart, char[] rightPart, int i) {
    
    
		String leftValue = new String(leftPart);
		String rightValue = new String(rightPart);
		leftValue = leftValue.substring(i) + leftValue.substring(0,i);
		rightValue = rightValue.substring(i) + rightValue.substring(0,i);
		this.leftPart = leftValue.toCharArray();
		this.rightPart = rightValue.toCharArray();
		return leftValue + rightValue; 
	}	
	public static void main(String[] args) {
    
    
		new KeyUtil().init().generateKey();
	}
}

代码的整体思路:

  1. 整体流程:

    1. 置换选择PC-1: 将64位初始值转换为左半部分28位和右半部分28位,也就是将64位初始值置换为56位子密钥,目的是增加混淆,去掉奇偶校验位。
    2. 循环左移:将左半部分和右半部分同时进行循环左移。将前面的一位移到末尾。【ps:这里使用了substring函数】。注意,第1、2、9、16循环左移1位,其余的循环左移2位,一共循环左移28位。
    3. 置换选择PC-2: 将左半部分和右半部分结合成56位子密钥后进行置换选择PC-2,获得48位的结果用于后续的f函数运算。PS: 因为f函数的输入为64位明文其中的一半(32位)进行扩展运算(48位)和密钥生成结果(48位)。
  2. 设计思路:
    将整个16轮循环左移的偏移量放入shift数组中,之后通过for循环遍历shift数组获取16轮的密钥获取输出结果再通过返回结果List的方式完成输出。

--------------------------------小尾巴--------------------------------------------------------------------
DES算法会持续更新

猜你喜欢

转载自blog.csdn.net/qq_31236027/article/details/129224730