對稱密碼——DES加密算法

前言

本篇博文將介紹對稱密碼算法中的DES密碼的算法原理與代碼實現(Java)java

DES算法原理

DES加密算法是對稱加密算法(加密和解密使用同一個密鑰)中的一種,DES也是分組密碼,以64位爲分組對明文進行加密。android

DES算法會對明文進行16輪的迭代加密,具體的算法過程能夠看下面這圖(來自文末參考博文中的圖,作了一些修改)。看一遍有點繞就那筆跟着走一遍。算法

下面這張圖是每次迭代的的一個提取,咱們從中能夠直接觀察到的就是迭代的兩個規律數組

Li = Ri-1
Ri = Li-1 ^ F(Ri-1, Ki)

上一輪的輸出做爲下一輪加密的輸入(也就是迭代的過程)。一樣,子密鑰也是迭代產生app

在整體概覽了一遍後,咱們能夠將DES算法分爲3部分來說解。從第一張圖從右往左,輪子密鑰(子密鑰)的生成、F函數的實現以及16次迭代的過程。函數

子密鑰的產生

如圖上的流程圖所示,將所給的初始64位密鑰(如果密鑰不足64位則前面加0補充至64位),通過PC-1置換壓縮成56位。而後分紅左右28位,表示成C0, D0。C0和D0按照循環左移表來分別循環左移,此處是第一次循環,因此循環左移1次,生成C1和D1。而後C1和D1合併成56位密鑰通過PC-2置換壓縮成48位的K1。oop

K2的生成過程:C1和D1分別循環左移1次,而後合併通過PC-2置換壓縮成K2。Ki的生成就爲Ci-1和Di-1分別循環左移,而後合併通過PC-2置換壓縮而成。ui

PC-1 置換表 PC-2置換表 循環左移表this

//PC-1置換表
private int[] PC1={
    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 int[] PC2={
    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};

//循環左移次數表
private int[] leftTable = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};

F函數的原理

F函數的內部仍是比較複雜,不過問題不大。咱們按照F函數內部執行順序來能夠分爲如下幾步:加密

  • Ri-1作一個E擴展,從32位擴展成48位
  • Ri-1與Ki異或運算,而後將異或運算的結果通過S盒選擇壓縮成32位
  • 從S盒出來的32位結果再通過P置換,就獲得最終的32位Ri

Ri-1作擴散選擇的表以下:

//E擴展
private int[] ETable={
    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};

從S盒出來的32位結果通過的P表以下:

//P置換
private int[] P={
    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};

在這三步中最爲機密的就是S盒的選擇壓縮了。S盒是如何實現選擇壓縮呢?咱們就要知道S的結構了。

S盒的結構

咱們能夠看出,進入S盒後將Ri-1與Ki異或的值分紅8組,每組6位,分別進入S1-S8盒,而後從每一個盒中出4位,合併成32位的結果。

如果還想探究6位變成4位是如何的變換的,就須要看下面這點介紹:

咱們以進入S!盒爲例,假設進入的6位二進制數爲101001,咱們通常將第一位和最後一位(從左到右)做爲行座標,中間四位做爲縱座標找值。11即3行,0100即4列(從0開始編號),最後選出的值就爲4,四位二進制表示則爲0100。

16次的迭代加密

最初咱們須要將初始的明文作一個IP置換,而後分紅左右各32位即L0 R0,帶入L0、R0去計算L1和R1。

16次迭代的規律爲:

Li = Ri-1
Ri = Li-1 ^ F(Ri-1, Ki)

最後,L16與R16直接交換賦給L17和R17(L17=R16, R17=L16),而後L17與R17合併後經過IP逆置換生成最終的密文。

以上,即是加密過程,解密能夠說是加密的逆向過程。解密爲什麼反向就能夠解密,還須要各位看官另覓資料~

DES算法Java實現(完整)

package symmetricipher;
/**   
* @description: 代碼實現Des算法加解密
* @author sakura  
* @date 2019年3月25日 下午12:52:21  
*/

/*
 * 1.主要的一個迭代公式  Li=Ri Ri = Li-1 ⊕F(Li-1,Ki)
 * 2.總體能夠分爲 加解密運算  F函數的處理  子密鑰的產生
 * 3.子祕鑰產生:64位通過PC-1密鑰置換成56位 分爲Ci Di左右各28爲位 而後根據循環左移表來左移  最後通過PC-2置換成48位的密鑰Ki
 * 4.F函數的處理:Li-1(32位)通過E盒擴展成48位; 48位的Li-1與 子祕鑰Ki進行異或  ;
 *      異或的結果通過S盒(8個盒子 6進4出)生成32位;32位再通過P盒轉換成最後32位F函數處理後的結果
 * 5.加解密運算這邊:先將明文作一個IP置換,而後將64位分紅左右32位L0,R0 而後開始迭代 ;到第16次,作IP逆置換生成最終的密文
 * 
 * 6.解密運算:
 *      加密反過來
 * 
 */

public class DES {
    //初始IP置換
     private int[] IP={
             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};
     //IP逆置換
     private int[] IP1={
             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};
     
     //E擴展
     private int[] ETable={
             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 int[] P={
             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};    

     //S盒
     private static final 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 } }
     };
     
     //PC-1置換表
     private int[] PC1={
             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 int[] PC2={
             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};
     
     //循環左移次數表
     private int[] leftTable = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
     
     //加密輪數16輪
     private static final int LOOP = 16;
     private String[] keys = new String[LOOP];
     private String[] pContent;
     private String[] cContent;
     private int originLength;  //初始明文長度
     
     //16個子密鑰
     private int[][] subKey = new int[16][48];      //存儲16次的子密鑰
     private String content;
     private int pOriginLegth;  //明文初始長度?
     
     //構造函數
    public DES(String key, String content) {
        this.content = content;
        pOriginLegth = content.getBytes().length;
        generateSubKey(key);
    }
    
    //主函數入口
    public static void main(String[] args) {
        String plainText = "SakuraOne";
        System.out.println("明文: \n" + plainText);
        String key = "IAMKEY";
        
        DES des = new DES(key,plainText);
        
        byte[] c = des.group(plainText.getBytes(), true);//加密
        System.out.println("密文:\n" + new String(c));
        
        byte[] p = des.group(c, false); //解密
        byte[] pd = new byte[plainText.getBytes().length];
        System.arraycopy(p, 0, pd, 0, plainText.getBytes().length);
        System.out.println("解密後的明文:\n" + new String(pd));
    
    }
    
    /**
     *拆分分組
     */
    public byte[] group(byte[] plainText, boolean decryption) {
        //填充明文長度爲64位的整數
        originLength = plainText.length;
        int gNum;
        int rNum;
        gNum = originLength/8;
        rNum = 8-(originLength-gNum*8);
        byte[] pPadding;
        if(rNum<8) {
            pPadding = new byte[originLength+rNum];
            System.arraycopy(plainText, 0, pPadding, 0, originLength);
            for(int i=0; i<rNum; i++) {
                pPadding[originLength+1]=(byte)rNum;
            }
        }else {
            pPadding = plainText;
        }
        
        gNum = pPadding.length/8;
        
        byte[] groupPT = new byte[8];   //64位分組單位
        byte[] resultData = new byte[pPadding.length];
        
        for(int i=0; i<gNum; i++) {
            System.arraycopy(pPadding, i*8, groupPT, 0, 8);
            System.arraycopy(encryptUnit(groupPT, subKey, decryption), 0, resultData, i*8, 8);
        }
        
        //若是是解密   這裏感受什麼也沒有作呢??
        if(decryption == false) {
            byte[] pResultData = new byte[pOriginLegth];
            System.arraycopy(resultData, 0, pResultData, 0, pOriginLegth);
            return pResultData;
        }
        
        return resultData;  
    }
    
    /**
     *加密一個64位分組
     *
     */
    public byte[] encryptUnit(byte[]unit, int keysArray[][], boolean decryption) {
        //獲得明文的01字符串
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<8; i++) {
            String tmpBit = Integer.toBinaryString(unit[i] & 0xff);
            while(tmpBit.length()%8!=0) {
                tmpBit="0"+tmpBit;
            }
            sb.append(tmpBit);
        }
        
        //將明文01字符串轉換爲數字01存放在數組中
        int[] pBit = new int[64];
        String pStr = sb.toString();
        for(int i=0; i<64; i++) {
            int bit = Integer.valueOf(pStr.charAt(i));
            if(bit == 48) {
                bit = 0;
            }else if(bit == 49){
                bit = 1;
            }else {
                System.out.println("To bit error");
            }
            pBit[i] = bit;
        }
        
        /*=========IP置換==========*/
        int[] pIP = new int[64];
        for(int i=0; i<64; i++) {
            pIP[i] = pBit[IP[i]-1];
        }
        
        //加密
        if(decryption) {
            //迭代16次
            for(int i=0; i<16; i++) {
                loop(pIP, i, decryption, keysArray[i]);
            }
        }else {             //解密  反向迭代
            for(int i=15; i>-1; i--) {
                loop(pIP, i, decryption, keysArray[i]);
            }
        }
        
        /*===========IP逆置換=============*/
        int[] c = new int[64];
        for(int i=0; i<IP1.length; i++) {
            c[i] = pIP[IP1[i]-1];
        }
        
        byte[] cByte = new byte[8];
        for(int i=0; i<8; i++) {
            cByte[i] = (byte)((c[8*i]<<7)+(c[8*i+1]<<6)+(c[8*i+2]<<5)+(c[8*i+3]<<4)+(c[8*i+4]<<3)+(c[8*i+5]<<2)+(c[8*i+6]<<1)+(c[8*i+7]));
        }
        return cByte;   //最終的密碼字節數組
    }
    
    //依次迭代過程
    public void loop(int[] median, int times, boolean decryption, int[]keyArray ) {
        int[] l0 = new int[32];
        int[] r0 = new int[32];
        int[] l1 = new int[32];
        int[] r1 = new int[32];
        int[] f = new int[32];      //調用F函數後生成的結果
        
        System.arraycopy(median, 0, l0, 0, 32);
        System.arraycopy(median, 32, r0, 0, 32);
        
        l1 = r0;
        f = fFunction(r0, keyArray);    //調用F函數
        
        for(int i=0; i<32; i++) {
            r1[i] = l0[i]^f[i];     //ri = li-1 ^ f[i]
            if(((decryption==false) && (times==0)) || ((decryption==true) && (times==15))) {
                median[i] = r1[i];
                median[i+32] = l1[i];
            }else {
                median[i] = l1[i];
                median[i+32] = r1[i];
            }
        }
    }
    
    /**
     *  F函數
     */
    public int[] fFunction(int[] rContent, int[] key) {
        int[] result = new int[32];
        int[] rXORkey = new int[48];
        
        //ri擴展 與 keyi異或
        for(int i=0; i<ETable.length; i++) {
            rXORkey[i] = rContent[ETable[i]-1]^key[i];
        }
        
        /*=============S-box替換 將48位變成32位==============*/
        int[][] s= new int[8][6];
        int[] sAfter = new int[32];
        
        for(int i=0; i<8; i++) {
            System.arraycopy(rXORkey, i*6, s[i], 0, 6);
            int r = (s[i][0]<<1)+s[i][5];   //橫座標
            int c = (s[i][1]<<3) + (s[i][2]<<2) + (s[i][1]<<1) + s[i][4]; //縱座標
            String str = Integer.toBinaryString(SBox[i][r][c]);
            while(str.length() < 4) {
                str = "0"+str;
            }
            
            for(int j=0; j<4; j++) {
                int p=Integer.valueOf(str.charAt(j));
                if(p==48) {
                    p=0;
                }else if(p==49) {
                    p=1;
                }else {
                    System.out.println("To bit error!");
                }
                sAfter[4*i+j] = p;
            }   
        }
        
        /*===============P盒替換=====================*/
        for(int i=0; i<P.length; i++) {
            result[i] = sAfter[P[i]-1];
        }
        return result;
    }
    
    
    /**
     * description:生成子密鑰
     * 
     * @param key 密鑰
     *    
     */
    public void generateSubKey(String key) {
        //當key的長度小於64位時要擴展至64位
        while(key.length()<8) {
            key = key + key;
        }
        key = key.substring(0, 8);
        
        //將字符密鑰轉換成二進制形式
        byte[] keys = key.getBytes();
        int[] kBit = new int[64];
        
        for(int i=0; i<8; i++) {
            //每一個字節即每8位&0000 0000
            String kStr = Integer.toBinaryString(keys[i] & 0xff);
            //補齊8位
            if(kStr.length()<8) {
                for(int t=0; t<8-kStr.length(); t++) {
                    kStr = "0" + kStr;
                }
            }
            
            //將01字符串轉換成二進制01
            for(int j=0; j<8; j++) {
                int p = Integer.valueOf(kStr.charAt(j));
                if(p == 48) {
                    p=0;
                }else if(p == 49) {
                    p=1;
                }else {
                    System.out.println("To bit error!");
                }
                kBit[i*8+j] = p;
            }
        }
        
        //獲得kBit 初始化的64位密鑰 而後進行PC-1壓縮成56位
        
        /*==============PC-1壓縮===============*/
        int[] kNewBit = new int[56];
        for(int i=0; i<PC1.length; i++) {
            kNewBit[i] = kBit[PC1[i]-1];
        }
        
        /*================初始密鑰分組=============*/
        int[] c0 = new int[28];
        int[] d0 = new int[28];
        System.arraycopy(kNewBit, 0, c0, 0, 28);
        System.arraycopy(kNewBit, 28, d0, 0, 28);
        
        //生成16個子密鑰
        for(int i=0; i<16; i++) {
            int[] c1 = new int[28];
            int[] d1 = new int[28];
            
            /*============ci、di分別循環左移===========*/
            if(leftTable[i] == 1) {
                System.arraycopy(c0, 1, c1, 0, 27);
                c1[27]=c0[0];
                System.arraycopy(d0, 1, d1, 0, 27);
                d1[27]=d0[0];
            }else if(leftTable[i] == 2) {
                System.arraycopy(c0, 2, c1, 0, 26);
                c1[26]=c0[0];
                c1[27]=c0[1];
                
                System.arraycopy(d0, 2, d1, 0, 26);
                d1[26]=d0[0];
                d1[27]=d0[1];
            }else {
                System.out.println("leftTable error!");
            }
            
            /*================ci、di合併 PC-2壓縮置換=============*/
            int[] tmp = new int[56];
            System.arraycopy(c1, 0, tmp, 0, 28);
            System.arraycopy(d1, 0, tmp, 28, 28);
            for(int j=0; j<PC2.length; j++) {
                subKey[i][j] = tmp[PC2[j]-1];
            }
            c0 = c1;
            d0 = d1;
        }
    }
}

小結

這學期在上密碼學的課程,課堂上聽老師講了對稱加密算法中的DES算法,一直以爲挺繞。在上完實驗課後勉強對其算法流程有了一個清晰認識。後面想着用算法實現或許會更明瞭, 因而寫代碼實現。確實,本身實現一遍後對算法會更理解。粗糙地記錄了下DES加解密的實現,以供參考。解密算法的驗證須要你們另覓資料,本篇博文就再也不介紹了~

參考:

DES算法原理與Java實現https://blog.csdn.net/android_jiangjun/article/details/79654940

相關文章
相關標籤/搜索