深入探索Golang中的SM4加密解密算法
前言
最近做的項目對安全性要求比較高,特別強(qiáng)調(diào):系統(tǒng)不能涉及MD5、SHA1、RSA1024、DES高風(fēng)險算法。
那用什么嘞?甲方:建議用國產(chǎn)密碼算法SM4。
擅長敏捷開發(fā)(CV大法)的我,先去GitHub找了開源項目、又去網(wǎng)絡(luò)上找了一些教程,但是或多或少都有些問題:
- 比如
golang.org/x/crypto/sm4
無法安裝編譯 - 比如C站爛大街的SM4教程,不能解決數(shù)據(jù)填充的問題,超過16位就解密失敗了
- 比如如何封裝成通用的方法,供系統(tǒng)進(jìn)行調(diào)用
- 更多就是復(fù)制粘貼了SM4的定義,很抽象。
于是我花了2天時間研究SM4的原理和應(yīng)用,解決了上面這些問題,整理這篇文章分享給大家,讓大家能少踩坑。
我會按照下面的順序分享這篇文章,方便大家更好的理解,如果你就是喜歡拿來主義(敏捷開發(fā)),可以直接copy底部的示例代碼,快速上手使用即可。
1. SM4的優(yōu)勢
相比于其他加密算法,SM4加密算法具有以下幾個優(yōu)勢:
- 高安全性:SM4是一種對稱加密算法,采用128位密鑰長度,具有較高的安全性和抗攻擊性。它經(jīng)過了廣泛的安全性分析和評估,并通過了多個密碼學(xué)標(biāo)準(zhǔn)的驗證。
- 高效性:SM4算法的加密和解密速度較快,適用于對大量數(shù)據(jù)進(jìn)行加密和解密的場景。它在硬件和軟件實現(xiàn)上都具有高效性能。
- 簡單性:SM4算法的實現(xiàn)相對簡單,代碼量較小,易于理解和使用。它的設(shè)計目標(biāo)之一是提供一種易于實現(xiàn)和部署的加密算法。
- 標(biāo)準(zhǔn)化:SM4算法是密碼算法標(biāo)準(zhǔn),得到了廣泛的應(yīng)用和認(rèn)可。它已成為國際上公認(rèn)的密碼算法之一。
- 廣泛支持:SM4算法在各種平臺和編程語言中都有支持和實現(xiàn),包括Go、Java、C/C++等。它可以在不同的系統(tǒng)和環(huán)境中進(jìn)行跨平臺的應(yīng)用和部署。
- 可擴(kuò)展性:SM4算法支持不同的工作模式和填充方式,可以根據(jù)具體需求進(jìn)行靈活配置。它可以與其他密碼算法結(jié)合使用,提供更高級別的安全保護(hù)。
小小的總結(jié)一下:SM4加密算法在安全性、高效性、簡單性、標(biāo)準(zhǔn)化和廣泛支持等方面具有優(yōu)勢,適用于各種數(shù)據(jù)保護(hù)和加密應(yīng)用場景。它是一種可靠的加密算法選擇。
2.IV是什么
我在學(xué)習(xí)的時候看到IV就蒙了,所以有必要先說清楚IV的概念:
Initialization Vector(IV)是一種在密碼學(xué)中使用的初始值。它是一個固定長度的隨機(jī)數(shù)或者隨機(jī)生成的值,用于在加密算法中初始化密碼算法的狀態(tài)。
在加密過程中,IV的作用是引入隨機(jī)性和唯一性,以增加加密的安全性。 它與密鑰一起用于初始化密碼算法的內(nèi)部狀態(tài),確保每次加密操作都產(chǎn)生不同的輸出,即使相同的明文使用相同的密鑰進(jìn)行加密。
IV的長度和使用方式取決于具體的加密算法和應(yīng)用場景。在使用加密算法時,IV通常需要與密文一起傳輸給解密方,以便解密方能夠正確還原明文。
需要注意的是:IV本身不需要保密,可以與密文一起傳輸。然而,為了確保加密的安全性,IV應(yīng)該是隨機(jī)生成的,并且每次加密操作都應(yīng)該使用不同的IV。這樣可以防止密碼分析者通過觀察加密結(jié)果的模式來破解密鑰或者明文。
3. SM4加密的方式和原理
SM4加密算法是一種對稱加密算法,采用分組密碼的方式對數(shù)據(jù)進(jìn)行加密。
下面是SM4加密的方式和原理的簡要說明:
1.密鑰擴(kuò)展:SM4使用128位的密鑰,首先對密鑰進(jìn)行擴(kuò)展,生成32個子密鑰,用于后續(xù)的加密輪操作。
2.初始輪:將明文分為4個字節(jié)的分組,與第一個子密鑰進(jìn)行異或操作。
3.加密輪:SM4加密算法共進(jìn)行32輪加密操作。每輪操作包括以下步驟:
- 字節(jié)替換:使用S盒進(jìn)行字節(jié)替換。
- 行移位:對每個分組進(jìn)行行移位操作。
- 列混淆:對每個分組進(jìn)行列混淆操作。
- 輪密鑰加:將當(dāng)前輪的子密鑰與分組進(jìn)行異或操作。
4.最終輪:在最后一輪加密操作中,不進(jìn)行列混淆操作,只進(jìn)行字節(jié)替換、行移位和輪密鑰加操作。
5.輸出:經(jīng)過32輪加密操作后,得到加密后的密文。
SM4加密算法的安全性和強(qiáng)度主要來自于其復(fù)雜的輪函數(shù)和密鑰擴(kuò)展過程。它具有較高的安全性和抗攻擊性,并且在實際應(yīng)用中得到了廣泛的應(yīng)用和認(rèn)可。
需要注意的是:SM4加密算法的安全性還依賴于密鑰的保密性和隨機(jī)性。在使用SM4進(jìn)行加密時,應(yīng)確保使用足夠強(qiáng)度的密鑰,并采取適當(dāng)?shù)拿荑€管理和保護(hù)措施。
4.SM4的各種工作模式對比
SM4加密算法可以使用不同的工作模式,其中包括CBC(Cipher Block Chaining)模式。
我使用的是CBC模式,下面和大家分享一下CBC模式與其他模式的對比:
1.CBC模式(Cipher Block Chaining):
- 特點(diǎn):每個明文塊與前一個密文塊進(jìn)行異或操作,然后再進(jìn)行加密。初始塊使用初始化向量(IV)。
- 優(yōu)點(diǎn):具有較好的安全性,能夠隱藏明文的模式和重復(fù)性。
- 缺點(diǎn):加密過程是串行的,不適合并行處理。
2.ECB模式(Electronic Codebook):
- 特點(diǎn):將每個明文塊獨(dú)立加密,相同的明文塊會得到相同的密文塊。
- 優(yōu)點(diǎn):簡單、并行處理效率高。
- 缺點(diǎn):不能隱藏明文的模式和重復(fù)性,不適合加密大量重復(fù)的數(shù)據(jù)。
3.CFB模式(Cipher Feedback):
- 特點(diǎn):將前一個密文塊作為輸入來加密當(dāng)前的明文塊,可以實現(xiàn)流密碼的功能。
- 優(yōu)點(diǎn):能夠處理不定長的數(shù)據(jù)流,適用于實時加密和流式傳輸。
- 缺點(diǎn):加密過程是串行的,不適合并行處理。
4.OFB模式(Output Feedback):
- 特點(diǎn):將前一個密文塊作為輸入來生成密鑰流,然后與明文塊進(jìn)行異或操作,可以實現(xiàn)流密碼的功能。
- 優(yōu)點(diǎn):能夠處理不定長的數(shù)據(jù)流,適用于實時加密和流式傳輸。
- 缺點(diǎn):加密過程是串行的,不適合并行處理。
5.CTR模式(Counter):
- 特點(diǎn):使用一個計數(shù)器來生成密鑰流,然后與明文塊進(jìn)行異或操作,可以實現(xiàn)流密碼的功能。
- 優(yōu)點(diǎn):能夠處理不定長的數(shù)據(jù)流,適用于實時加密和流式傳輸。并行處理效率高,適合硬件實現(xiàn)。
- 缺點(diǎn):需要保證計數(shù)器的唯一性,否則會導(dǎo)致密鑰流的重復(fù)。
對比總結(jié):
- CBC模式和ECB模式相比,CBC模式具有更好的安全性,能夠隱藏明文的模式和重復(fù)性,而ECB模式無法隱藏這些信息。
- CFB模式、OFB模式和CTR模式都是流密碼模式,適用于不定長的數(shù)據(jù)流加密,能夠?qū)崿F(xiàn)實時加密和流式傳輸。它們的主要區(qū)別在于密鑰流的生成方式和加密過程的并行性。
- CFB模式和OFB模式的加密過程是串行的,不適合并行處理,而CTR模式的加密過程可以并行處理,適合硬件實現(xiàn)。
總的來說:CBC模式在安全性方面較好,能夠隱藏明文的模式和重復(fù)性。而流密碼模式(CFB、OFB和CTR)適用于不定長數(shù)據(jù)流的加密,能夠?qū)崿F(xiàn)實時加密和流式傳輸,其中CTR模式具有較好的并行處理性能。選擇合適的加密模式取決于具體的應(yīng)用需求和安全性要求。
5. 直接可用的「代碼示例」
我一直認(rèn)為可以通過復(fù)制粘貼,直接跑通的示例代碼才是好代碼。
沒錯,我的代碼示例就是這樣,并且關(guān)鍵代碼都寫好了注釋:
package main import ( "bytes" "crypto/cipher" "encoding/hex" "fmt" "github.com/tjfoc/gmsm/sm4" ) // SM4加密 func SM4Encrypt(data string) (result string, err error) { //字符串轉(zhuǎn)byte切片 plainText := []byte(data) //建議從配置文件中讀取秘鑰,進(jìn)行統(tǒng)一管理 SM4Key := "Uv6tkf2M3xYSRuFv" //todo 注意:iv需要是隨機(jī)的,進(jìn)一步保證加密的安全性,將iv的值和加密后的數(shù)據(jù)一起返回給外部 SM4Iv := "04TzMuvkHm_EZnHm" iv := []byte(SM4Iv) key := []byte(SM4Key) //實例化sm4加密對象 block, err := sm4.NewCipher(key) if err != nil { panic(err) } //明文數(shù)據(jù)填充 paddingData := paddingLastGroup(plainText, block.BlockSize()) //聲明SM4的加密工作模式 blockMode := cipher.NewCBCEncrypter(block, iv) //為填充后的數(shù)據(jù)進(jìn)行加密處理 cipherText := make([]byte, len(paddingData)) //使用CryptBlocks這個核心方法,將paddingData進(jìn)行加密處理,將加密處理后的值賦值到cipherText中 blockMode.CryptBlocks(cipherText, paddingData) //加密結(jié)果使用hex轉(zhuǎn)成字符串,方便外部調(diào)用 cipherString := hex.EncodeToString(cipherText) return cipherString, nil } // SM4解密 傳入string 輸出string func SM4Decrypt(data string) (res string, err error) { //秘鑰 SM4Key := "Uv6tkf2M3xYSRuFv" //iv是Initialization Vector,初始向量, SM4Iv := "04TzMuvkHm_EZnHm" iv := []byte(SM4Iv) key := []byte(SM4Key) block, err := sm4.NewCipher(key) if err != nil { panic(err) } //使用hex解碼 decodeString, err := hex.DecodeString(data) if err != nil { return "", err } //CBC模式 優(yōu)點(diǎn):具有較好的安全性,能夠隱藏明文的模式和重復(fù)性。 缺點(diǎn):加密過程是串行的,不適合并行處理。 blockMode := cipher.NewCBCDecrypter(block, iv) //下文有詳解這段代碼的含義 blockMode.CryptBlocks(decodeString, decodeString) //去掉明文后面的填充數(shù)據(jù) plainText := unPaddingLastGroup(decodeString) //直接返回字符串類型,方便外部調(diào)用 return string(plainText), nil } // 明文數(shù)據(jù)填充 func paddingLastGroup(plainText []byte, blockSize int) []byte { //1.計算最后一個分組中明文后需要填充的字節(jié)數(shù) padNum := blockSize - len(plainText)%blockSize //2.將字節(jié)數(shù)轉(zhuǎn)換為byte類型 char := []byte{byte(padNum)} //3.創(chuàng)建切片并初始化 newPlain := bytes.Repeat(char, padNum) //4.將填充數(shù)據(jù)追加到原始數(shù)據(jù)后 newText := append(plainText, newPlain...) return newText } // 去掉明文后面的填充數(shù)據(jù) func unPaddingLastGroup(plainText []byte) []byte { //1.拿到切片中的最后一個字節(jié) length := len(plainText) lastChar := plainText[length-1] //2.將最后一個數(shù)據(jù)轉(zhuǎn)換為整數(shù) number := int(lastChar) return plainText[:length-number] } func main() { //待加密的數(shù)據(jù) 模擬18位的身份證號 plainText := "131229199907097219" //SM4加密 decrypt, err := SM4Encrypt(plainText) if err != nil { return } fmt.Printf("sm4加密結(jié)果:%s\n", decrypt) //cipherString := hex.EncodeToString(cipherText) //fmt.Printf("sm4加密結(jié)果轉(zhuǎn)成字符串:%s\n", cipherString) //SM4解密 sm4Decrypt, err := SM4Decrypt(decrypt) if err != nil { return } fmt.Printf("plainText:%s\n", sm4Decrypt) flag := plainText == sm4Decrypt fmt.Println("解密是否成功:", flag) }
運(yùn)行結(jié)果如下:
6. 核心方法的源碼解析
細(xì)心的小伙伴應(yīng)該又發(fā)現(xiàn),(或者通過你真實的敲代碼一定能發(fā)現(xiàn)。
在加密和解密部分有一個CryptBlocks()方法,我們來解析一下這段源碼:
// CryptBlocks encrypts or decrypts a number of blocks. The length of // src must be a multiple of the block size. Dst and src must overlap // entirely or not at all. // // If len(dst) < len(src), CryptBlocks should panic. It is acceptable // to pass a dst bigger than src, and in that case, CryptBlocks will // only update dst[:len(src)] and will not touch the rest of dst. // // Multiple calls to CryptBlocks behave as if the concatenation of // the src buffers was passed in a single run. That is, BlockMode // maintains state and does not reset at each CryptBlocks call. CryptBlocks(dst, src []byte)
翻譯翻譯
CryptBlocks方法用于加密或解密多個數(shù)據(jù)塊。src的長度必須是塊大小的倍數(shù)。dst和src必須完全重疊或完全不重疊。
如果len(dst) < len(src),CryptBlocks方法應(yīng)該引發(fā)panic。允許傳遞比src更大的dst,此時CryptBlocks只會更新dst[:len(src)],不會觸及dst的其余部分。
在這段代碼注釋中,dst表示目標(biāo)緩沖區(qū),用于存儲加密或解密后的結(jié)果。src表示源緩沖區(qū),包含要加密或解密的數(shù)據(jù)。這兩個緩沖區(qū)可以是相同的內(nèi)存區(qū)域,也可以是不同的內(nèi)存區(qū)域。CryptBlocks方法會將src中的數(shù)據(jù)進(jìn)行加密或解密,并將結(jié)果存儲在dst中。
需要注意的是,dst和src的長度必須是塊大小的倍數(shù),否則CryptBlocks方法可能會引發(fā)panic。如果dst的長度小于src的長度,CryptBlocks方法只會更新dst的前l(fā)en(src)個字節(jié),并不會修改dst的其余部分。
此外,CryptBlocks方法可以多次調(diào)用,多次調(diào)用的效果相當(dāng)于將所有src緩沖區(qū)的數(shù)據(jù)連接在一起,然后進(jìn)行加密或解密。這意味著BlockMode會保持狀態(tài),并且不會在每次CryptBlocks調(diào)用時重置。
如果你看注釋翻譯理解起來還是比較抽象的話,我換個方式介紹一下:
用我的話來說
在SM4加密中,CryptBlocks()方法是用于加密或解密多個數(shù)據(jù)塊的方法。它是SM4算法中的一個核心函數(shù)。
具體來說,CryptBlocks()方法接受一個源數(shù)據(jù)緩沖區(qū)(src)和一個目標(biāo)數(shù)據(jù)緩沖區(qū)(dst),并對源數(shù)據(jù)進(jìn)行加密或解密操作,將結(jié)果存儲在目標(biāo)數(shù)據(jù)緩沖區(qū)中。
在加密過程中,CryptBlocks()方法會將源數(shù)據(jù)分成多個數(shù)據(jù)塊,然后對每個數(shù)據(jù)塊進(jìn)行加密操作,并將結(jié)果存儲在目標(biāo)數(shù)據(jù)緩沖區(qū)中。加密過程中使用的密鑰和其他參數(shù)由SM4算法的實現(xiàn)確定。
在解密過程中,CryptBlocks()方法會對源數(shù)據(jù)緩沖區(qū)中的數(shù)據(jù)塊進(jìn)行解密操作,并將解密后的結(jié)果存儲在目標(biāo)數(shù)據(jù)緩沖區(qū)中。
需要注意的是:CryptBlocks()方法要求源數(shù)據(jù)緩沖區(qū)和目標(biāo)數(shù)據(jù)緩沖區(qū)的長度必須是SM4算法的塊大小的倍數(shù)。否則,可能會引發(fā)錯誤或產(chǎn)生不可預(yù)測的結(jié)果。
CryptBlocks()方法是SM4加密算法中用于加密或解密多個數(shù)據(jù)塊的關(guān)鍵方法,它實現(xiàn)了SM4算法的核心功能。
以上就是深入探索Golang中的SM4加密解密算法的詳細(xì)內(nèi)容,更多關(guān)于Golang SM4加密解密算法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang實現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法詳解
在 Go 語言中,將 IP 地址轉(zhuǎn)換為整數(shù)涉及到解析 IP 地址并處理其字節(jié)表示,本文給大家介紹了Golang實現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下2024-02-02golang使用go mod導(dǎo)入本地包和第三方包的方式
這篇文章主要介紹了golang使用go mod導(dǎo)入本地包和第三方包的方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01golang 如何用反射reflect操作結(jié)構(gòu)體
這篇文章主要介紹了golang 用反射reflect操作結(jié)構(gòu)體的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04淺析如何利用Go的plugin機(jī)制實現(xiàn)熱更新
熱更新,或稱熱重載或動態(tài)更新,是一種軟件更新技術(shù),允許程序在運(yùn)行時,不停機(jī)更新代碼或資源,本文主要來討論下GO語言是否可以利用plugin機(jī)制實現(xiàn)熱更新,感興趣的可以了解下2024-04-04使用Go和Tesseract實現(xiàn)驗證碼識別的流程步驟
驗證碼主要用于區(qū)分人類用戶和機(jī)器程序,Tesseract 是一個開源的光學(xué)字符識別(OCR)引擎,支持多種語言和字體,并具有較高的識別準(zhǔn)確率,它由 Google 維護(hù),并且可以通過多種編程語言調(diào)用,本文給大家介紹了使用Go和Tesseract實現(xiàn)驗證碼識別的流程步驟2025-01-01