乾貨:密碼學系列 – 對稱加密

密碼學很神秘?很高端?本文是密碼學系列第二篇:對稱加密

該系列包含以下文章:

密碼學系列 – 概述[1]

密碼學系列 – 對稱加密[2]

密碼學系列 – 非對稱加密

密碼學系列 – 簽名

密碼學系列 – mimblewimble

(微信不支持鏈接,歡迎點擊原文閱讀)

本文討論的對稱加密算法主要包括 DES、3DES、AES


DES

明文:64 bit 密文:64 bit 密鑰:56/64 bit(每 7 位插入一個校驗位的時候為 64 bit) 其設計思想充分體現了香農提出的混淆和擴散原則

DES 使用的是 Feistel 結構來加密的,一共需要 16 輪,加密過程如下:

1.將明文進行初始置換(通過置換表)2.將置換後的數據分為左右 L1 R1 各 32 bit3.將 48 bit 的子密鑰與 R1 作為輪函數F的輸入4.將 L1 與輪函數的輸出異或運算,得到 L1密文5.將 L1 密文與 R1 交換位置,分別作為下一輪的 R2,L26.將 2-5 再重複 15 次7.將 L17 R17 交換位置,並拼接為 64bit 數據8.將 64bit 數據進行逆初始置換,得到最終密文

需要注意的是:

•子密鑰在每一輪中都是不一樣的•每一輪之間會將左側和右側對調(右側沒有加密)•解密的過程就是將輸出用相同的子密鑰再走一遍,如果加密的子密鑰順序是key1 key2 key3,則解密的子密鑰為key3 key2 key1•輪函數可以設計為不可逆函數如hash,對解密沒有影響

golang 代碼實戰:

func TestDesEncrypt(t *testing.T) {      key:=[]byte{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}      cipherBlock,err:=des.NewCipher(key)      if err!=nil{          t.Error(err)      }      src:=[]byte{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}      encrptDst :=make([]byte,len(src))      cipherBlock.Encrypt(encrptDst,src)      t.Log(encrptDst)      plainDst:=make([]byte,len(encrptDst))      cipherBlock.Decrypt(plainDst, encrptDst)      t.Log(plainDst)  }    //out: [206 173 55 61 184 14 171 248]  //out: [1 2 3 4 5 6 7 8]

三重DES

明文:64 bit 密文:64 bit 密鑰:56/64 * 3 bit(加入校驗位的時候為64 bit)

為了增加 DES 的強度,明文經過 3 次 DES 處理後變成最後的密文,因此密鑰長度為 56/64 * 3 bit。3 次 DES 處理並不是簡單的 3 次加密的過程,而是加密、解密、加密,解密的過程相應的就是解密、解密、解密。這樣設計是因為在 3 個密鑰相同時,可以兼容 DES 算法

golang 代碼實戰:

func TestTripleDesEncrypt(t *testing.T) {      key:=[]byte{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,      0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}      cipherBlock,err:=des.NewTripleDESCipher(key)      if err!=nil{          t.Error(err)      }      src:=[]byte{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}      encrptDst :=make([]byte,len(src))      cipherBlock.Encrypt(encrptDst,src)      t.Log(encrptDst)      plainDst:=make([]byte,len(encrptDst))      cipherBlock.Decrypt(plainDst, encrptDst)      t.Log(plainDst)  }    //此處3個密鑰相同,兼容DES  //out: [206 173 55 61 184 14 171 248]  //out: [1 2 3 4 5 6 7 8]

AES

明文:128 bit 密文:128 bit 密鑰:128/192/256 bit (分別需要10/12/14輪)

AES 標準最後評選出的算法是 Rijindale 算法,該算法支持密鑰 128/192/256 bit ,分別需要 10/12/14 輪,本文討論的是 128 bit密鑰。它的加密過程並沒有使用 DES 的 feistel 結構,而是使用了一種新的 SPN 結構,需要 10-14 輪計算,如下圖:

其中每一輪計算過程如下:

1.SubBytes(位元組替換):以位元組大小為索引,與s_box表中位元組映射2.ShiftRows(行移位-擴散):從上到下從左到右的順序組成 4 * 4 數組,從 0 行開始,第 n 行向左平移 n 個位元組3.MixColums(列混餚-擴散):對每一列進行矩陣運算,共四列4.AddRoundKey(輪密鑰加):與輪密鑰即子密鑰異或運算

需要注意的是:

•最後一輪沒有列混淆•加密時:SubBytes -> ShiftRows -> MixColums -> AddRoundKey 解密時:AddRoundkey -> InvMixColums -> InvShiftRows -> InvSubBytes (Inv代表逆運算)

golang 代碼實戰:

func TestAesEncrypt(t *testing.T){      key:=[]byte{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}      cipherBlock,err:=aes.NewCipher(key)      if err!=nil{          t.Error(err)      }      src:=[]byte{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}      encrptDst :=make([]byte,len(src))      cipherBlock.Encrypt(encrptDst,src)      t.Log(encrptDst)      plainDst:=make([]byte,len(encrptDst))      cipherBlock.Decrypt(plainDst, encrptDst)      t.Log(plainDst)  }    //out [19 7 34 196 163 153 225 186 223 245 40 131 80 80 70 203]  //out [1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8]

迭代模式

以上討論的三種加密算法都是分組密碼,每次只能處理特定長度的一塊數據,例如 DES 和 3DES 能處理的這塊數據長度為 8 bytes,AES 的為 16 bytes。而我們的日常需要加密的明文基本上都是大於這個長度,這就需要我們將明文的內容進行分組并迭代加密,這個迭代加密的方式就是模式。

ECB 模式

電子密碼本模式(electronic codebook ),最簡單的模式,將明文分組直接作為加密算法的輸入,加密算法的輸出直接作為密文分組。

CBC 模式

密文分組鏈接模式(Cipher Block Chaining),密文之間是鏈狀的,明文分組跟上個密文分組異或之後作為加密算法的輸入,加密算法的輸出作為密文分組。第一個明文分組加密時需要一個初始化向量。

CFB 模式

密文反饋模式(Cipher FeedBack),上一個密文分組作為下一個加密算法的輸入,加密算法的輸出與明文分組異或結果作為密文分組。同樣需要一個初始化向量

OFB 模式

輸出反饋模式(OutPut FeedBack),上一個加密算法的輸出作為下一個加密算法的輸入,明文與加密算法的輸出異或作為密文分組。需要初始化向量

CTR 模式

計數器模式(Counter),將計數器作為加密算法的輸入,加密算法的輸出與明文分組異或作為密文分組,計數器是累加的。需要一個初始的計數器值

以上各種模式,ECB 不推薦使用

golang 代碼實戰:

func TestCBCMode(t *testing.T) {      key:=[]byte{0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}      cipherBlock,err:=aes.NewCipher(key)      if err!=nil{          t.Error(err)      }      src:=[]byte{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}      inv:=[]byte{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}      cbcEncrypter:=cipher.NewCBCEncrypter(cipherBlock,inv)      encrptDst :=make([]byte,len(src))      cbcEncrypter.CryptBlocks(encrptDst,src)      t.Log(encrptDst)        plainDst:=make([]byte,len(encrptDst))      cbcDecrypter:=cipher.NewCBCDecrypter(cipherBlock,inv)      cbcDecrypter.CryptBlocks(plainDst,encrptDst)      t.Log(plainDst)  }    //out [182 174 175 250 117 45 192 139 81 99 151 49 118 26 237 0 98 117 59 208 145 166 116 62 43 199 115 70 250 251 56 226]  //out [1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8]  

References

[1] 密碼學系列 – 概述: https://learnblockchain.cn/article/798 [2] 密碼學系列 – 對稱加密: https://learnblockchain.cn/article/805