2013年8月18日 星期日

Python M2Crypto - AES 的 Encrypt 與 Decrypt

        這邊要介紹有關 AES 的 Encrypt 和 Decrypt ,其中會介紹一些 AES 的基本知識和相關名詞以及一個簡單的範例程式說明。

AES的基本知識

        AES(Advanced Encryption Standard)是一種對稱式(symmetric)的加密演算法,是透過對每個固定大小的4x4位元矩陣區塊(block = 128 bits = 16 bytes),對其每一個元素進行多次交互置換和XOR運算。簡單來說,就是用同一把 secret key 進行Encrypt和Decrypt的動作。優點是對於資料大的檔案加解密的速度較快,而且容易透過硬體實作,運算所需要的記憶體較少,但是缺點是在資料交換的過程中,雙方必須取得這把相同的secret key。否則,接收到加密檔案的接收者,無法解開加密後的檔案。而它的安全性強度取決於金鑰(secret key)的長度,目前美國國防安全局審核認定的AES標準演算法secret key長度有128 bits, 192 bits和256 bits 三種。secret key的長度越長就越安全,但是相對的加密所需的時間就越多,然而實際上secret key長度對於加密時間的影響並不大,主要還是取決於所選擇的 Block Cipher Mode以及需要加密的資料大小。

AES演算法中常見的專有名詞

        在AES演算法中,不管是網路上的文章或是API文件中,常見到以下這些名詞:

Initialization vector (IV)

        "初始化向量"或稱"起始變數",它的用途主要在於避免相同的資料加密多次都產生相同的密文(Cipher Text)。因此,使用上必須要注意的是,相同的一把金鑰(secret key)在加密的時候,不可以使用相同的 IV,否則就破壞了AES的安全性。此外 IV 本身並不需要保護,它是可以被公開的。而IV的最大長度必須是 16 bytes,而且產生IV的方式必須是無法預測的,也就是隨機產生即可。

以下是關於幾種常見的IV類型:
(1) Fixed IV
  顧名思義就是,在使用同一把金鑰(secret key)在加密的時候,所使用的IV都是固定的。這會造成兩個相同的 plain-text 的區塊,產生的 cipher-text 區塊是相同的。

(2) Counter IV
  是指在使用同一把金鑰(secret key)在加密的時候,每次加密一個訊息時,都將 IV 累加 1。

(3) Random IV
  是指隨機產生一個亂數當作 IV。建議的作法是將第一個 IV 當作密文的第一個 cipher-text 區塊。從第二個 cipher-text 區塊才是真正的加密資料。

(4) Nonce-Generated IV
  是指使用相同的一把金鑰(secret key)在加密的時候,用一個 nonce 來產生 IV 。nonce 是指 number used once 的意思,主要的精神在於同一把金鑰(secret key)不可以使用相同的 nonce。例如:假設選擇 nonce = 0 開始後,使用相同的一把金鑰(secret key)在加密的時候,不可以出現使用 nonce = 0 第 2 次。

Padding

        由於AES加密過程,是針對每個固定大小的區塊(16 bytes),進行多次的交互置換和XOR運算,因此當需要被加密的資料小於矩陣區塊16 bytes 的時候,或是資料的size 不是 16 bytes的倍數時,為了讓加密能夠順利進行,必須將資料的 size 補齊到能夠被 16 bytes 整除的大小。舉例來說,假設需要保護的資料總長度只有 5 bytes,那在進行AES加密之前,必須補齊資料長度達到16 bytes。至於,不同補齊的方式可以參考 Use Padding in Encryption 這篇文章。

特別註記,以下的圖出自於維基百科 (https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation)

Block cipher mode 

  • ECB (Electronic codebook,ECB)
    ECB是對於每一個資料區塊都用同一把金鑰(secret key)去加密,而且沒有使用 IV。缺點是在於相同的資料區塊,加密後的密文(Cipher Text)會是相同的。優點是每個區塊都可以獨立進行加密,因此可同時對每個區塊進行加密。

  • CBC (Cipher-block chaining)
    CBC是一種串鏈的加密方式,第一個資料區塊必須加入IV和金鑰(secret key)進行加密,之後將加密後的密文(Cipher Text)作為第二個資料區塊的IV再加上金鑰進行加密,以此類推下去,直到所有區塊都被加密完成。這種方式在加密過程當中,下一個區塊必須依賴這個區塊加密後的結果才能夠得到IV,因此無法同時進行。但是在解密的時候,可以同時對所有區塊進行解密,因為前後兩個密文(Cipher Text)區塊,後面的密文區塊要解密時候所需要的IV就是前一個密文區塊。

  • CFB (Cipher feedback)
    CFB 則是將 IV 和金鑰(secret key) 產生出 密文(Cipher Text)區塊,然後把密文(Cipher Text)區塊和明文資料區塊進行XOR運算後的值,當做下一個資料區塊的IV再和金鑰(secret key)產生出下一個密文區塊,以此類推下去做相同的運算,直到所有區塊都被加密完成。

  • OFB (Output feedback)
    OFB 則是將 IV 和 金鑰 (secret key) 產生密文區塊(Cipher Block),然後將Cipher Block和明文區塊 PlainText 進行 XOR 運算。而這個區塊產生的 Cipher Block 則當作下一個資料區塊加密處理所需的 IV。


  • CTR (Counter)
    CTR 則是先透過所謂的 nonce (其實就是IV) 加上可以在長時間內每次都產生不重復 sequence 整數的 counter 所組合出來的一個整數值, 接著用同一把金鑰 (secret key) 對這個整數值加密後的產生一個密文區塊 (Cipher Block), 最後把這個密文區塊和明文區塊(PlainText)進行XOR運算。這種做法每個區塊都可以獨立地進行加密或是解密,因此可運用在平行處理的加解密運算。

AES Encrypt / Decrypt 基礎範例程式解說


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import M2Crypto.EVP as EVP
import cStringIO

ENCRYPT_OP = 1
DECRYPT_OP = 0

def aes_encrypt(iv, secret_key, plain_text):
    cipher = EVP.Cipher(alg='aes_128_cbc', 
                        key=secret_key, 
                        iv=iv,
                        op=ENCRYPT_OP)

    input_buffer  = cStringIO.StringIO(plain_text)        
    cipher_data1 = cipher.update(input_buffer.read())
    cipher_data2 = cipher.final()
    input_buffer.close()
        
    output_buffer = cStringIO.StringIO()
    output_buffer.write(cipher_data1)
    output_buffer.write(cipher_data2)
    cipher_text = output_buffer.getvalue()    
    output_buffer.close()
    
    return cipher_text
    
def aes_decrypt(iv, secret_key, cipher_text):
    cipher = EVP.Cipher(alg='aes_128_cbc', 
                        key=secret_key, 
                        iv=iv, 
                        op=DECRYPT_OP)
    
    input_buffer = cStringIO.StringIO(cipher_text)
    plain_text1 = cipher.update(input_buffer.read())
    plain_text2 = cipher.final()
    input_buffer.close()
    
    output_buffer = cStringIO.StringIO()
    output_buffer.write(plain_text1)
    output_buffer.write(plain_text2)
    plain_text = output_buffer.getvalue()    
    output_buffer.close()
    
    return plain_text

if __name__ == '__main__':

    IV = 'c782dc4c098c66cb' # 16 bytes
    secrect_key = 'c286696d887c9aa0' # 16 bytes
    
    data = 'This is a 48-byte message (exactly 3 AES blocks)'

    cipher_text = aes_encrypt(IV, secrect_key, data)
    plain_text = aes_decrypt(IV, secrect_key, cipher_text)

    print('Cipher Text length is %d bytes'% len(cipher_text))    
    print('Cipher Text is as following')
    print('####################')
    print(cipher_text)    
    print('####################')            
    print('Plain Text=%s'% plain_text)

        EVP是OpenSSL針對對稱式加密所提供的模組,它針對某些對稱式(Symmetric)密碼學演算法在處理加密或是解密時,所需要的API規格。

  1. 因此在加密或是解密之前,需要產生一個Cipher的物件來負責加密或是解密。
  2. 初始化Cipher物件時,第 8 和 27 行程式的 alg 參數指定使用 128-bits 長度的 AES-CBC模式的演算法。這個 alg 參數在AES演算法的使用上,可以指定以下幾種分別代表不同長度和模式:
    • ECB
      • aes_128_ecb 
      • aes_192_ecb
      • aes_256_ecb
    • CBC
      • aes_128_cbc
      • aes_192_cbc
      • aes_256_cbc
    • CFB
      • aes_128_cfb
      • aes_192_cfb
      • aes_256_cfb
    • OFB
      • aes_128_ofb
      • aes_192_ofb
      • aes_256_ofb
  3. 初始化Cipher物件時,第 9 和 28 行程式的 key 參數是用來指定加密或是解密時,所需要用到的金鑰(secret key)。它的長度至少需要128 bits 也就是16 bytes。若你是使用aes_256_cbc模式,則金鑰長度必須為256 bits 等於 32 bytes長度。
  4. 初始化Cipher物件時,第 10 和 29 行程式的 iv 參數是用來指定初始向量(Initialization vector),它的最大長度為 16 bytes。
  5. 初始化Cipher物件時,根據要做加密或是解密來決定 op的參數。若是用於加密,則 op 參數要設定為 1 。反之,用於解密時,則 op 參數必須指定為 0。
  6. 在程式碼的的 第 47 行,我們選定的 IV 長度為 16 bytes 。根據我的實驗結果,在M2Crypto API 使用上,即使所給的 IV不足 16 bytes,它還是可以執行。在 secret key不變的情況下,每次執行後的密文都會改變。但是,如果指定的 IV 剛好是 16 bytes長度時,執行多次後的密文是相同的。如果指定的 IV 長度大於  16 bytes,則超過16 bytes後的資料將不會被當做 IV 使用。然而,演算法上 IV 的長度應該要取決於你所使用的 AES mode 與金鑰長度來決定。
  7. 在程式碼的第 48 行,我們選定的 secret key 長度為 16 bytes 。因為這個範例程式指定的 AES模式為 aes_128_cbc,就 AES 128 bits 加密所需要的長度就是16 bytes 。但是,在 M2Crypto API 使用上,它的行為和 IV 有類似的狀況。以這個範例來說,如果 secret key 少於 16 bytes 而 IV 不變的情況下,對相同的資料加密後所產生的密文,對相同的資料執行多次後的密文都會不同。但是,如果 secret key 剛好指定 16 bytes 時候,對相同資料執行多次加密後所得到的密文是相同的。如果指定的 secret key 超過 16 bytes 時候,超過16 bytes 後的資料不會被當作 secret key來使用。

Important warring: You should select the correct cipher mode,  for example: CCM or GCM mode. (Update 2022/03/04) The reason is the CBC mode is vulnerable to padding oracle attacks.

Reference:

1 則留言:

歡迎留言討論與指教