一、對稱加密概述
我們在開發中常會遇到這種需求:通信的兩端需要傳輸安全級別較高的數據,這需要我們傳輸的加密數據既要難以破解,又要可逆的解密過程。哈希算法雖難以破解,但并非適用于通信中的加解密傳輸。這就需要基于秘鑰管理的加密技術了。對稱加密是最簡單快速的加密方式,所謂對稱加密,即加解密雙方都掌握相同的秘鑰,通過同一秘鑰完成加解密操作。常用的對稱加密算法有:
- DES:Data Encryption Standard,即數據加密標準;
- TDES(TripleDES) :三重DES;
- AES:Rijndael加密法,Advanced Encryption Standard高級加密標準。
Go標準庫中的加密相關包都有相應的支持,下面我們來逐一演示:
二、DES對稱加密
DES算法原理:明文按64位進行分組,密鑰長64位,密鑰事實上是56位參與DES運算(第8、16、24、32、40、48、56、64位是校驗位, 使得每個密鑰都有奇數個1)分組后的明文組和56位的密鑰按位替代或交換的方法形成密文組的加密方法。
所以要實現DES加密需要準備如下要求:
- ==準備一個8字節(64Bit)的秘鑰==;
- 原文需要按8字節(64Bit)長度進行分組,共有五種分組模式,此處我們不具體展開,我們用常用的CBC(密碼分組鏈接模式),這種模式需要一個與分組長度相同的初始化向量,有興趣的可搜索了解其他模式;
- 對末尾的未夠8字節的數據進行填充,把原文轉為64Bit的整數倍;
- ==實現加密時的未位分組填充算法和解密時的刪除密文末尾分組算法。==
以上,秘鑰和初始化向量值需要自己管理,末尾分組的填充算法和刪除算法需要自己實現。下面我們實現一個填充算法和刪除算法:
//填充明文最后一個分組工具方法
//src - 原始數據
//blockSize - 每個分組的數據長度
func paddingBytes(src []byte, blockSize int) []byte {
//1.求出最后一個分組要填充多個字節
padding := blockSize - len(src)%blockSize
//2.創建新的切片,切片的字節數為填充的字節數,并初始化化,每個字節的值為填充的字節數
padBytes := bytes.Repeat([]byte{byte(padding)}, padding)
//3.將創建出的新切片和原始數據進行連接
newBytes := append(src, padBytes...)
//4.返回新的字符串
return newBytes
}
//刪除密文末尾分組填充的工具方法
func unPaddingBytes(src []byte) []byte {
//1.求出要處理的切片的長度
l := len(src)
//2.取出最后一個字符,得到其整型值
n := int(src[l-1])
//3.將切片末尾的number個字節刪除
return src[:l-n]
}
以上實現思路為:計算出最后一個分組需要填充的字節數(1-8),并把這個數作為需要填充的占位符,再把填充后的分組與源數據拼接就得到待加密的字符串。
Go通過crypto/des包支持DES加密算法:
import (
"bytes"
"crypto/cipher"
"crypto/des"
)
const (
MYKEY = "abcdefgh" //八字節密匙
IV = "aaaabbbb" //CBC模式的初始化向量
)
//使用des進行對稱加密
func EncryptDES(src, key []byte) []byte {
//1. 創建并返回一個使用DES算法的cipher.Block接口
block, err := des.NewCipher([]byte(key))
if err != nil {
panic(err)
}
//2. 對最后一個明文分組進行數據填充
src = paddingBytes(src, block.BlockSize())
//3. 創建一個密碼分組為鏈接模式的,底層使用DES加密的BlockMode接口
cbcDecrypter := cipher.NewCBCEncrypter(block, []byte(IV))
//4. 加密連續的數據并返回
dst := make([]byte, len(src))
cbcDecrypter.CryptBlocks(dst, src)
return dst
}
//使用des進行解密
func DecryptDES(src, key []byte) []byte {
//1. 創建并返回一個使用DES算法的cipher.Block接口
block, err := des.NewCipher(key)
if err != nil {
panic(err)
}
//2. 創建一個密碼分組為鏈接模式的,底層使用DES解密的BlockMode接口
cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
//3. 數據塊解密
dst := make([]byte, len(src))
cbcDecrypter.CryptBlocks(dst, src)
//4. 去掉最后一組填充數據
newBytes := unPaddingBytes(dst)
return newBytes
}
使用以上加解密方法:
//測試DES加解密算法
func TestDES() {
fmt.Println("測試DES對稱加密:")
srcString := "GO 密碼學 —— DES對稱加密實現"
//加密
cryptBytes := myDES.EncryptDES([]byte(srcString), []byte(myDES.MYKEY))
fmt.Println("加密效果:")
fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte長度:", len([]byte(srcString)))
fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
fmt.Println()
//解密
deCryptBytes := myDES.DecryptDES(cryptBytes, []byte(myDES.MYKEY))
fmt.Println("解密效果:")
fmt.Println("src :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
fmt.Println("dst :", hex.EncodeToString(deCryptBytes), "[]byte長度:", len(deCryptBytes))
fmt.Println()
fmt.Println("原字串為:" + srcString)
fmt.Println("解密字符為:" + string(deCryptBytes))
}
//OUTPUT:
測試DES對稱加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e2809420444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41
dst : e50636c0545eb031b3b36d363c050deb0640806475b04da4e5b54641eb635b1394d70f86c12f4119319904475235d441 []byte長度: 48
解密效果:
src : e50636c0545eb031b3b36d363c050deb0640806475b04da4e5b54641eb635b1394d70f86c12f4119319904475235d441 []byte長度: 48
dst : 474f20e5af86e7a081e5ada620e28094e2809420444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41
原字串為:GO 密碼學 —— DES對稱加密實現
解密字符為:GO 密碼學 —— DES對稱加密實現
以上就是Go 中DES的加解密實現,可見其加密的安全性依賴于秘鑰的管理,而且其只有64Bit的秘鑰長度在算力越來越高的現在已經不夠安全了,這也是對稱加密的弱點,在實際的網絡項目中,秘鑰的分發需要依賴其他方式的配合。
三、TDES(TripleDES)對稱加密
對于DES秘鑰較弱的問題做了一些改進,這就是三重DES加密算法,其原理與DES類似,唯一不同的是秘鑰從一組八字節變成三組二十四字節(192Bit)。其加密強度也和秘鑰設置相關,此處演示我們也用CBC分組模式,三重加密執行順序分別為,加密->解密->加密。
為了兼容DES,只有當秘鑰一組和秘鑰二組相同時,3DES其加密安全性和DES相同,其余秘鑰的分組設置都是三重DES加密。
import (
"bytes"
"crypto/cipher"
"crypto/des"
)
const (
MYKEY = "abcdefgh12345678ABCDEFGH" //三組八字節密匙,即24字節
IV = "aaaabbbb" //CBC模式的初始化向量,8字節
)
//使用3des進行對稱加密
func EncryptTDES(src, key []byte) []byte {
//1. 創建并返回一個使用DES算法的cipher.Block接口
block, err := des.NewTripleDESCipher([]byte(key))
if err != nil {
panic(err)
}
//2. 對最后一個明文分組進行數據填充,和DES的算法一樣
src = paddingBytes(src, block.BlockSize())
//3. 創建一個密碼分組為鏈接模式的,底層使用DES加密的BlockMode接口
cbcDecrypter := cipher.NewCBCEncrypter(block, []byte(IV))
//4. 加密連續的數據并返回
dst := make([]byte, len(src))
cbcDecrypter.CryptBlocks(dst, src)
return dst
}
//使用3des進行解密
func DecryptTDES(src, key []byte) []byte {
//1. 創建并返回一個使用DES算法的cipher.Block接口
block, err := des.NewTripleDESCipher(key)
if err != nil {
panic(err)
}
//2. 創建一個密碼分組為鏈接模式的,底層使用DES解密的BlockMode接口
cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
//3. 數據塊解密
dst := make([]byte, len(src))
cbcDecrypter.CryptBlocks(dst, src)
//4. 去掉最后一組填充數據,和DES的刪除算法一樣
newBytes := unPaddingBytes(dst)
return newBytes
}
使用以上加解密方法:
//測試三重DES對稱加密算法
func TestTDES() {
fmt.Println("測試TDES對稱加密:")
srcString := "GO 密碼學 —— TDES對稱加密實現"
//加密
cryptBytes := myTDES.EncryptTDES([]byte(srcString), []byte(myTDES.MYKEY))
fmt.Println("加密效果:")
fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte長度:", len([]byte(srcString)))
fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
fmt.Println()
//解密
deCryptBytes := myTDES.DecryptTDES(cryptBytes, []byte(my3DES.MYKEY))
fmt.Println("解密效果:")
fmt.Println("src bytes:", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
fmt.Println("dst bytes:", hex.EncodeToString(deCryptBytes), "[]byte長度:", len(deCryptBytes))
fmt.Println()
fmt.Println("原字串為:" + srcString)
fmt.Println("解密字符為:" + string(deCryptBytes))
}
//OUTPUT:
測試TDES對稱加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e280942033444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 42
dst : 41da5be3a5b9f05438c2896173d141f43c7b12168e80ef299b35a46b76799ab0d3c573f1fc544a6ec3a135f4d5b912ba []byte長度: 48
解密效果:
src bytes: 41da5be3a5b9f05438c2896173d141f43c7b12168e80ef299b35a46b76799ab0d3c573f1fc544a6ec3a135f4d5b912ba []byte長度: 48
dst bytes: 474f20e5af86e7a081e5ada620e28094e280942033444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 42
原字串為:GO 密碼學 —— TDES對稱加密實現
解密字符為:GO 密碼學 —— TDES對稱加密實現
四、AES對稱加密
隨著時代的發展,DES和3DES于現今的算力和性能要求來說已經逐漸落伍,為了適應更高的安全性和性能需要,業界采用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣為全世界所使用。這就是AES對稱加密算法,在現今的項目開發中,建議使用這種加密方式。
AES的基本要求是,采用對稱分組密碼體制,密鑰的長度最少支持為128、192、256,分組長度128位。
所以要實現AES加密需要準備如下要求:
- ==準備一個十六字節(128Bit)的秘鑰==;
- 原文需要按十六字節(128Bit)長度進行分組,采用常用的CBC(密碼分組鏈接模式),準備十六字節(128Bit)長度的初始化向量值;
- ==實現加密時的未位分組填充算法和解密時的刪除密文末尾分組算法。這里的填充和刪除算法和DES相同,不再贅述==
Go通過crypto/aes包支持DES加密算法:
import (
"bytes"
"crypto/aes"
"crypto/cipher"
)
const (
MYKEY = "abcdefgh12345678" //十六字節密匙
IV = "aaaabbbb12345678" //CBC模式的初始化向量:與key等長:十六字節
)
//使用aes進行對稱加密
func EncryptAES(src, key []byte) []byte {
//1. 創建并返回一個使用DES算法的cipher.Block接口
block, err := aes.NewCipher([]byte(key))
if err != nil {
panic(err)
}
//2. 對最后一個明文分組進行數據填充
src = paddingBytes(src, block.BlockSize())
//3. 創建一個密碼分組為鏈接模式的,底層使用DES加密的BlockMode接口
cbcDecrypter := cipher.NewCBCEncrypter(block, []byte(IV))
//4. 加密連續的數據并返回
dst := make([]byte, len(src))
cbcDecrypter.CryptBlocks(dst, src)
return dst
}
//使用aes進行解密
func DecryptAES(src, key []byte) []byte {
//1. 創建并返回一個使用DES算法的cipher.Block接口
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
//2. 創建一個密碼分組為鏈接模式的,底層使用DES解密的BlockMode接口
cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
//3. 數據塊解密
dst := make([]byte, len(src))
cbcDecrypter.CryptBlocks(dst, src)
//4. 去掉最后一組填充數據
newBytes := unPaddingBytes(dst)
return newBytes
}
以上,其用法和DES差不多,只不過秘鑰管理稍微不同而已。
//測試AES對稱加密算法
func TestAES() {
fmt.Println("測試AES對稱加密:")
srcString := "GO 密碼學 —— AES對稱加密實現"
//加密
cryptBytes := myAES.EncryptAES([]byte(srcString), []byte(myAES.MYKEY))
fmt.Println("加密效果:")
fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte長度:", len([]byte(srcString)))
fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
fmt.Println()
//解密
deCryptBytes := myAES.DecryptAES(cryptBytes, []byte(myAES.MYKEY))
fmt.Println("解密效果:")
fmt.Println("src bytes:", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
fmt.Println("dst bytes:", hex.EncodeToString(deCryptBytes), "[]byte長度:", len(deCryptBytes))
fmt.Println()
fmt.Println("原字串為:" + srcString)
fmt.Println("解密字符為:" + string(deCryptBytes))
}
//OUTPUT:
測試AES對稱加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e2809420414553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41
dst : 07a22e3059c8e9da2a3c1db2801b478a18f3ca7b33ace2f19931d2100a792681c61ac7ff3a61e0973be91638b3e9b1e1 []byte長度: 48
解密效果:
src bytes: 07a22e3059c8e9da2a3c1db2801b478a18f3ca7b33ace2f19931d2100a792681c61ac7ff3a61e0973be91638b3e9b1e1 []byte長度: 48
dst bytes: 474f20e5af86e7a081e5ada620e28094e2809420414553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41
原字串為:GO 密碼學 —— AES對稱加密實現
解密字符為:GO 密碼學 —— AES對稱加密實現