DES加密算法

DES算法

ES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来。

基本原则:

DES设计中使用了分组密码设计的两个原则:混淆(confusion)和扩散(diffusion),其目的是抗击敌手对密码系统的统计分析。混淆是使密文的统计特性与密钥的取值之间的关系尽可能复杂化,以使密钥和明文以及密文之间的依赖性对密码分析者来说是无法利用的。扩散的作用就是将每一位明文的影响尽可能迅速地作用到较多的输出密文位中,以便在大量的密文中消除明文的统计结构,并且使每一位密钥的影响尽可能迅速地扩展到较多的密文位中,以防对密钥进行逐段破译。

加密流程:

(1)输入64位明文数据,并进行初始置换IP;

(2)在初始置换IP后,明文数据再被分为左右两部分,每部分32位,以L0,R0表示;

(3)在秘钥的控制下,经过16轮运算(f);

(4)16轮后,左、右两部分交换,并连接再一起,再进行逆置换;

(5)输出64位密文。

算法特点:

(1)分组加密算法:以64位为分组。64位明文输入,64位密文输出。

(2)对称算法:加密和解密使用同一秘钥。

(3)有效秘钥长度为56位:秘钥通常表示为64位数,但每个第8位用作奇偶校验,可以忽略。

(4)代替和置换:DES算法是两种加密技术的组合:混乱和扩散。先替代后置换。

(5)易于实现:DES算法只是使用了标准的算术和逻辑运算,其作用的数最多也只有64 位,因此用70年代末期的硬件技术很容易实现,算法的重复特性使得它可以非常理想地用在一个专用芯片中。

实现流程(JAVA):

在这里插入图片描述

初始IP置换:

    /**
     * 起始置换,根据置换表,将明文进行置换,记得置换表的数字要减1
     *
     * @param plainText
     * @return
     */
    public String innitialPermutation(String plainText) {
        char[] plainTextCh = plainText.toCharArray();
        char[] permutation = new char[64];
        for (int i = 0; i < 64; i++) {
            permutation[i] = plainTextCh[innitial[i] - 1];
        }
        String initPlaintext = String.valueOf(permutation);
        return initPlaintext;
    }

轮函数:

  • 将IP置换后的64bit分为L与R两部分,每部分32bit,进行16轮运算,在轮函数中,需要进行E扩展、将32bit扩展为48bit,接着再将其与每一轮的密钥(48位)进行异或,再经过S盒进行压缩,最终再进行P盒置换。
E扩展:

在这里插入图片描述

    /**
     * 扩展置换E,同样的,记得置换表里面的数字-1
     *
     * @param Ri 传入Ri
     * @return
     */
    public String expandChange(String Ri) {
        char[] RiCh = Ri.toCharArray();
        char[] exRiCh = new char[48];
        for (int i = 0; i < 48; i++) {
            exRiCh[i] = RiCh[expandTbE[i] - 1];
        }
        String ERi = String.valueOf(exRiCh);
        return ERi;
    }
S盒:

在这里插入图片描述

 /**
     * S盒运算,48位得到对照表得到32位
     *
     * @param oxrRes 异或运算得到的结果
     * @return
     */
    public String sboxCul(String oxrRes) {
        StringBuilder sb = new StringBuilder();
        String column = "";
        String row = "";
        int colNum = 0;
        int rowNum = 0;
        int boxNum = 0;
        String res = "";
        for (int i = 0; i < 8; i++) {
            row = oxrRes.substring(6*i,6*i+1) + oxrRes.substring(6*i+5,6*i+6);
            column = oxrRes.substring(6*i+1,6*i+5);
            colNum = Integer.parseUnsignedInt(column, 2); //列
            rowNum = Integer.parseUnsignedInt(row, 2); //行
            boxNum = s[i][rowNum * 8 + colNum];

            //补0
            res = Integer.toBinaryString(boxNum);
            if (res.length() != 4) {
                for (int k = res.length(); k < 4; k++) {
                    res = "0" + res;
                }
            }
            sb.append(res);
        }
        String sboxRes = sb.toString();
        return sboxRes;
    }
P盒置换:

在这里插入图片描述

    /**
     * P置换
     */
    public String pboxCul(String sboxRes){
        char[] sboxResCh = sboxRes.toCharArray();
        char[] pboxResCh = new char[32];
        for(int i = 0;i<32;i++){
            pboxResCh[i] = sboxResCh[pbox[i]-1];
        }
        String pboxRes = String.valueOf(pboxResCh);
        return  pboxRes;
    }

IP逆置换:

    /**
     * 最终逆置换
     *
     * @param plainText
     * @return
     */
    public String EndPermutation(String plainText) {
        char[] plainTextCh = plainText.toCharArray();
        char[] permutation = new char[64];
        for (int i = 0; i < 64; i++) {
            permutation[i] = plainTextCh[end[i] - 1];
        }
        String initPlaintext = String.valueOf(permutation);
        return initPlaintext;
    }

解密流程:

在这里插入图片描述

完整代码:

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * @author the best
 * @version 1.0
 * @date 2022/8/9 21:31
 */
public class DES {
    //起始置换表 长度8X8
    public int[] innitial = new int[]{
            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
    };

    //扩展置换表E  长度6X8
    public int[] expandTbE = new int[]{
            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
    };


    //生成子密钥 pc1表 长度7X8
    public int[] pc1 = new int[]{
            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
    };

    //生成子秘钥 pc2表 长度6X8
    public int[] pc2 = new int[]{
            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
    };


    //生成子秘钥 循环左移轮次表 长度 16
    public int[] leftRoundTable = new int[]{
            1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
    };


    //S1盒
    public int[][] s = new int[][]{
            {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}
    };


    //P盒 长度为32
    public int[] pbox = new int[]{
            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
    };

    //逆置置换表
    public int[] end = new int[]{
            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
    };

    /**
     * 起始置换,根据置换表,将明文进行置换,记得置换表的数字要减1
     *
     * @param plainText
     * @return
     */
    public String innitialPermutation(String plainText) {
        char[] plainTextCh = plainText.toCharArray();
        char[] permutation = new char[64];
        for (int i = 0; i < 64; i++) {
            permutation[i] = plainTextCh[innitial[i] - 1];
        }
        String initPlaintext = String.valueOf(permutation);
        return initPlaintext;
    }

    /**
     * 最终逆置换
     *
     * @param plainText
     * @return
     */
    public String EndPermutation(String plainText) {
        char[] plainTextCh = plainText.toCharArray();
        char[] permutation = new char[64];
        for (int i = 0; i < 64; i++) {
            permutation[i] = plainTextCh[end[i] - 1];
        }
        String initPlaintext = String.valueOf(permutation);
        return initPlaintext;
    }
    
    

    /**
     * S盒运算,48位得到对照表得到32位
     *
     * @param oxrRes 异或运算得到的结果
     * @return
     */
    public String sboxCul(String oxrRes) {
        StringBuilder sb = new StringBuilder();
        String column = "";
        String row = "";
        int colNum = 0;
        int rowNum = 0;
        int boxNum = 0;
        String res = "";
        for (int i = 0; i < 8; i++) {
            row = oxrRes.substring(6*i,6*i+1) + oxrRes.substring(6*i+5,6*i+6);
            column = oxrRes.substring(6*i+1,6*i+5);
            colNum = Integer.parseUnsignedInt(column, 2); //列
            rowNum = Integer.parseUnsignedInt(row, 2); //行
            boxNum = s[i][rowNum * 8 + colNum];

            //补0
            res = Integer.toBinaryString(boxNum);
            if (res.length() != 4) {
                for (int k = res.length(); k < 4; k++) {
                    res = "0" + res;
                }
            }
            sb.append(res);
        }
        String sboxRes = sb.toString();
        return sboxRes;
    }


    /**
     * 扩展置换E,同样的,记得置换表里面的数字-1
     *
     * @param Ri 传入Ri
     * @return
     */
    public String expandChange(String Ri) {
        char[] RiCh = Ri.toCharArray();
        char[] exRiCh = new char[48];
        for (int i = 0; i < 48; i++) {
            exRiCh[i] = RiCh[expandTbE[i] - 1];
        }
        String ERi = String.valueOf(exRiCh);
        return ERi;
    }

    /**
     * DES加密,生成子密钥,返回第X轮次的子秘钥。<br/> 因为字符串都是从0开始,所以pc里面的数字都要减1
     *
     * @param key   秘钥的字符串
     * @param round 生成子秘钥的轮次
     * @return
     */
    public String roundKeyGenerate(String key, int round) {
        char[] keyCh = key.toCharArray();
        char[] generateOne = new char[56];
        for (int i = 0; i < 56; i++) {
            generateOne[i] = keyCh[pc1[i] - 1];
        }
        key = String.valueOf(generateOne);
        String c0 = key.substring(0, 28);
        String d0 = key.substring(28, 56);

        int bitCount = leftRoundTable[round];
        c0 = c0.substring(0 + bitCount, 28) + c0.substring(0, bitCount);
        d0 = d0.substring(0 + bitCount, 28) + d0.substring(0, bitCount);

        String subKey = c0 + d0;
        char[] subKeyCh = subKey.toCharArray();
        char[] generateTwo = new char[48];
        for (int i = 0; i < 48; i++) {
            generateTwo[i] = subKeyCh[pc2[i] - 1];
        }
        subKey = String.valueOf(generateTwo);
        return subKey;
    }


    /**
     * 异或运算
     *
     * @param
     * @param
     * @return
     */
    public String xorCul(String ERi, String Ki,Integer num) {
        char[] ERiCh = ERi.toCharArray();
        char[] KiCh = Ki.toCharArray();
        char[] resCh = new char[num];
        for (int i = 0; i < num; i++) {
            if (ERiCh[i] == KiCh[i]) {
                resCh[i] = '0';
            } else {
                resCh[i] = '1';
            }
        }
        String result = String.valueOf(resCh);
        return result;
    }

    /**
     * P置换
     */
    public String pboxCul(String sboxRes){
        char[] sboxResCh = sboxRes.toCharArray();
        char[] pboxResCh = new char[32];
        for(int i = 0;i<32;i++){
            pboxResCh[i] = sboxResCh[pbox[i]-1];
        }
        String pboxRes = String.valueOf(pboxResCh);
        return  pboxRes;
    }


    /**
     * 轮函数
     *
     * @return
     */
    public String f(String R, String k,int round) {
        //E扩展 将32位扩展为48位
        String s = expandChange(R);
     //   System.out.println("E After the extension ="+s);
        //与密钥K进行异或运算
        String k1 = roundKeyGenerate(k,round);
       // System.out.println("The key generated in each round ="+k1);
        String s2 = xorCul(s,k1,48);
        //System.out.println("And the key K xor after ="+s2);
        //S盒压缩,将48位压缩为32位
        String s3 = sboxCul(s2);
       // System.out.println("S-box compressed to 32 bits ="+s3);
        //P置换
        String s4 = pboxCul(s3);
        return s4;
    }

    /**
     * 加密算法
     * @param
     */
    public String encrypt(String plainText,String key){
        DES des = new DES();
        String s1 = des.changeString(plainText);
        String[] strings = des.spiltString(s1);
        StringBuffer sb = new StringBuffer();
        key = des.changekey(key);
        for(int j=0;j<strings.length;j++){
            //P置换
            String s = des.innitialPermutation(strings[j]);
            //System.out.println("Initial P permutation ="+s);
            //将字符串分为L和R
            String L = s.substring(0, 32);
            String R = s.substring(32, 64);
            for(int i=0;i<16;i++){
                String temp = R;
                R = des.xorCul(des.f(R, key,i), L,32);
                L=temp;
            }
            String data = R+L;
            String ciphertext = des.EndPermutation(data);
            sb.append(ciphertext);
        }
        System.out.println("ciphertext ="+sb);
        return sb.toString();
    }

    /**
     * 解密算法1
     * @param
     */
    public String decrypt(String ciphertext,String key){
        DES des = new DES();
        String[] strings = des.spiltString(ciphertext);
        StringBuffer sb = new StringBuffer();
        key = des.changekey(key);
        for(int j=0;j<strings.length;j++){
            //P置换
            String s = des.innitialPermutation(strings[j]);
            //将字符串分为L和R
            String L = s.substring(0, 32);
            String R = s.substring(32, 64);
            for(int i=15;i>=0;i--){
                String temp = R;
                R = des.xorCul(des.f(R, key,i), L,32);
                L=temp;
            }
            String data = R+L;
            String plainText = des.EndPermutation(data);
            sb.append(plainText);
        }
        String abcd = new String(des.string2bytes(sb.toString().replace("00000000", "")));
        System.out.println("plainText ="+abcd);
        return abcd;
    }


    /**
     * 把byte转化成2进制字符串
     * @param b
     * @return
     */
    public static String getBinaryStrFromByte(byte b) {
        String result = "";
        byte a = b;
        ;
        for (int i = 0; i < 8; i++) {
            byte c = a;
            a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。
            a = (byte) (a << 1);
            if (a == c) {
                result = "0" + result;
            } else {
                result = "1" + result;
            }
            a = (byte) (a >> 1);
        }
        return result;
    }

    /**
     * 二制度字符串转字节数组,如 101000000100100101110000 -> A0 09 70
     * @param input 输入字符串。
     * @return 转换好的字节数组。
     */
    static byte[] string2bytes(String input) {
        StringBuilder in = new StringBuilder(input);
        // 注:这里in.length() 不可在for循环内调用,因为长度在变化
        int remainder = in.length() % 8;
        if (remainder > 0)
            for (int i = 0; i < 8 - remainder; i++)
                in.append("0");
        byte[] bts = new byte[in.length() / 8];

        // Step 8 Apply compression
        for (int i = 0; i < bts.length; i++)
            bts[i] = (byte) Integer.parseInt(in.substring(i * 8, i * 8 + 8), 2);

        return bts;
    }

    /**
     * 将字符串转为二进制
     * @param
     */
    public String changeString(String temp){
        byte[] bytes = temp.getBytes(StandardCharsets.UTF_8);
        DES des = new DES();
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<bytes.length;i++){
            sb.append(des.getBinaryStrFromByte(bytes[i]));
        }
        return sb.toString();
    }

    /**
     * 将字符串按64位分割,不足补0
     * @param
     */
    public String[] spiltString(String temp){
        int n = (temp.length() + 64 - 1) / 64; //获取整个字符串可以被切割成字符子串的个数
        String[] split = new String[n];
        for (int i = 0; i < n; i++) {
            if (i < (n - 1)) {
                split[i] = temp.substring(i * 64, (i + 1) * 64);
            } else {
                split[i] = temp.substring(i * 64);
                while (split[i].length()<64){
                    split[i]+="0";
                }
            }
        }
        return split;
    }

    /**
     * 将密钥转成64bit,不足补0
     * @param
     */
    public String changekey(String key){
        byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
        DES des = new DES();
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<bytes.length;i++){
            sb.append(des.getBinaryStrFromByte(bytes[i]));
        }
        while (sb.length()<64){
            sb.append("0");
        }
        return sb.toString();
    }


    public static void main(String[] args) {
        String plainText = "ABCDEFG123456";
        System.out.println("plainText="+plainText);
        String key = "QWE";
        System.out.println("key="+key);
        DES des = new DES();
        String encrypt = des.encrypt(plainText, key);
        des.decrypt(encrypt,key);
    }
}

总结:

加密:

首先需要将明文字符串转为字节数组,再将字节数组转为二进制字符串,按64位分割(不足位补0),通过DES算法进行加密,将加密完成的字符串拼接成为密文。

解密:

将密文进行按64位进行分割,分别进行解密,将解密之后的字符串去除补位的0,再将字符串转为字节数组,最终转为明文。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值