密碼學總結(一)

一、密碼常識

1.1 信息安全面臨的問題以及解決方案

在這裏插入圖片描述
記住:

  • 不要過於依賴密碼保證信息安全,因爲任何密碼都會有被破解的一天;
  • 不要使用低強度的密碼;
  • 不要使用保密的密碼算法;
  • 信息安全是一個系統的問題,密碼只是信息安全的一部分;

1.2 加密三要素

明文和密文:加密處理的數據。
祕鑰(key):用於生成明文的一串數字。
算法(algorithm):用於解決複雜問題的步驟。從明文生成密文的步驟,也就是加密的步驟,稱爲“加密算法",而解密的步驟則稱爲“解密算法"。
在這裏插入圖片描述
在這裏插入圖片描述

1.3 凱撒密碼

愷撒密碼(Caesar cipher)是一種相傳尤利烏斯·愷撒曾使用過的密碼。愷撒於公元前100年左右誕生於古羅馬,是一位著名的軍事統帥。

愷撤密碼是通過將明文中所使用的字母表按照一定的字數“平移”來進行加密的。

爲了講解方便,我們用小寫字母(a,b,c,…)來表示明文,用大寫字母(A,B,C,…)來表示密文。

現在我們將字母表平移3個字母,於是,明文中的a在加密後就變成了與其相隔3個字母的D,以此類推。b變成E,c變成F,d變成G…v變成Y,w變成Z,而x則會回到字母表的開頭而變成A,相應地,y變成B,z變成C。通過下圖我們可以很容易地理解“平移"的具體工作方式。
在這裏插入圖片描述
比如英文hello,加密後的數據變爲KHOOR

愷撒密碼的解密過程是使用與加密時相同的密鑰進行反向的平移操作。比如上面例子,只要反向平移3個字母就可以解密了。

二、對稱加密

2.1 什麼是對稱加密

對稱加密是指在加密和解密時使用相同的祕鑰。
在這裏插入圖片描述

2.2 分組密碼

一般來說,以分組爲單位進行處理的密碼算法稱爲分組密碼(blockcipher)。一個分組的比特數就稱爲分組長度(blocklength)。例如,DES三重DES的分組長度都是64比特。這些密碼算法一次只能加密64比特的明文,並生成64比特的密文。

2.3 分組模式

分組密碼算法只能加密固定長度的分組,但是我們需要加密的明文長度可能會超過分組密碼的分組長度,這時就需要對分組密碼算法進行迭代,以便將一段很長的明文全部加密。而迭代的方法就稱爲分組密碼模式。

比較常見的分組模式有:ECB、CBC、CFB、OFB、CTR等模式。

2.3.1 ECB模式

ECB(Electronic Code Book, 電子密碼本模式),是最簡單的分組模式。明文消息被分成固定大小的塊(分組),並且每個塊被單獨加密。如果最後一個明文分組的內容小於分組長度時,需要用一特定的數據進行填充(padding)。
在這裏插入圖片描述
在這裏插入圖片描述
特點: 每個塊的加密和解密都是獨立的,且使用相同的方法進行加密,所以可以進行並行計算,所以效率較高。但是,由於密文有規律,如果有一個塊被破解,那麼使用相同的方法可以解密其他塊中的數據,所以安全性較低,容易被破解。

2.3.2 CBC模式

2.3.2.1 XOR

XOR的全稱是exclusive or,在中文裏叫作異或。由於XOR和加法運算很相似,因此一般用+和O組合而成的符號⊕來表示XOR。
在這裏插入圖片描述
如果兩個比特序列執行異或運算,只要對其中每個相對應的比特進行XOR運算就可以了(如下圖)。
在這裏插入圖片描述
由於兩個相同的數進行XOR運算的結果一定爲0,因此如果將A⊕B的結果再與B進行XOR運算,則結果會變回A。也就是說,兩個公式中的B會相互抵消。
在這裏插入圖片描述
通過上面的運算公式與加密解密非常相似:

  • 將明文A用密鑰B進行加密,得到密文A⊕B
  • 將密文A⊕B用密鑰B進行解密,得到明文A

實際上,只要選擇一個合適的B(相當於祕鑰),僅僅使用XOR就可以實現一個高強度的密碼。

2.3.2.2 CBC模式

CBC - Cipher Block Chaining, 密碼塊鏈模式,它是一種最常見的加密模式。CBC模式中的每一個分組要先和前一個分組加密後的數據進行XOR異或操作,然後再進行加密。而第一個數據塊需要用初始化向量IV進行異或操作後再進行加密。

初始化向量:當加密第一個明文分組時,由於不存在前一個密文分組,因此需要事先準備一個長度爲一個分組的比特序列來代替前一個密文分組,這個比特序列稱爲初始化向量(initialization vector),通常縮寫爲 IV。一般來說,每次加密時都會隨機產生一個不同的比特序列來作爲初始化向量。

在這裏插入圖片描述
在這裏插入圖片描述
特點:明文分組在加密之前一定會與前一個密文分組進行 XOR 運算,即便明文分組1和2的值是相等的,密文分組1和2的值也不一定是相等的。這樣一來,ECB模式的缺陷在CBC模式中就不存在了。但是,由於CBC加密是連續的,不能並行處理,因此效率較低。

明文分組:是指分組密碼算法中作爲加密對象的明文。明文分組的長度與分組密碼算法的分組長度是相等的。
密文分組:是指使用分組密碼算法將明文分組加密之後所生成的密文。

如果將一個分組的加密過程分離出來,我們就可以很容易地比較出ECB模式和CBC模式的區別 。ECB模式只進行了加密,而CBC模式則在加密之前進行了一次XOR。
在這裏插入圖片描述

2.3.3 CFB模式

CFB - Cipher FeedBack, 密文反饋模式。在CFB模式中,前一個分組的密文被送回到密碼算法的輸入端進行加密,然後再把加密後的祕鑰流和當前分組的明文進行異或操作生成當前分組的密文。

由密碼算法所生成的比特序列稱爲密鑰流密碼算法就相當於用來生成密鑰流的僞隨機數生成器,而初始化向量就相當於僞隨機數生成器的種子。

在這裏插入圖片描述
CFB模式的解密流程與加密流程是非常相似的。
在這裏插入圖片描述
通過對比CBC模式與CFB模式發現,在CBC模式中,明文分組和密文分組之間有XOR和密碼算法兩個步驟,而在CFB模式中,明文分組和密文分組之間則只有XOR。
在這裏插入圖片描述
在CFB模式中,由於密碼算法的輸出是通過計算得到的,並不是真正的隨機數,因此CFB模式不可能具各理論上不可破譯的性質。

2.3.4 OFB模式

OFB - Output-Feedback, 輸出反饋模式。在OFB模式中,密碼算法的輸出會反饋到密碼算法的輸入中, 即上一個分組密碼算法的輸出是當前分組密碼算法的輸入。
在這裏插入圖片描述
在這裏插入圖片描述
和CBC模式、CFB模式一樣,OFB模式中也需要使用初始化向量(IV)。一般來說,我們需要在每次加密時生成一個不同的隨機比特序列用作初始化向量。

通過CFB和OFB模式對比發現,CFB和OFB模式的區別僅僅在於密碼算法的輸入。CFB式中,密碼算法的輸人是前一個密文分組,OFB模式中,密碼算法的輸入則是密碼算法的前一個輸出(如下圖)。
在這裏插入圖片描述
相對地,在OFB模式中,XOR所需的密鑰流可以事先通過密碼算法生成,和明文分組無關。只要提前準備好所需的密鑰流,就可以將明文與密鑰流進行XOR操作即可,不需要動用密碼算法。換個角度看,生成密鑰流的操作和進行XOR運算的操作是可以並行的。

2.3.5 CTR模式

CTR - CounTeR, 計數器模式,它是一種通過將逐次累加的計數器進行加密來生成密鑰流的流密碼(如下圖)。
在這裏插入圖片描述
在這裏插入圖片描述
CTR模式中,每個分組對應一個計數器,並通過對計數器進行加密來生成密鑰流,然後再與明文分組進行XOR操作得到密文分組。

  • 計數器生成方法:

每次加密時都會生成一個隨機數(nonce)作爲計數器的初始值。當分組長度爲128比特(16字節)時,計數器的初始值的格式爲:
在這裏插入圖片描述
其中,前面8個字節爲nonce,每次加密都會生成一個新的none值。後面8位位分組序號,這部分會逐次累加。
在這裏插入圖片描述
由於計數器的值是不相同的,因此每個分組中將計數器進行加密所得到的密鑰流也是不同的。也是說,這種方法就是用分組密碼來模擬生成隨機的比特序列。

如果我們將單個分組的加密過程拿出來,那麼OFB模式和CTR模式之間的差異還是很容易理解的(下圖)。
在這裏插入圖片描述
OFB模式是將加密的輸出反憒到密碼算法的輸入,而CTR模式則是將計數器的值用作密碼算法的輸入。

由於CTR模式在加密和解密時所需要計數器的值可以有none和分組序號計算出來,因此它能夠以任意順序處理分組。這也就意味着在支持並行計算的系統中,CTR模式的速度是非常快的。

2.3.6 最後總結

模式 特點 備註
ECB模式
  • 簡單, 效率高, 密文有規律, 容易被破解
  • 最後一個明文分組需要填充
  • 不需要初始化向量
  • 最後一個明文分組需要填充
不推薦使用
CBC模式
  • 密文沒有規律
  • 需要初始化向量
推薦使用
CFB模式
  • 密文沒有規律, 明文分組是和一個數據流進行的按位異或操作, 最終生成了密文
  • 需要初始化向量
  • 不需要填充
建議使用CTR模式代替
OFB模式
  • 密文沒有規律, 明文分組是和一個數據流進行的按位異或操作, 最終生成了密文
  • 需要初始化向量
  • 不需要填充
推薦使用CTR模式代替
CTR模式
  • 密文沒有規律, 明文分組是和一個數據流進行的按位異或操作, 最終生成了密文
  • 不需要初始化向量
  • 不需要填充
推薦使用

2.4 對稱加密在Go語言中的實現

2.4.1 加密實現思路

  • 第一步:創建一個底層使用des/3des/aes的密碼接口;
  • 第二步:如果使用cbc/ecb分組模式,需要對明文分組進行填充;
  • 第三步:創建一個密碼分組模式的接口對象;
  • 第四步:執行加密,得到密文;

2.4.2 解密實現思路

  • 第一步:創建一個底層使用des/3des/aes的密碼接口;
  • 第二步:創建一個密碼分組模式的接口對象;
  • 第三步:執行解密操作;
  • 第四步:去掉最後分組的填充數據;

2.5 對稱加密技術

2.5.1 DES

DES(Data Encryption Standard)是1977年美國聯邦信息處理標準(FIPS)中所採用的一種對稱密碼(FIPS46.3)。DES一直以來被美國以及其他國家的政府和銀行等廣泛使用。然而,隨着計算機的進步,現在DES已經能夠被暴力破解,因此在實際應用中不推薦使用。

特點:安全性低、對數據進行分組、分組長度爲64bit,祕鑰長度爲56,其中有8位是錯誤檢測標誌位。

2.5.1.1 加密解密

DES是以64比特的明文爲一組來進行加密的。儘管從規格上來說,DES的密鑰長度是64比特,一般來說,以分組爲單位進行處理的密碼算法稱爲分組密碼(blockcipher),DES就是分組密碼的一種。DES每次只能加密64比特的數據,如果要加密的明文比較長,就需要對DES加密進行迭代。
在這裏插入圖片描述
由於DES每隔7比特會設置一個用於錯誤檢查的比特位,因此,實質上其密鑰長度爲56比特。

2.5.1.2 CBC模式實現AES加密

// des加密
func desEncrypt(plainText, key []byte) []byte {
	// 創建cipher.Block接口的實例
	block, err := des.NewCipher(key)
	if err != nil {
		panic(err)
	}
	// 填充數據(每一塊大小爲8比特)
	newText := paddingLastGroup(plainText, block.BlockSize())
	// 初始化向量
	iv := []byte("12345678")
	// 創建BlockMode接口的實例
	blockMode := cipher.NewCBCEncrypter(block, iv)
	// 定義一個切片,用於存儲密文數據
	cipherText := make([]byte, len(newText))
	// 將newText加密後存儲到cipherText中
	blockMode.CryptBlocks(cipherText, newText)
	// 返回密文
	return cipherText
}

// 填充數據
func paddingLastGroup(plainText []byte, blockSize int) []byte {
	// 求出最後一個分組的字節數
	padNum := blockSize - len(plainText) % blockSize
	// padNum作爲填充數據
	data:= []byte{byte(padNum)}
	// 重複產生padNum個填充數據data,並存儲到切片中
	newPlain := bytes.Repeat(data, padNum)
	// 把填充數據與plainText進行合併
	newText := append(plainText, newPlain...)
	return newText
}

// 測試
func main() {
	plainText := []byte("hello world hello go hello java")
	key := []byte("87654321") // 祕鑰
	cipherText := desEncrypt(plainText, key)
	fmt.Println("加密後:", cipherText)
}

2.5.1.3 CBC模式實現AES解密

// des解密
func desDecrypt(cipherText, key []byte) []byte {
	// 1.創建cipher.Block接口的實例
	block, err := des.NewCipher(key)
	if err != nil {
		panic(err)
	}
	// 2.創建BlockMode實例,
	iv := []byte("12345678")
	blockMode := cipher.NewCBCDecrypter(block, iv)
	// 3.解密
	plainText := make([]byte, len(cipherText))
	blockMode.CryptBlocks(plainText, cipherText) // 該方法將plainText解密後保存在cipherText中
	// 3.刪除填充數據
	newText := unPadddingLastGroup(plainText)
	return newText
}

// 去掉最後一組的填充數據
func unPadddingLastGroup(plainText []byte) []byte {
	// 獲取明文的長度
	length := len(plainText)
	// 獲取最後一個字節的值
	lastChar := plainText[length - 1]
	// 該值爲填充數據的長度
	num := int(lastChar)
	// 從位置0開始一直截取到length - num位置
	return plainText[:length - num]
}

// 測試
func main() {
	fmt.Println("--------------cbc加密解密----------------")
	plainText := []byte("hello world hello go hello java")
	key := []byte("87654321") // 祕鑰
	cipherText := desEncrypt(plainText, key)
	fmt.Println("加密後:", cipherText)
	newText := desDecrypt(cipherText, key)
	fmt.Println("解密後:", string(newText))
}

運行結果:
在這裏插入圖片描述

2.5.2 3DES

現在DES已經可以在現實的時間內被暴力破解,因此我們需要一種用來替代DES的分組密碼,3DES就是出於這個目的被開發出來的。3DES是爲了增加DES的強度,將DES重複3次所得到的一種密碼算法(如下圖)。
在這裏插入圖片描述
在這裏插入圖片描述
明文經過三次DES處理才能變成最後的密文,由於DES密鑰的長度實質上是56比特,因此3DES的密鑰長度就是56×3=168比特, 加上用於錯誤檢測的標誌位8x3, 共192比特。

從上圖我們可以發現,三重DES並不是進行三次DES加密(加密–>加密–>加密),而是加密–>解密–>加密的過程。在加密算法中加人解密操作讓人感覺很不可思議,實際上這個方法是IBM公司設計出來的,目的是爲了讓三重DES能夠兼容普通的DES。

當三重DES中所有的密鑰都相同時,三重DES也就等同於普通的DES了。這是因爲在前兩步加密–>解密之後,得到的就是最初的明文。因此,以前用DES加密的密文,就可以通過這種方式用三重DES來進行解密。也就是說,三重DES對DES具備向下兼容性。

2.5.2.1 CBC模式實現3DES加密

// 3DES加密
func TripleDESEncrypt(plainText, key []byte) []byte {
	// 創建並返回一個使用3DES算法的cipher.Block接口
	block, err := des.NewTripleDESCipher(key)
	if err != nil{
		panic(err)
	}
	// 填充數據
	plainText = paddingLastGroup(plainText, block.BlockSize())
	// 定義初始化向量,可以隨意指定
	iv := []byte("12345678")
	// 創建一個密碼分組爲鏈接模式, 底層使用3DES加密的BlockMode模型
	blockMode := cipher.NewCBCEncrypter(block, iv)
	// 加密數據
	// 定義一個切片,用於存儲密文數據
	cipherText := make([]byte, len(plainText))
	blockMode.CryptBlocks(cipherText, plainText)
	return cipherText
}

2.5.2.2 CBC模式實現3DES解密

// 3DES解密
func TripleDESDecrypt(cipherText, key []byte) []byte {
	// 創建3DES算法的Block接口對象
	block, err := des.NewTripleDESCipher(key)
	if err != nil{
		panic(err)
	}
	// 初始化向量
	iv := []byte("12345678")
	// 創建密碼分組爲鏈接模式, 底層使用3DES解密的BlockMode模型
	blockMode := cipher.NewCBCDecrypter(block, iv)
	// 解密
	plainText := make([]byte, len(cipherText))
	blockMode.CryptBlocks(plainText, cipherText)
	// 去掉尾部填充的數據
	newText := unPadddingLastGroup(plainText)
	return newText
}

// 測試
func main() {
	plainText := []byte("hello world hello go hello java")
	fmt.Println("--------------3DES加密解密----------------")
	key := []byte("876543218765432187654321") // 祕鑰長度爲192比特位
	cipherText := TripleDESEncrypt(plainText, key)
	fmt.Println("加密後:", cipherText)
	newText := TripleDESDecrypt(cipherText, key)
	fmt.Println("解密後:", string(newText))
}

運行結果:
在這裏插入圖片描述

2.5.3 AES

AES(Advanced Encryption Standard)是取代其前任標準(DES)而成爲新標準的一種對稱密碼算法。全世界的企業和密碼學家提交了多個對稱密碼算法作爲AES的候選,最終在2000年從這些候選算法中選出了一種名爲Rijndae的對稱密碼算法,並將其確定爲了AES。

AES密鑰長度可以有128bit、192bit、256bit三種規格供用戶選擇。但是在Go語言提供的接口中祕鑰長度只能是16個字節(128bit)。

2.5.3.1 加密解密

和DES—樣,AES加密結果也是要經過多輪計算才能夠得到的。

  • 每一輪的大致計算步驟如下:

第一步:AES需要逐個字節地對16字節的輸入數據進行SubBytes處理。所謂SubBytes,就是以每個字節的值(0~255中的任意值)爲索引,從一張擁有256個值的替換表(S-Box)中查找出對應值的處理,也是說,將一個1字節的值替換成另一個1字節的值。

第二步:SubBytes之後需要進行ShiftRows處理,即將SubBytes的輸出以字節爲單位進行打亂處理。從下圖的線我們可以看出,這種打亂處理是有規律的。
在這裏插入圖片描述
第三步:ShiftRows之後需要進行MixColumns處理,即對一個4字節的值進行比特運算,將其變爲另外一個4字節值。

第四步:需要將MixColumns的輸出與輪密鑰進行XOR,即進行AddRoundKey處理。到這裏爲止,AES的一輪加密就已經結東了。

AES加密的SubBytes、ShiftRows、MixColumns操作都存在反向運算InvSubBytes、InvShiftRows、InvMixColumns。

  • 每一輪解密的計算過程如下圖:
    第一步:

2.5.3.2 CTR模式實現AES加密和解密

func aesEncryptAndDecrypt(data, key []byte) []byte {
	// 創建cipher.Block接口的實例
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}
	// 創建初始化向量,ctr每個分組佔16個字節
	iv := []byte("123456789abcdefg") 
	// 使用CTR模式創建Stream實例
	stream := cipher.NewCTR(block, iv)
	// 3.加密解密
	newText := make([]byte, len(data))
	stream.XORKeyStream(newText, data)
	return newText
}

// 測試
func main() {
	fmt.Println("--------------ctr加密解密----------------")
	plainText := []byte("hello world hello go hello java")
	key := []byte("1122334455667788") 
	cipherText := aesEncryptAndDecrypt(plainText, key)
	fmt.Println("加密後:", cipherText)
	newText := aesEncryptAndDecrypt(cipherText, key)
	fmt.Println("解密後:", string(newText))
}

運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章