通過Golang編寫一個AES加密解密工具
前言
本文包含如下兩個內(nèi)容:
AES加密介紹及實現(xiàn)原理
Go實現(xiàn)AES加密和解密工具
AES加密介紹及實現(xiàn)原理
AES( advanced encryption standard)使用相同密鑰進行加密和解密,也就是對稱加密。其他的對稱加密如DES,由于DES密鑰長度只有56位如今的算力甚至可以在5分鐘內(nèi)破解,而AES最高級別達(dá)到了256位密鑰長度,如果采用窮舉法,目前來看AES是一種”無法“被破解的加密存在。

AES用在哪里
如果你正在瀏覽本文,那么你就在使用AES(https協(xié)議中一部分使用了對稱加密)。
- 綠色上網(wǎng):通過加密安全地連接到另一臺搬石頭砸腳的服務(wù)器。
- 無線網(wǎng)絡(luò)WIFI:和WAP2一起使用。
- 應(yīng)用程序:wechat、JD、Alipay等使用 AES 加密照片和消息或支付信息。
- 存檔和壓縮工具:7z、WinZip 和 RAR。
- 操作系統(tǒng)組件:一些操作系統(tǒng)組件(如文件系統(tǒng))使用高級加密標(biāo)準(zhǔn)來確保安全性。
- 編程語言庫: Go、Python 和 C++ 等編碼庫實現(xiàn)了的AES加密(等會使用到)。
AES加密是如何實現(xiàn)的
參考:
What is AES encryption and how does it work?
Block cipher mode of operation
從宏觀上來看AES加密過程中的一輪(根據(jù)不同的密鑰長度,輪數(shù)不一樣,下面會說到)如下:

1.數(shù)據(jù)分塊
首先把明文按照128bit拆分成若干個明文塊(圖上黃色塊),一個字節(jié)包含 8 位,布局為 4×4矩陣(上圖黃色部分),對最后一塊填充至128bit,填充方式有PKCS7Padding(采用)/PKCS5Padding/ZeroPadding,無論咋填充最后解密時都要去除這些多余的填充。
2.密鑰擴展
AES通過Rijndael's key schedule 將密鑰被擴展為 (n+1) 個密鑰,其中 n 是加密過程中要遵循的輪數(shù)。AES每個標(biāo)準(zhǔn)規(guī)定了所要加密的輪數(shù),對于128位密鑰,輪數(shù)是 10,要生成的密鑰個數(shù)為 10+1,總共 11 個密鑰。
| 標(biāo)準(zhǔn) | 密鑰長度 | 輪數(shù) | 分組長度 |
|---|---|---|---|
| AES-128 | 128位(16字節(jié)) | 10 | 128位(16字節(jié)) |
| AES-192 | 192位(24字節(jié)) | 12 | 128位(16字節(jié)) |
| AES-256 | 256位(32字節(jié)) | 14 | 128位(16字節(jié)) |
每一輪所要做的包括:字節(jié)替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)、加輪密鑰(AddRoundKey)

3.字節(jié)替代(SubBytes)
每輪開始,首先進行SubBytes,字節(jié)根據(jù)預(yù)定義的 Rijndael S-box(可以簡單認(rèn)為是一個轉(zhuǎn)換表)規(guī)定的規(guī)則進行替換。對a[i,j]中的每個字節(jié)進行一次轉(zhuǎn)換后得到b[i,j]

4.行移位(ShiftRows)
對上一步得到矩陣進行ShiftRows,第一行不變,第二行移動1位,第三行2位,第四行3位。

5.列混淆(MixColumns)
再對矩陣的每一列和修補矩陣fixed matrix的二維常量數(shù)組做矩陣相乘,得到對應(yīng)的輸出列。

6.加輪密鑰(AddRoundKey)
先將擴展密鑰Kn排列成4×4矩陣,然后讓輸入數(shù)組的每一個字節(jié)a[i,j]與密鑰對應(yīng)位置的字節(jié)k[i,j]異或一次,得到輸出b[i,j]。最后一輪不參與AddRoundKey

經(jīng)過如上的10輪操作之后,得到了一個明文塊的加密字符。解密則進行反向加密。
AES加密模式
ECB
在上面加密過程中每一個明文塊都是獨立進行加密的,簡單且高效,但是如果一個段數(shù)據(jù)存在相關(guān)的明文塊,則加密后的密文也會相同,對安全性也有一定影響。

CBC
CBC加密模式如下圖所示,初始向量IV和明文異或,每個塊的密文作為后續(xù)塊的“向量”,讓每一個密文獨一無二。我們待會采用這種模式。

Go實現(xiàn)AES加密工具scode
ok,上面大致了解AES加密是如何工作起來的,接下來通過Go中的crypto/aes和crypto/cipher包實現(xiàn)的AES加密解密工具。
PKCS7Padding將待補足字節(jié)數(shù)作為填充的字節(jié)
// pkcs7Padding 填充
func pkcs7Padding(data []byte, blockSize int) []byte {
//判斷缺少幾位長度。最少1,最多 blockSize
padding := blockSize - len(data)%blockSize
//補足位數(shù)。把切片[]byte{byte(padding)}復(fù)制padding個
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// pkcs7UnPadding 移除
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("加密字符串錯誤!")
}
//獲取填充的個數(shù)
unPadding := int(data[length-1])
return data[:(length - unPadding)], nil
}
使用 cipher的CBC模式對block加密和解密
// AesEncrypt 加密
func AesEncrypt(data []byte, key []byte) ([]byte, error) {
// NewCipher creates and returns a new cipher.Block. The key argument should be the AES key, either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
//判斷加密快的大小
blockSize := block.BlockSize()
//填充
encryptBytes := pkcs7Padding(data, blockSize)
//初始化加密數(shù)據(jù)接收切片
crypted := make([]byte, len(encryptBytes))
//使用cbc加密模式
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
//執(zhí)行加密
blockMode.CryptBlocks(crypted, encryptBytes)
return crypted, nil
}
// AesDecrypt 解密
func AesDecrypt(data []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
//獲取塊的大小
blockSize := block.BlockSize()
//使用cbc
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
//初始化解密數(shù)據(jù)接收切片
crypted := make([]byte, len(data))
//執(zhí)行解密
blockMode.CryptBlocks(crypted, data)
//去填充
crypted, err = pkcs7UnPadding(crypted)
if err != nil {
return nil, err
}
return crypted, nil
}
循環(huán)從文件中讀取100mb源數(shù)據(jù)用于加密后將密文寫入文件,解密則讀取密文解密后將源數(shù)據(jù)寫入文件。
func EncryptFile(fileName string) (err error) {
f, err := os.Open(fileName)
if err != nil {
fmt.Println("未找到文件")
return
}
defer f.Close()
fInfo, _ := f.Stat()
fLen := fInfo.Size()
fmt.Println("待處理文件大小:", fLen)
maxLen := 1024 * 1024 * 100 //100mb 每 100mb 進行加密一次
var forNum int64 = 0
getLen := fLen
if fLen > int64(maxLen) {
getLen = int64(maxLen)
forNum = fLen / int64(maxLen)
fmt.Println("需要加密次數(shù):", forNum+1)
}
// encryptd to file
ff, err := os.OpenFile("en_"+fileName, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("文件寫入錯誤")
return err
}
defer ff.Close()
//循環(huán)加密,并寫入文件
for i := 0; i < int(forNum+1); i++ {
a := make([]byte, getLen)
n, err := f.Read(a)
if err != nil {
fmt.Println("文件讀取錯誤")
return err
}
getByte, err := EncryptByAes(a[:n])
if err != nil {
fmt.Println("加密錯誤")
return err
}
getBytes := append([]byte(getByte), []byte("\n")...)
//寫入
buf := bufio.NewWriter(ff)
buf.WriteString(string(getBytes[:]))
buf.Flush()
}
ffInfo, _ := ff.Stat()
fmt.Printf("加密后文件為:%s,文件大小為:%v Byte \n", ffInfo.Name(), ffInfo.Size())
return nil
}
參考:Golang實現(xiàn)AES加密和解密的示例代碼
通過cobra添加命令后,創(chuàng)建命令的匿名函數(shù)
func(cmd *cobra.Command, args []string) {
copy(PwdKey, readPass())
Pwd := []byte("csgo!gogo")
if ByteSliceEqual(PwdKey, Pwd) {
//16字節(jié)key
PwdKey = append(PwdKey, 7, 3, 5, 5, 6, 0, 8)
if err := DecryptFile(args[0]); err != nil {
panic(err)
}
} else {
fmt.Println("密碼錯誤")
os.Exit(1)
}
}
使用方式看起來如下:
scode工具包含2個命令encode和decode,解密文件需要密碼。
# ./scode encode xpower.tar.gz 待處理文件大小: 3397 加密后文件為:en_xpower.tar.gz,文件大小為:4545 Byte # ./scode decode en_xpower.tar.gz ENTER PASSWORD: 密碼錯誤 # ./scode decode en_xpower.tar.gz ENTER PASSWORD: 待處理文件大小: 4545 解密后文件為:de_en_xpower.tar.gz,文件大小為:3159 Byte
完整代碼:source
到此這篇關(guān)于通過Golang編寫一個AES加密解密工具的文章就介紹到這了,更多相關(guān)Golang AES加密解密工具內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang?gorm模型結(jié)構(gòu)體的定義示例
這篇文章主要為大家介紹了golang?gorm模型結(jié)構(gòu)體的定義示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04
Windows上安裝Go并配置環(huán)境變量(圖文步驟)
開始使用Go創(chuàng)建應(yīng)用程序之前,需要設(shè)置開發(fā)環(huán)境,本文主要介紹了Windows上安裝Go并配置環(huán)境變量,具有一定的參考價值,感興趣的可以了解一下2023-08-08

