(加密基礎)DES篇

本文章分析非常淺顯,java代碼寫的不夠細緻,只考慮了標準的DES加密輸出,並未將結果轉化爲hex或者base64輸出,若有不當之處歡迎各位大佬指出

DES加密

DES加密是一種對稱加密算法,被廣泛應用與協議傳輸、本地數據加密、加密用戶登錄結果信息並序列化到本地磁盤

一.C語言實現和原理解析

說明:理論部分借鑑看雪密碼學大佬:https://bbs.pediy.com/thread-253558.htm#des%E7%AE%97%E6%B3%95%E7%9A%84%E8%A7%A3%E5%AF%86%E6%96%B9%E6%B3%95%EF%BC%9A

DES算法使用Feistel框架和分組密碼原理
 流程圖如下鏈接: https://bbs.pediy.com/upload/attach/202002/813468_24VWUXQHSW3DQWC.png

1.明文初始置換

通過置換表對傳入的數據進行亂序處理

置換表如下:

在這裏插入圖片描述
例如:假如原數據的第58位爲1,那麼經過置換後,第0行、0列的值就等於1

在這裏插入圖片描述
代碼實現:

const unsigned char IP_Table[64] =
{
    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
};
 
int IP_Substitution(const unsigned char* BitPlain, unsigned char* Bit_IP_Table)
{
    int ret = 0;
 
    for (int i = 0; i < 64; i++)
    {
        Bit_IP_Table[i] = BitPlain[IP_Table[i] - 1];
    }
 
    return ret;
}

2.分組(L,R組)

在這裏插入圖片描述

按照上下進行分組

unsigned char Bit_IP_Table[64];      //初始置換後的明文表
unsigned char BitL_Table[17][32];    //L表Bit組
unsigned char BitR_Table[17][32];    //R表Bit組
 
memcpy(BitL_Table[0], Bit_IP_Table,         32);
memcpy(BitR_Table[0], &Bit_IP_Table[32],    32);

3.右分組(R組)由32位擴展爲48位

通過擴展置換變對R組進行擴展,原理和前面的置換表差不多,就是多了一些重複的映射

在這裏插入圖片描述

代碼實現:

const unsigned char E_Table[48] =
{
    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
};
 
int E_Substitution(const unsigned char* BitR_Table, unsigned char* BitE_Table)
{
    int ret = 0;
 
    for (int i = 0; i < 48; i++)
    {
        BitE_Table[i] = BitR_Table[E_Table[i] - 1];
    }
 
    return ret;
}
```c
#### 4.擴展的R組和48位密鑰進行異或
```c
int DES_XOR(const unsigned char* Bit1_Table, const unsigned char* Bit2_Table, unsigned char* Bit_Xor/*異或運算的結果*/, int nBit/*異或運算的位數*/)
{
	int ret = 0;
	for (int i = 0; i < nBit; i++)
	{
		Bit_Xor[i] = Bit1_Table[i] ^ Bit2_Table[i];
	}

	return ret;
}

5.將48位異或的結果轉換爲32位

詳細過程:先將48爲分成8組,每組6位,在將6位數字經過變化轉換爲4位

6->4操作:

例:(一個6位的分組)

1 0 0 1 0 1

將該分組的第一位和最後一位取出來,以二進制的形式表示,作爲行索引

將該分組的第2到5位取出來,以二進制的形式表示,作爲列索引

代碼:

const unsigned char S_Table[8][4][16] =
{
    //S1盒
    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,
 
    //S2盒
    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,
 
    //S3盒
    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,
 
    //S4盒
    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,
 
    //S5盒
    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,
 
    //S6盒
    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,
 
    //S7盒
    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,
 
    //S8盒
    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
};
 
 
unsigned char Bit_Xor[8][6];          //存放異或運算的結果
unsigned char Bit_Integer[8][4];      //將整數變成Bit位
unsigned char Row;                    //S盒的行號
unsigned char Col;                    //S盒的列號
unsigned char Integer;                //從S盒中取得的32位整數
 
for (int i = 0; i < 8; i++)
{
    //計算S盒的行號和列號
    Row = (Bit_Xor[i][0] << 1) + Bit_Xor[i][5];
    Col = (Bit_Xor[i][1] << 3) + (Bit_Xor[i][2] << 2) + (Bit_Xor[i][3] << 1) + Bit_Xor[i][4];
 
    //從S盒中取得整數
    Integer = S_Table[i][Row][Col];
 
    //將取得的4Bit數轉換成Bit組
    for (int j = 0; j < 4; j++)
    {
        Bit_Integer[i][j] = Integer >> (3 - j) & 1;
    }
}

6.p置換

同最開始的初始置換,只是對數據進行了打亂操作
在這裏插入圖片描述

const unsigned char P_Table[32] =
{
    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
};
 
int P_Substitution(const unsigned char *Bit_Integer, unsigned char* BitP_Table)
{
    int ret = 0;
 
    for (int i = 0; i < 32; i++)
    {
        BitP_Table[i] = Bit_Integer[P_Table[i] - 1];
    }
 
    return ret;
}

7、將返回的結果和L組進行異或操作,作爲新的R組,並將舊的R組賦值給新的L組

僞代碼:


R[i+1] = L[i] ^ F(R[i], K[i]);
L[i+1] = R[i];

8.按照3、4、5、6、7步重複16次

for (int i = 0; i < 16; i++)
{
    //將R組和子密鑰組進行F函數運算
    DES_F_Function(BitR_Table[i], BitSubKey[i], Bit_F_Out);
 
    //L組盒F函數的輸出結果進行異或運算
    DES_XOR(BitL_Table[i], Bit_F_Out, BitR_Table[i + 1], 32);
 
    //Li+1 = Ri
    memcpy(BitL_Table[i + 1], BitR_Table[i], 32);
}
 
//L[16]和R[16]進行交叉合併
memcpy(BitRL_Table,         BitR_Table[16], 32);
memcpy(&BitRL_Table[32],    BitL_Table[16], 32);

9.逆初始值置換

對上述16步操作後的結果進行逆初始值置換操作,也是類似於初始置換表

const unsigned char reIP_Table[64] =
{
    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
};
 
int reIP_Substitution(const unsigned char *BitRL_Table, unsigned char *Bit_reIP_Table)
{
    int ret = 0;
 
    for (int i = 0; i < 64; i++)
    {
        Bit_reIP_Table[i] = BitRL_Table[reIP_Table[i] - 1];
    }
 
    return ret;
}

10.密鑰的擴展和加密

PC-1置換
將64位密鑰中的8、16、24、32、40、48、56、64位剔除作爲校驗位。
其餘的位進行置換表操作

const unsigned char PC_1_Table[56] =
{
    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
};
 
int PC_1_Substitution(const unsigned char *BitKey, unsigned char *BitKey_PC_1)
{
    int ret = 0;
 
    for (int i = 0; i < 56; i++)
    {
        BitKey_PC_1[i] = BitKey[PC_1_Table[i] - 1];
    }
 
    return ret;
}

然後對56位數據進行分組,分成C、D組,採用上下分組,再進行16次變化,每次變化後會進行(PC-2置換)生成一次密鑰用作每一輪的加密

每輪變化都進行整體左位移操作(並把前面超出範圍的兩位數據移動到數據的最後面),根據具體的輪數位移的位數也不同,其中1、2、9、16輪中,位移1位,其餘輪位移2位

const unsigned char Bit_Round[16] =
{
    1, 1, 2, 2,
    2, 2, 2, 2,
    1, 2, 2, 2,
    2, 2, 2, 1
};
 
int BitRound_L(const unsigned char* SrcBitGroup, unsigned char* DesBitGroup, int nBit)
{
    int ret = 0;
 
    memcpy(DesBitGroup,             &SrcBitGroup[nBit], 28 - nBit);
    memcpy(&DesBitGroup[28 - nBit], SrcBitGroup,        nBit);
 
    return ret;
}
 
//將C、D兩組進行輪轉移位操作    左移
BitRound_L(BitC_Table[i], BitC_Table[i + 1], Bit_Round[i]);
BitRound_L(BitD_Table[i], BitD_Table[i + 1], Bit_Round[i]);
密鑰生成算法PC-2置換

PC-2置換
置換嘛,不多說了

const unsigned char PC_2_Table[48] =
{
    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
};
 
int PC_2_Substitution(const unsigned char *BitKey, unsigned char *SubKey)
{
    int ret = 0;
 
    for (int i = 0; i < 48; i++)
    {
        SubKey[i] = BitKey[PC_2_Table[i] - 1];
    }
 
    return ret;
}

C語言實現完成!!

二、java實現

根據模式的不同有很多實現方式,現展示一種方式
,主要抓住特徵private static final String ALGORITHM = "DES";來對加密方式進行判斷,標準的DES算法以字符串輸出時很容易產生亂碼,因此輸出時需要將其轉化爲hex或者base64編碼的形式

package decrypt01;
import java.util.Base64;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

public final class DES {

    private static final String ALGORITHM = "DES";
    private static final String TRANSFORMATION = "DES/ECB/PKCS5Padding";

    //利用8個字節64位的key給src加密
    public static byte[] encrypt(byte[] src,byte[]key)
    {
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM);
            KeySpec keySpec = new DESKeySpec(key);
            SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey,new SecureRandom());
            byte[] enMsgBytes = cipher.doFinal(src);    
            return enMsgBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //利用8個字節64位的key給src解密
    public static byte[] decrypt(byte[] encryptBytes,byte[]key){
        try {
            Cipher deCipher = Cipher.getInstance(TRANSFORMATION);
            SecretKeyFactory deDecretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM);
            KeySpec deKeySpec = new DESKeySpec(key);
            SecretKey deSecretKey = deDecretKeyFactory.generateSecret(deKeySpec);
            deCipher.init(Cipher.DECRYPT_MODE, deSecretKey,new SecureRandom());
            byte[] deMsgBytes = deCipher.doFinal(encryptBytes);
            return deMsgBytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
	public static String encode(byte[] key) {
	    return new String(Base64.getEncoder().encode(key));
	    //����base64���ܰ�
	}
	public static String decode(byte[] key) {
	    return new String(Base64.getDecoder().decode(key));
	}
    private static String key = "12345678";

    public static void main(String[] args) throws Exception{
        String msg = "12345678";
        System.out.println("加密前:"+msg);
       byte[] encryptBytes = DES.encrypt(msg.getBytes(),key.getBytes());
        System.out.println("加密後:"+new String(encryptBytes));
        byte[] deMsgBytes = DES.decrypt(encryptBytes,key.getBytes());
        System.out.println("解密後:"+new String(deMsgBytes));
    }

}

三、3DES java實現

3DES是DES經過改進的結果,增強了其密鑰的長度



import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class do3DES {

   //key 根據實際情況對應的修改
   private final byte[] keybyte="123456788765432112345678".getBytes(); //keybyte爲加密密鑰,長度爲24字節
   private static final String Algorithm = "DESede"; //定義 加密算法,可用 DES,DESede,Blowfish
   private SecretKey deskey;
   //生成密鑰
   public do3DES(){
       deskey = new SecretKeySpec(keybyte, Algorithm);
   }
   //加密
   public byte[] encrypt(byte[] data){
        try {
            Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, deskey);
            return cipher.doFinal(data);
        } catch (Exception ex) {
            //加密失敗,打日誌
            ex.printStackTrace();
        } 
        return null;
   }
   //解密
   public byte[] decrypt(byte[] data){
       try {
            Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE,deskey);
            return cipher.doFinal(data);
        } catch (Exception ex) {
            //解密失敗,打日誌
            ex.printStackTrace();
        } 
        return null;
   }

   public static void main(String[] args) throws Exception {
	   do3DES des=new do3DES();
       String req ="cryptology";

       String toreq  = toHexString(req);
       System.err.println("十六進制報文=="+toreq);
       byte[] srcData=req.toString().getBytes("utf-8");
       byte[] encryptData=des.encrypt(srcData);
       System.out.println("密文:");
       if(encryptData!=null){
           for(int i=0;i<encryptData.length;i++){
               String hex=Integer.toHexString(encryptData[i]);
               if(hex.length()>1)
                System.out.print(hex.substring(hex.length()-2)+" ");
               else
                System.out.print("0"+hex+" ");
           }
       }
       System.out.println("");
       System.out.println("明文:"+req);
   }

   // 轉化字符串爲十六進制編碼
   public static String toHexString(String s) {
       String str = "";
       for (int i = 0; i < s.length(); i++) {
           int ch = (int) s.charAt(i);
           String s4 = Integer.toHexString(ch);
           str = str + s4;
       }
       return str;
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章