1. 程式人生 > >密碼學安全演算法--對稱加密演算法

密碼學安全演算法--對稱加密演算法

對稱加密也稱為常規加密、單鑰加密,在非對稱加密(公鑰加密)開發之前是唯一使用的加密型別,直到現在,它也仍然是使用最廣泛的加密型別之一。最常見對稱加密演算法是:DES、3DES、AES、RC4。

對稱加密演算法基本原理

先上圖,對稱加密工作過程
對稱加密演算法原理
在整個工作過程中涉及到以下幾個概念
- 明文:也就是原始資訊或者說原始資料。也就是上圖中的A。
- 加密演算法:對明文進行各種替換或轉換操作的一種演算法。也就是①過程執行的演算法。
- 金鑰:金鑰也是加密演算法的輸入,加密演算法進行替換或轉換的具體操作依賴於這個金鑰。也就是上圖中描述的金鑰Key。
- 密文:經過加密演算法打亂的訊息輸出。密文的輸出取決於明文與金鑰,對於相同的明文,不同的金鑰會產生不同的密文。也就是上圖中的B。
- 解密演算法

:本質上來說就是加密演算法的逆過程,演算法輸入的是密文和加密時使用的同一金鑰。

對稱加密的分類:流加密與分組加密

  1. 流加密每次加密資料流的一位(bit)或者一個位元組(byte)。如RC4。
  2. 分組加密(通常也成為塊加密)是將明文進行分組,加密演算法對每個分組分別加密,通常明文分組和加密後得到的密文分組等長。典型的分組大小是64bit或128bit。如DES,3DES,AES。

DES

Data Encryption Standard 資料加密標準,該標準中的資料加密演算法(Data Encryption Algorithm),簡稱DEA,國內對DES和DEA兩個術語的使用有點混亂。DES是IBM公司發明的,在1977年應徵作為美國國家密碼標準方案。隨著CPU計算速度的提升,以及硬體成本的下降,1999年美國國家標準技術研究所頒佈新標準,規定DES只能用於歷史遺留系統以及3DES中。
DES採用64bit分組長度

56bit金鑰長度,經過一系列變換得到64bit的密文輸出。解密使用相同的金鑰對密文進行解密運算。

3DES

金鑰大小(bit)金鑰個數每微秒執行一次解密所需要的時間每微妙執行一百萬次解密所需要的時間
56256 = 7.2*1016255μs=1142年10.01小時
隨著軟硬體技術的發展,多核CPU、分散式計算、量子計算等理論的實現,DES在窮舉方式的暴力攻擊下還是相當脆弱的,因此很多人想辦法用某種演算法替代它,面對這種需求廣泛被採用的有兩種方案:
1. 設計一套全新的演算法,例如AES,這個在後面會說到。
2. 為了保護已有軟硬體的投資,仍使用DES,但使用多個金鑰進行多次加密,這就是多重DES加密。

雙重加密

雙重加密是多重加密最簡單的形式,使用兩個金鑰。使用表示式敘述如下:

C:      代表加密後的密文(crypted)
E:      代表加密演算法(encrypt)
D:      代表解密演算法(decrypt)
K1,K2:  代表兩個金鑰(key)
P:      代表明文(proclaimed)

        加密過程:
        C = E( K2, E( K1, P ) )
        解密過程:
        P = D( K1, D( K2, C) )

雙重DES加密表面上進行了兩次加密,從而金鑰長度為56×2=112bit,密碼強度增加了,但是在1992年在一篇論文中被證實存在下面的情況:

E(K2, E(K1, P)) = E(K3, P)

也就是說存在金鑰K3能解密經過K1,K2進行雙重加密的密文,這意味著雙重DES退化成了單重DES了。顯然這種方式是不安全的。

使用兩個金鑰的三重加密

加密過程:
    C = E( K1, D( K2, E( K1, P ) )
解密過程:
    P = D( K1, E( K2, D( K1, C ) )

這個加密方式就有意思了,先用K1進行加密,然後對加密的密文用K2進行解密,解密後的密文再用K1進行加密;解密過程就與之相反,並且該演算法當k1==k2時能與單重DES相容。這個奇特的想法最後也成為了美國的加密標準之一X9.17。

使用三個金鑰的三重加密

加密過程:
    C = E( K3, D( K2, E( K1, P ) )
解密過程:
    P = D( K3, E( K2, D( K1, C ) )

這種加密方式1998年被列為美國加密標準X9.52,並且已經廣泛地替代了DES。過程很好理解。如果需要與DES進行相容,只需設定K3=K2或K2=K1;如果需要與前面的雙重金鑰的三重加密相容只需k1=k3。

知道了3DES與DES的關係,那我們就可以推出3DES的金鑰長度應該為DES的三倍也就是168bit,輸入的明文塊和輸出的密文塊仍然是64bit的。

更多3DES的資訊可以檢視wiki

AES

3DES缺點是演算法執行相對較慢。因為原來DEA是為70年代的硬體設計的,演算法程式碼並不是很高效,而3DES是DEA演算法的3輪迭代,因此更慢。而且DEA和3DES的分組大小都是64bit,3DES金鑰長度卻是168bit,處於加密效率和安全的考慮,需要更大的分組長度。於是AES應運而生。

Advanced Encryption Standard 高階加密標準,該標準是美國國家標準技術研究所於2001年頒佈的。AES旨在取代DES成為廣泛使用的標準,2006年AES已成為最流行的對稱加密演算法。

AES使用的分組大小為128bit金鑰長度可以為128、192、256 bit。最簡單最常用的也就是128 bit的金鑰。

AES的詳細內容可以檢視wiki

下面列個表,大致的總結一下分組加密演算法。

分組(塊)加密標準 分組大小 金鑰長度
DES 64 bit = 8 byte 56 bit = 7 byte
3DES 64 bit = 8 byte 168 bit = 21 byte
AES 128 bit = 16 byte 128|192|256 bit =16|24|32 byte

流加密實現原理

真隨機數,偽隨機數

因為隨機數在加密演算法中相當重要,而且RC4中就使用了偽隨機數生成演算法。所以有必要理解什麼是偽隨機數,什麼事真隨機數。看一下Wiki中對真隨機數和偽隨機數的界定。

根據密碼學原理,隨機數的隨機性檢驗可以分為三個標準:

  1. 統計學偽隨機性。統計學偽隨機性指的是:在給定的隨機位元流樣本中,1的數量大致等於0的數量,同理,“10”“01”“00”“11”四者數量大致相等。類似的標準被稱為統計學隨機性。滿足這類要求的數字在人類“一眼看上去”是隨機的。
  2. 密碼學安全偽隨機性。其定義為:給定隨機樣本的一部分和隨機演算法(RNG,Random number generator),不能有效的演算出隨機樣本的剩餘部分。
  3. 真隨機性。其定義為:隨機樣本不可重現。實際上只要給定邊界條件,真隨機數並不存在,可是如果產生一個真隨機數樣本的邊界條件十分複雜且難以捕捉(比如計算機本身的輻射波動值),可以認為用這個方法演算出來了真隨機數。

相應的,隨機數也分為三類:

  1. 偽隨機數:滿足第一個條件的隨機數。
  2. 密碼學安全的偽隨機數:同時滿足前兩個條件的隨機數。可以通過密碼學安全偽隨機數生成器計算得出。
  3. 真隨機數:同時滿足三個條件的隨機數。

真正的隨機數是使用物理現象產生的:比如擲錢幣、骰子、使用電子元件的噪音、核裂變等等。這樣的隨機數生成器叫做物理性隨機數生成器,它們的缺點是技術要求比較高。

在實際應用中往往使用偽隨機數就足夠了。這些數列是“似乎”隨機的數,實際上它們是通過一個固定的、可以重複的演算法產生的(只是重複的週期比較大)。它們不真正地隨機,因為它們實際上是可以計算出來的,但是它們具有類似於隨機數的統計特徵(分佈均勻)。這樣的生成器叫做偽隨機數生成器(PRNG, pseudo-random number generator)。

流加密的基本原理

流加密的原理很簡單,用前面提到的偽隨機數生成器(PRNG)根據祕鑰來生成一個與明文長度一樣的金鑰流,然後將金鑰流與明文流進行異或運算從而得到加密後的密文。解密時用同樣的演算法,根據金鑰通過PRNG得到金鑰流,將金鑰流與密文進行異或運算即可。這裡面遵循了一個很簡單的原則:對一個數據進行兩次相同的異或運算得到的還是原來的資料。

流加密演算法原理圖

流加密的優缺點

優點:

  1. 實現簡單。流加密生成金鑰流後,只要進行一次異或運算即可;而像DES這樣的分組加密需要經過Feistel加密網路(16輪的置換操作)。
  2. 速度更快。因為流加密的簡單操作少,所以速度也比分組加密快。
  3. 變長金鑰。金鑰只負責生成金鑰流,金鑰流的生成與金鑰長度關係不大,這就使得流加密可以使用變長金鑰。

自己實現一個簡單的流加密

知道了上面的原理,我們可以實現一個簡單的流加密演算法,從而更深層的瞭解流加密演算法的原理。

首先我們知道C語言stdlib.h函式庫中有一個rand()函式可以生成0~0x7FFF範圍內的偽隨機數,而且可以通過srand()來設定隨機數種子,我們就用這兩個函式來實現一個流加密演算法:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

/*加密演算法:message被加密的訊息,count訊息長度,key加密金鑰*/
void crypto(char message[], int length, int key);

/*將字元位元組流轉16進位制*/
const char* toHexStr(const char* message, int length);

void main(int argc, char *argv[])
{
    char msg[] = "Hello World!";
    int key = 10;
    int length = strlen(msg);
    printf("before encryption:%s\n", toHexStr(msg, length));

    // 加密
    crypto(msg, sizeof(msg), key);
    printf("after encryption :%s\n", toHexStr(msg, length));

    // 解密
    crypto(msg, sizeof(msg), key);
    printf("after decryption :%s\n", toHexStr(msg, length));
    system("pause");
}

void crypto(char message[], int length, int key)
{
    char key_stream = 0;
    int i = 0;

    // 以金鑰作為種子
    srand(key);

    for (i = 0; i < length; i++)
    {
        // 生成與密文長度一致的金鑰流
        key_stream = rand() & 0x00FF;

        // 將明文與金鑰流進行異或運算得到密文
        message[i] = message[i] ^ key_stream;
    }
}

const char* toHexStr(const char* message, int length)
{
#   define BUFFER_SIZE 4*1024+1
    static const char CHAR_TABLE[] = "0123456789ABCDEF";
    static char buffer[BUFFER_SIZE];
    int i = 0, j = 0;
    int high = 0, low = 0;

    memset(buffer, 0, sizeof(buffer));
    while (i < length && j < BUFFER_SIZE)
    {
        // char只佔1個位元組,所以可以直接計算高4位和低四位
        high = message[i] / 16;
        low = message[i] % 16;
        buffer[j++] = CHAR_TABLE[high & 0x000F];
        buffer[j++] = CHAR_TABLE[low & 0x000F];
        i++;
    }
    return buffer;
}

我們可以看一下執行結果:

執行結果

上面的程式把“Hello World!”這個字串進行了加密,金鑰是個整數10。這個加密演算法很簡單,所以也有很多缺陷,比如它只能接受正整數的金鑰,這些缺陷主要由於rand()這個隨機數產生器導致的,而很多知名流加密演算法就是在這個隨機數產生器上做文章。

RC4

Rivest Cipher 4是RSA公司的成員Ron Rivest於1987年設計的(RSA是三個麻省理工大學的學生創立的,著名的非對稱公鑰加密演算法RSA就是該公司發明的)。起初該演算法是商業機密,直到1994年,才被公之於眾。由於RC4具有演算法簡單,運算速度快,軟硬體實現都十分容易等優點,使其在一些協議和標準裡得到了廣泛應用。比如:SSL/TLS(Secure Sockets Layer/Transport Layer Security),無線區域網WEP(Wired Equivalent Privacy)協議和WPA(Wi-Fi Protected Access)中都有RC4的應用。

Ron Rivest共設計了六套加密演算法,都以RC命名,其中RC4是流加密方式實現的,RC2是一種分組加密演算法實現,RC6更是AES演算法徵集的入圍者,更多內容點選這裡