數據加密算法--詳解DES加密算法原理與實現

DES算法簡介

DES(Data Encryption Standard)是目前最爲流行的加密算法之一。DES是對稱的,也就是說它使用同一個密鑰來加密和解密數據。算法

DES仍是一種分組加密算法,該算法每次處理固定長度的數據段,稱之爲分組。DES分組的大小是64位,若是加密的數據長度不是64位的倍數,能夠按照某種具體的規則來填充位。數組

從本質上來講,DES的安全性依賴於虛假表象,從密碼學的術語來說就是依賴於「混亂和擴散」的原則。混亂的目的是爲隱藏任何明文同密文、或者密鑰之間的關係,而擴散的目的是使明文中的有效位和密鑰一塊兒組成儘量多的密文。二者結合到一塊兒就使得安全性變得相對較高。安全

DES算法具體經過對明文進行一系列的排列和替換操做來將其加密。過程的關鍵就是從給定的初始密鑰中獲得16個子密鑰的函數。要加密一組明文,每一個子密鑰按照順序(1-16)以一系列的位操做施加於數據上,每一個子密鑰一次,一共重複16次。每一次迭代稱之爲一輪。要對密文進行解密能夠採用一樣的步驟,只是子密鑰是按照逆向的順序(16-1)對密文進行處理。數據結構

計算16個子密鑰

上面提到DES算法的第一步就是從初始密鑰中計算得出16個子密鑰。圖示1展現了這個過程。DES使用一個56位的初始密鑰,可是這裏提供的是一個64位的值,這是由於在硬件實現中每8位能夠用於奇偶校驗,在軟件實現中多出的位只是簡單的忽略掉。要得到一個56位的密鑰,能夠執照表1的方式執行密鑰轉換。解釋一下表1,按照從左往右從上往下的方式看,表格中每一個位置P包含初始密鑰中位在轉換後的密鑰中所佔的位置。好比,初始密鑰中的第57位就是轉換後的密鑰中的第1位,而初始密鑰中的第49位則變成轉換後的密鑰中的第2位,以此類推...。(數據位的計數順序按照從左到右從1開始的)app

表1:DES中密鑰的轉換表(DesTransform[56])函數

 

將密鑰轉換爲56位後,接下來計算子密鑰。首先,將56位的密鑰分爲兩個28位的組。而後,針對每一個子密鑰,根據子密鑰的序列值(也就是16個子密鑰中的第幾個)旋轉這兩組值(旋轉的位數見表2),而後從新合併。以後,再按照表3所示對重組後的密鑰進行置換,使56位的子密鑰縮小爲48位(注意表3只有48位,丟棄了8位)這個排列過程就稱爲置換選擇加密

針對16個子密鑰,每一個子密鑰重複一次該過程。這裏的目的是保證將初始密鑰中的不一樣位在每一輪排列後應用於加密的數據上。spa

表2:針對DES子密鑰每一輪的旋轉次數(Round輪,Rotations旋轉次數)(DesRotations)3d

表3:DES子密鑰的置換選擇(Despermuted[48])code

圖1:在DES中計算子密鑰的過程

 

加密和解密數據塊

 

通過上述過程,咱們已經準備好了子密鑰。接着就能夠加密和解密數據塊了。圖2展現了這個過程。

圖2:DES中加密和解密數據塊

 

從表4所示的方式置換64位的數據塊開始,該置換過程稱爲初始置換。該過程並不會增長DES的安全性,但這種作法在16位和32位的總線出現以前將使得數據更容易加載到DES芯片中。雖然這種處理已經不合時宜,但該置換過程仍然保留以知足DES標準。

表4:DES中數據的初始置換(DesInitial[64])

通過初始置換後,64位的數據塊分爲兩個32位的組,L0和R0

完成初始置換後,數據塊將重複執行16輪一系列的操做。每一輪操做(i)的目的是計算出Li和Ri ,這些結果將用在下一輪操做中直到最終獲得數據R16和L16

每一輪以Li-1和Ri-1開始,而後根據表5所示進行擴展置換,將Ri-1從32位擴展到48位。該置換的主要目的是在加密數據的過程當中製造一些雪崩效應,使用數據塊中的1位將在下一步操做中影響更多位,從而產生擴散效果

一旦擴展置換完成,計算出48位的結果值與這一輪子密鑰Ki的異或值(XOR,符號計爲⊕)。這將產生48位的中間值,記爲Rint

若是將E計爲擴展置換的結果,則本輪到目前爲止的操做能夠表示爲:

Rint = E(Ri-1) ⊕ Ki

表5:DES中數據塊的擴展置換(DesExpansion[48])

下一步,Rint 須要經過8個單獨的S盒執行8次替換操做。每一個S盒(j)從Rint的6j 到 6j+6 的位置取出6位,併爲其在表6中查出1個4位的值,將該值寫到緩衝區的4j位置處(如圖3)。

圖3:DES中的8個S盒

讀表6,查找S盒(j)。經過前面取出的6位值,根據第1位和最後1位組成的2位值找到表6中的行號,而根據中間剩下的4位來肯定表6中的列號。好比,在圖3中,Rint中的第3個6位組是101011。所以,在表6中查找到的第3個S盒是9。由於行號等於112 = 3,列號等於01012 = 5(查表時從索引0開始計數)。S盒爲數據增長了不肯定性,除了給DES帶來安全性外,沒什麼特別的。

表6:DES中數據塊的S盒替換

一旦完成了S盒替換,獲得的結果又變爲一個32位的值。接下來再經過P盒來置換。以下表7所示。

表7:DES中數據塊的P盒置換

到目前爲止,咱們把這一輪的操做想象爲一個函數,通常記爲f。若是 bj 表明Rint中的第j個6位組,Sj 表明第j個S盒,而P表明P盒置換,則該函數能夠定義爲

f = P(S1(b1),S2(b2),...,S8(b8))

每一輪的最後一個操做是計算 f 的32位結果值與傳入本輪操做的原始數據的左分組Li-1之間的異或值。

一旦完成,將左右兩個分組交換而後開始下一輪

在最後一輪中,不用交換左右分組。

把全部的步驟連起來,在每一輪中計算Li和Ri的步驟能夠精確表示爲:

Li = Ri-1

Ri = Li-1  f(Ri-1,Ki)

當所有的16輪操做都結束後,將最後的右分組R16和最後剩下的左分組L16鏈接起來,組成一個64位的分組R16L16。(回顧一下,在最後一輪中左右分組並無交換。最後的右分組在左邊而最後的左分組在右邊。)

最後一步是將R16L16按照表8所示的置換進行置換。簡而言之,就是撤消以前的初始置換。

加密數據時,最終結果就是一個64位的密文,而當解密數據時,最終結果就是64位的明文了。

表8:DES中數據塊的最終置換

 DES的接口定義 

des_encipher


 

void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, unsigned char *key);

返回值:無

描述採用DES算法,將明文plaintext的一個64位的明文組加密。在key中指定64位的密鑰(最後8位將被忽略掉,實際獲得56位的密鑰)。ciphertext是返回的64位的密文組。

由調用者負責管理ciphertext所須要的空間。要加密一段較大的數據,能夠按照分組加密模式調用des_encipher。爲了獲得較高的效率,des_encipher能夠重用以前的調用中計算出來的子密鑰,這能夠經過在以後的調用中將NULL傳給key,以此來開啓這種功能。

複雜度:O(1)

des_decipher


void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, unsigned char *key);

返回值:無

描述採用DES算法將密文ciphertext的一個64位分組解密。該函數假設ciphertext包含的是經過des_encipher加密過的密文。在key中指定64位的密鑰(最後8位將被忽略掉,實際獲得56位的密鑰)。plaintext是返回的64位的明文組。由調用者負責管理plaintext所須要的空間。要解密一大段的數據,能夠按照分組加密模式調用des_decipher。爲了得到較高的效率,des_decipher能夠重用以前調用中計算出來的子密鑰。能夠在隨後的調用中將NULL傳給key,以此來開啓這種功能。

複雜度:O(1)

DES算法的實現

考慮到DES算法中涉及的位操做不少,所以DES算法一般都是在硬件中實現。DES算法中的圖表和術語(經過線、框畫的流程圖,以及諸如S盒、P盒這樣的術語)使其更傾向於在硬件中實現,固然,軟件實現也有它的價值所在。在軟件開發中,經過幾種基本的指令操做來幫助實現DES中的各類置換、轉換以及替換操做都是頗有效的。

des_encipher

函數des_encipher將明文的一個64位的明文分組經過DES算法加密。

因爲DES的一個很好的特色是一樣的過程既能用來加密數據也能用來解密數據,所以des_encipher只須要簡單的調用des_main,而des_decipher一樣也只須要調用des_main便可。

函數des_main經過使用其參數direction來肯定到參數source提供的數據是明文仍是密文。direction參數只是簡單地修改子密鑰的順序。

在des_encipher中,direction設置爲encipher

函數des_main()首先檢測參數key是否爲NULL。這將容許des_encipher的調用者重用上一次調用時計算出來的子密鑰。將子密鑰數組subkeys聲明爲static類型。若是key不爲NULL,將計算子密鑰。

要計算子密鑰,能夠按照前面介紹過的步驟(計算16個子密鑰)來執行。key的轉換是經過函數permute來實現的這裏就是根據一個特定的表在一個緩衝區中置換位。假設表中的每一個位置(i)上都存在一個值p,函數permute經過將位置p的位移動到位置(i)上來完成對傳入的buffer的置換。

要置換密鑰,將表Des_Transform(同表1)傳給函數permute。必要的旋轉操做能夠經過調用位操做bit_rot_left來實現。該操做將buffer按照指定的位數向左旋轉。每一輪要正確旋轉28位的子密鑰分組,將表Des_Rotations(同表2)中合適的元素傳給bit_rot_left。經過調用permute,並把傳入表Des_permuted(同表3),來對每個子密鑰作置換選擇。

 要加密一個數據塊,首先要執行初始置換。爲實現這一步,首先調用函數permute並將表Des_Initial(同表4)傳入。而後,將數據分爲兩個32位的分組:lblk以及rblk。回顧一下,加密數據的大部分工做都是將一系列的操做重複執行16輪。每一輪的主要工做都花在計算函數(f)的值上,將值保存在fblk中。

每一輪操做開始時,將對rblk執行一個擴展置換。爲了實現這個步驟,將調用函數permute,並把表Des_Expansion(同表5)傳入。而後,經過調用位操做bit_xor來計算擴展後的rblk和某個適當的子密鑰的異或值。不管是加密仍是解密數據,與本輪操做相關的子密鑰都須要參與執行。一旦完成了異或計算,將對結果執行一系列的S盒替換操做。Des_Sbox(同表6)定義了8個用於DES算法中的S盒。對於當前fblk中的每一個6位分組,第1位和最後1位聯合起來肯定Des_Sbox中的行號,而中間的4位用來肯定列號。最後執行P盒置換來完成函數f的計算。經過調用permute函數並傳入表Des_Pbox(同表7)來實現這個步驟。計算出lblk與函數f的值的異或結果,並交換lblk和rblk來結束一輪的操做。

將上述過程重複16次,每輪一次。當所有16輪操做完成後,將rblk拷貝到target的前32位中,將lblk拷貝到以後的32位中(按照要求,最後一輪不交換lblk和rblk)。最終,經過調用permute並把Des_Final(同表8)傳入來完成最後的置換操做。

des_encipher的時間複雜度是O(1),由於加密數據塊的全部步驟都能在恆定的時間內完成。

des_decipher

函數des_decipher將一個64位的密文分組經過DES算法進行解密。同des_encipher同樣,des_decipher實際經過調用des_main來完成解密任務,但這裏direction須要設置爲decipher。所以,des_decipher的工做方式同des_encipher同樣,只是這裏的子密鑰須要以逆序的方式參與。具體來講,就是在des_main中,針對每一輪i(從0開始計數),參與計算的子密鑰爲subkeys數組中下標爲(15-i)的元素。

des_decipher的時間複雜度爲O(1),由於解密數據塊中的全部步驟均可以在恆定的時間內完成。

示例1:數據加密的頭文件(DES算法和RSA算法通用頭文件)

/*encrypt.h*/
#ifndef ENCRYPT_H
#define ENCRYPT_H

/*在一個安全實現中,Huge 最少要400位10進制數字*/
typedef unsigned long Huge; 

/*爲RSA公鑰定義一個數據結構*/
typedef struct RsaPubKey_
{
    Huge e;
    Huge n;
}RsaPubkey;

/*爲RSA私鑰定義一個數據結構*/
typedef struct RsaPriKey_
{
    Huge d;
    Huge n;
}RsaPriKey;

/*函數聲明*/
void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key);
void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key);
void rsa_encipher(Huge plaintext, Huge *ciphertext, RsaPubKey pubkey);
void rsa_decipher(Huge ciphertext,Huge *plaintext, RsaPriKey prikey);

#endif // ENCRYPT_H

 示例2:DES算法的實現

/*des.c*/
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "bit.h"
#include "encrypt.h"

/*定義一個密鑰置換的映射*/
static const int DesTransform[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
};
/*定義用於計算子密鑰的旋轉次數*/
static const int DesRotations[16] =
{
    1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
};
/*定義用於子密鑰置換選擇的映射*/
static const int DesPermuted[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    
};
/*定義用於數據塊初始化轉換的映射*/
static const int DesInitial[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
};
/*定義用於數據塊擴輾轉換的映射*/
static const int DesExpansion[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,22,23,24,25,
    24,25,26,27,28,29,28,29,30,31,32,1
};
/*定義用於數據塊中S盒轉換的S盒表*/
static const int DesSbox[8][4][16] =
{
    {
        {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盒映射表*/
static const int DesPbox[32] =
{

};
/*定義用於數據塊最終轉換的映射*/
static const int DesFinal[64] =
{

};

/*定義一個枚舉類型,用於選擇是加密或是解密數據*/
typedef enum DesEorD_ {encipher,decipher} DesEorD;

/*permute函數  用於轉換、改變位序列*/
static void permute(unsigned char *bits, const int *mapping, int n)
{
    unsigned char *temp[8];
    int i;

    /*使用n位映射映射緩衝區*/
    memset(temp, 0, (int)ceil(n/8));

    for(i=0; i<n; i++)
        bit_set(temp, i, bit_get(bits,mapping[i]-1));

    memcpy(bits, temp, (int)ceil(n/8));
    return;
}

/*des_main函數  加密或解密數據的計算函數*/
static int des_main(const unsigned char *source, unsigned char *target, const char *key, DesEorD direction)
{
    static unsigned char subkeys[16][7];
    unsigned char temp[8],
                  lkey[4],
                  rkey[4],
                  lblk[6],
                  rblk[6],
                  fblk[6],
                  xblk[6],
                  sblk;
    int row,col,i,j,k,p;

    /*若是key等於NULL,重用上次調用時計算出來的子密鑰,不然計算子密鑰*/
    if(key != NULL)
    {
        /*建立一個key的副本*/
        memcpy(temp,key,8);

        /*將key轉換並壓縮至56位*/
        permute(temp,DesTransform,56);

        /*將key分爲兩個28位的組*/
        memset(lkey,0,4);
        memset(rkey,0,4);

        for(j=0; j<28; j++)
            bit_set(lkey, j, bit_get(temp,j));

        for(j=0; j<28; j++)
            bit_set(rkey, j, bit_get(temp,j+28));

        /*計算每一輪的子密鑰*/
        for(i=0; i<16; i++)
        {
            /*根據定義好的位數對每一塊進行旋轉*/
            bit_rot_left(lkey,28,DesRotations[i]);
            bit_rot_left(rkey,28,DesRotations[i]);

            /*從新合併兩個塊*/
            for(j=0; j<28; j++)
                bit_set(subkeys[i],j,bit_get(lkey,j));

            for(j=0; j<28; j++)
                bit_set(subkeys[i],j+28,bit_get(rkey,j));

            /*對子密鑰作轉換選擇並壓縮至48位*/
            permute(subkeys[i],DesPermuted,48);
        }
    }
    /*建立source參數的副本*/
    memcpy(temp, source, 8);

    /*初始轉換數據塊*/
    permute(temp, DesInitial, 64);

    /*將源數據塊分爲大小爲32位的左右兩個數據塊*/
    memcpy(lblk, &temp[0], 4);
    memcpy(rblk, &temp[4], 4);

    /*加密或解密源數據*/
    for(i=0; i<16; i++)
    {
        /*開始f緩衝衝的計算*/
        memcpy(fblk,rblk,4);

        /*置換、擴展右數據塊的拷貝,使其達到48位*/
        permute(fblk, DesExpansion, 48);

        /*根據direction的值來應用適當的子密鑰*/
        if(direction == encipher)
        {
            /*加密數據,子密鑰組以遞增的順序應用*/
            bit_xor(fblk, subkeys[i], xblk, 48);
            memcpy(fblk, xblk, 6);
        }
        else
        {
            /*解密數據,子密鑰組以遞減的順序應用*/
            bit_xor(fblk, subkeys[15-i], xblk, 48);
            meycpy(fblk, xblk, 6);
        }

        /*執行S盒替換*/
        p=0;
        for(j=0; j<8; j++)
        {
            /*計算出S盒表中的行號和列號*/
            row = (bit_get(fblk,(j*6)+0)*2) + (bit_get(fblk,(j*6)+5)*1);
            col = (bit_get(fblk,(j*6)+1)*8) + (bit_get(fblk,(j*6)+2)*4) +
                  (bit_get(fblk,(j*6)+3)*2) + (bit_get(fblk,(j*6)+4)*1);
            /*爲當前的6位數據塊作S盒替換*/
            sblk = (unsigned char)DesSbox[j][row][col];
            for (k=4; k<8; k++)
            {
                bit_set(fblk,p,bit_get(&sblk,k));
                p++;
            }
        }

        /*爲f緩衝區執行P盒替換*/
        permute(fblk, DesPbox, 32);

        /*計算左數據塊與f緩衝區的異或值*/
        bit_xor(lblk, fblk, xblk, 32);

        /*設置本輪的左數據塊*/
        memcpy(lblk, rblk, 4);
        /*設置本輪的右數據塊*/
        memcpy(rblk, xblk, 4);
    }

    /*將目標正文設置爲從新鏈接的左右兩個數據塊*/
    memcpy(&target[0], rblk, 4);
    memcpy(&target[4], lblk, 4);

    /*執行最終置換*/
    permute(target, DesFinal, 64);

    return 0;
}

/*des_encipher DES加密數據*/
void des_encipher(const unsigned char *plaintext, unsigned char *ciphertext, const unsigned char *key)
{
    des_main(plaintext, ciphertext, key, encipher);
    return;
}

/*des_decipher DES解密數據*/
void des_decipher(const unsigned char *ciphertext, unsigned char *plaintext, const unsigned char *key)
{
    des_main(ciphertext, plaintext, key, decipher);
    return;
}
相關文章
相關標籤/搜索