這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
- 對稱式加密演算法,即加密和解密使用一樣的密鑰的加解密演算法。
- 分組密碼(block cipher),是每次只能處理特定長度的一塊(block)資料的一類加解密演算法。
- 目前常見的對稱式加密演算法DES、3DES、AES都是屬於分組密碼。
DES
- DES,全稱Data Encryption Standard,是上一代標準對稱式加密演算法,現已不推薦使用。
- 密鑰:DES是一種將64bit的明文加密成64bit的密文的對稱密碼演算法,它的密鑰長度是64bit(每隔7bit會設定一個用於錯誤檢查的bit,因此實際使用密鑰長度56bit)。
- 分組:DES是以64bit的明文作為一個單位來進行加密的,這64bit的單位稱為分組。一般來說,以分組為單位進行處理的密碼演算法稱為分組密碼(block cipher),DES就是分組密碼中的一種。DES每次只能加密64位元的資料,如果要加密的明文比較長,就需要對DES加密進行迭代。
DES加密
DES解密
3DES
- 3DES,即triple-DES,簡單地說就是3次DES加解密的組合。現已不推薦使用。
- 加密:cipthertext = E(k3, D(k2, E(k1, plaintext)))
- 解密:plaintext = D(k1, E(k2, D(k3, ciphertext)))
- 特點:如果三個密鑰都一樣,拿剛好和DES一樣。
註:E表示Encrypt,D表示Decrypt。
3DES加密
3DES解密
AES
- AES, Advanced Encryption Standard,是現行的對稱式加密標準。目前(2017)如果使用對稱式加密,應該使用AES。當然,只能說當前AES演算法是安全的,不能保證AES永遠都是安全的。
- 分組:128bit。
- 密鑰:128bit、192bit、256bit。
分組密碼的模式
分組密碼簡介
- 分組密碼(block cipher),是每次只能處理特定長度的一塊資料的一類密碼演算法,這裡的“一塊”就稱為分組(block)。一個分組的位元數就稱為分組長度(block length)。
- 流密碼(stream cipher),是對資料流進行連續處理的一類密碼演算法。
- DES、3DES、AES等大多數對稱密碼演算法都屬於分組密碼。
ECB模式
- 全稱Electronic CodeBook mode,電子密碼本模式。
- 分組方式:將明文區塊編碼器之後的結果直接稱為密文分組。
- 優點:
- 缺點:
- 相同的明文分組會轉換為相同的密文分組。
- 無需破譯密碼就能操縱明文(每個分組獨立且前後文無關,直接增加或刪除一個分組不影響其它分組解密過程的正確性)。
ECB加密
ECB解密
CBC模式
- 全稱Cipher Block Chaining mode,密碼分組連結模式。
- 分組方式:將明文分組與前一個密文分組進行XOR運算,然後再進行加密。每個分組的加解密都依賴於前一個分組。而第一個分組沒有前一個分組,因此需要一個初始化向量(initialization vector)。
- 優點:
- 加密結果與前文相關,有利於提高加密結果的隨機性。
- 可並行解密。
- 缺點
- 無法並行加密。
- 一個分組損壞,如果密文長度不變,則兩個分組受影響。
- 一個分組損壞,如果密文長度改變,則後面所有分組受影響。
CBC加密
CBC解密
CFB模式
- 全稱Cipher FeedBack mode,密文反饋模式。
- 分組方式:前一個密文分組會被送回到密碼演算法的輸入端(具體見)。
- 在CBC和EBC模式中,明文分組都是通過密碼演算法進行加密的。而在CFB模式中,明文分組並沒有通過密碼編譯演算法直接進行加密,明文分組和密文分組之間只有一個XOR。
- CFB模式是通過將“明文分組”與“密碼演算法的輸出”進行XOR運行產生“密文分組”。CFB模式中由密碼演算法產生的位元序列稱為密鑰流(key stream)。密碼演算法相當於密鑰流的偽隨機數產生器,而初始化向量相當於偽隨機數產生器的種子。(CFB模式有點類似單次密碼本。)
- 優點:
- 缺點:
- 不能抵禦重放攻擊(replay attack)。
- 不支援並行加密。
CFB加密
CFB解密
OFB模式
- Output FeedBack mode 輸出反饋模式
- 密碼演算法的輸出會反饋到密碼演算法的輸入中(具體見)。
- OFB模式中,XOR所需的位元序列(密鑰流)可以事先通過密碼演算法產生,和明文分組無關。只需要提前準備好所需的密鑰流,然後進行XOR運算就可以了。
OFB加密
OFB解密
分組模式小結
推薦使用CBC模式。
填充
為什麼要填充?
ECB和CBC模式要求明文資料必須填充至長度為分組長度的整數倍。
填充的兩個問題。
填充多少位元組?
需要填充的位元組數為:paddingSize = blockSize - textLength % blockSize
填充什麼內容?(這裡列舉的三種方式本質上是一致的)
- ANSI X.923:以數列填滿的最後一個位元組填
paddingSize
,其它填0。
- ISO 10126:以數列填滿的最後一個位元組填
paddingSize
, 其它填隨機數。
- PKCS7:以數列填滿的每個位元組都填
paddingSize
。
樣本
這裡用golang寫一個AES加密的例子。
由於加密出來的資料很可能有很多不可見字元,因此這裡會將加密後的結果進行一次Base64Encode。
這裡採用CBC模式+PKCS7填充方式。
package mainimport ( "bytes" "crypto/cipher" "crypto/aes" "encoding/base64" "fmt")func PKCS7Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext) % blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padtext...)}func PKCS7UnPadding(origData []byte) []byte { length := len(origData) unpadding := int(origData[length-1]) return origData[:(length - unpadding)]}func AesEncrypt(origData, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() origData = PKCS7Padding(origData, blockSize) blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) crypted := make([]byte, len(origData)) blockMode.CryptBlocks(crypted, origData) return crypted, nil}func AesDecrypt(crypted, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) origData := make([]byte, len(crypted)) blockMode.CryptBlocks(origData, crypted) origData = PKCS7UnPadding(origData) return origData, nil}func main() { key := []byte("0123456789abcdef") result, err := AesEncrypt([]byte("hello world"), key) if err != nil { panic(err) } fmt.Println(base64.StdEncoding.EncodeToString(result)) origData, err := AesDecrypt(result, key) if err != nil { panic(err) } fmt.Println(string(origData))}
參考文檔