保護數據隱私:深入探索Golang中的SM4加密解密演算法

来源:https://www.cnblogs.com/wangzhongyang/archive/2023/06/25/17502480.html
-Advertisement-
Play Games

確保網路請求數據傳輸的安全性、一致性和防篡改是至關重要的。通過結合對稱加密和非對稱加密的強大能力,我們可以實現高度安全的數據傳輸。對稱加密提供了快速且高效的加密和解密過程,而非對稱加密則保證了密鑰的安全性。這種結合能夠確保數據在傳輸過程中的保密性、完整性和可靠性,有效防止數據被篡改或竊取。無論是保護... ...


前言

最近做的項目對安全性要求比較高,特別強調:系統不能涉及MD5、SHA1、RSA1024、DES高風險演算法。

那用什麼嘞?甲方:建議用國產密碼演算法SM4。

擅長敏捷開發(CV大法)的我,先去GitHub找了開源項目、又去網路上找了一些教程,但是或多或少都有些問題:

  1. 比如golang.org/x/crypto/sm4無法安裝編譯
  2. 比如C站爛大街的SM4教程,不能解決數據填充的問題,超過16位就解密失敗了
  3. 比如如何封裝成通用的方法,供系統進行調用
  4. 更多就是複製粘貼了SM4的定義,很抽象。

於是我花了2天時間研究SM4的原理和應用,解決了上面這些問題,整理這篇文章分享給大家,讓大家能少踩坑。

我會按照下麵的順序分享這篇文章,方便大家更好的理解,如果你就是喜歡拿來主義(敏捷開發),可以直接copy底部的示例代碼,快速上手使用即可。

文章目錄

  1. SM4的優勢
  2. IV是什麼?
  3. SM4加密的方式和原理
  4. SM4的各種工作模式對比
  5. 直接可用的「代碼示例」
  6. 核心方法的源碼解析
  7. 總結回顧

1. SM4的優勢

相比於其他加密演算法,SM4加密演算法具有以下幾個優勢:

  1. 高安全性:SM4是一種對稱加密演算法,採用128位密鑰長度,具有較高的安全性和抗攻擊性。它經過了廣泛的安全性分析和評估,並通過了多個密碼學標準的驗證。

  2. 高效性:SM4演算法的加密和解密速度較快,適用於對大量數據進行加密和解密的場景。它在硬體和軟體實現上都具有高效性能。

  3. 簡單性:SM4演算法的實現相對簡單,代碼量較小,易於理解和使用。它的設計目標之一是提供一種易於實現和部署的加密演算法。

  4. 標準化SM4演算法是中國國家密碼管理局發佈的密碼演算法標準,得到了廣泛的應用和認可。它已成為國際上公認的密碼演算法之一。

  5. 廣泛支持:SM4演算法在各種平臺和編程語言中都有支持和實現,包括Go、Java、C/C++等。它可以在不同的系統和環境中進行跨平臺的應用和部署。

  6. 可擴展性:SM4演算法支持不同的工作模式和填充方式,可以根據具體需求進行靈活配置。它可以與其他密碼演算法結合使用,提供更高級別的安全保護。

小小的總結一下:SM4加密演算法在安全性、高效性、簡單性、標準化和廣泛支持等方面具有優勢,適用於各種數據保護和加密應用場景。它是一種可靠的加密演算法選擇。

2.IV是什麼?

我在學習的時候看到IV就蒙了,所以有必要先說清楚IV的概念:

Initialization Vector(IV)是一種在密碼學中使用的初始值。它是一個固定長度的隨機數或者隨機生成的值,用於在加密演算法中初始化密碼演算法的狀態。

在加密過程中,IV的作用是引入隨機性和唯一性,以增加加密的安全性。 它與密鑰一起用於初始化密碼演算法的內部狀態,確保每次加密操作都產生不同的輸出,即使相同的明文使用相同的密鑰進行加密。

IV的長度和使用方式取決於具體的加密演算法和應用場景。在使用加密演算法時,IV通常需要與密文一起傳輸給解密方,以便解密方能夠正確還原明文。

需要註意的是:IV本身不需要保密,可以與密文一起傳輸。然而,為了確保加密的安全性,IV應該是隨機生成的,並且每次加密操作都應該使用不同的IV。這樣可以防止密碼分析者通過觀察加密結果的模式來破解密鑰或者明文。

3. SM4加密的方式和原理

SM4加密演算法是一種對稱加密演算法,採用分組密碼的方式對數據進行加密。

下麵是SM4加密的方式和原理的簡要說明:

  1. 密鑰擴展:SM4使用128位的密鑰,首先對密鑰進行擴展,生成32個子密鑰,用於後續的加密輪操作。

  2. 初始輪:將明文分為4個位元組的分組,與第一個子密鑰進行異或操作。

  3. 加密輪:SM4加密演算法共進行32輪加密操作。每輪操作包括以下步驟:

    • 位元組替換:使用S盒進行位元組替換。
    • 行移位:對每個分組進行行移位操作。
    • 列混淆:對每個分組進行列混淆操作。
    • 輪密鑰加:將當前輪的子密鑰與分組進行異或操作。
  4. 最終輪:在最後一輪加密操作中,不進行列混淆操作,只進行位元組替換、行移位和輪密鑰加操作。

  5. 輸出:經過32輪加密操作後,得到加密後的密文。

SM4加密演算法的安全性和強度主要來自於其複雜的輪函數和密鑰擴展過程。它具有較高的安全性和抗攻擊性,並且在實際應用中得到了廣泛的應用和認可。

需要註意的是:SM4加密演算法的安全性還依賴於密鑰的保密性和隨機性。在使用SM4進行加密時,應確保使用足夠強度的密鑰,並採取適當的密鑰管理和保護措施。

4.SM4的各種工作模式對比

SM4加密演算法可以使用不同的工作模式,其中包括CBC(Cipher Block Chaining)模式。

我使用的是CBC模式,下麵和大家分享一下CBC模式與其他模式的對比:

  1. CBC模式(Cipher Block Chaining):
  • 特點:每個明文塊與前一個密文塊進行異或操作,然後再進行加密。初始塊使用初始化向量(IV)。
  • 優點:具有較好的安全性,能夠隱藏明文的模式和重覆性。
  • 缺點:加密過程是串列的,不適合併行處理。
  1. ECB模式(Electronic Codebook):
  • 特點:將每個明文塊獨立加密,相同的明文塊會得到相同的密文塊。
  • 優點:簡單、並行處理效率高。
  • 缺點:不能隱藏明文的模式和重覆性,不適合加密大量重覆的數據。
  1. CFB模式(Cipher Feedback):
  • 特點:將前一個密文塊作為輸入來加密當前的明文塊,可以實現流密碼的功能。
  • 優點:能夠處理不定長的數據流,適用於實時加密和流式傳輸。
  • 缺點:加密過程是串列的,不適合併行處理。
  1. OFB模式(Output Feedback):
  • 特點:將前一個密文塊作為輸入來生成密鑰流,然後與明文塊進行異或操作,可以實現流密碼的功能。
  • 優點:能夠處理不定長的數據流,適用於實時加密和流式傳輸。
  • 缺點:加密過程是串列的,不適合併行處理。
  1. CTR模式(Counter):
  • 特點:使用一個計數器來生成密鑰流,然後與明文塊進行異或操作,可以實現流密碼的功能。
  • 優點:能夠處理不定長的數據流,適用於實時加密和流式傳輸。並行處理效率高,適合硬體實現。
  • 缺點:需要保證計數器的唯一性,否則會導緻密鑰流的重覆。

對比總結:

  • CBC模式和ECB模式相比,CBC模式具有更好的安全性,能夠隱藏明文的模式和重覆性,而ECB模式無法隱藏這些信息。
  • CFB模式、OFB模式和CTR模式都是流密碼模式,適用於不定長的數據流加密,能夠實現實時加密和流式傳輸。它們的主要區別在於密鑰流的生成方式和加密過程的並行性。
  • CFB模式和OFB模式的加密過程是串列的,不適合併行處理,而CTR模式的加密過程可以並行處理,適合硬體實現。

總的來說:CBC模式在安全性方面較好,能夠隱藏明文的模式和重覆性。而流密碼模式(CFB、OFB和CTR)適用於不定長數據流的加密,能夠實現實時加密和流式傳輸,其中CTR模式具有較好的並行處理性能。選擇合適的加密模式取決於具體的應用需求和安全性要求。

5. 直接可用的「代碼示例」

我一直認為可以通過複製粘貼,直接跑通的示例代碼才是好代碼。

沒錯,我的代碼示例就是這樣,並且關鍵代碼都寫好了註釋:

package main

import (
	"bytes"
	"crypto/cipher"
	"encoding/hex"
	"fmt"
	"github.com/tjfoc/gmsm/sm4"
)

// SM4加密
func SM4Encrypt(data string) (result string, err error) {
	//字元串轉byte切片
	plainText := []byte(data)
	//建議從配置文件中讀取秘鑰,進行統一管理
	SM4Key := "Uv6tkf2M3xYSRuFv"
	//todo 註意:iv需要是隨機的,進一步保證加密的安全性,將iv的值和加密後的數據一起返回給外部
	SM4Iv := "04TzMuvkHm_EZnHm"
	iv := []byte(SM4Iv)
	key := []byte(SM4Key)
	//實例化sm4加密對象
	block, err := sm4.NewCipher(key)
	if err != nil {
		panic(err)
	}
	//明文數據填充
	paddingData := paddingLastGroup(plainText, block.BlockSize())
	//聲明SM4的加密工作模式
	blockMode := cipher.NewCBCEncrypter(block, iv)
	//為填充後的數據進行加密處理
	cipherText := make([]byte, len(paddingData))
	//使用CryptBlocks這個核心方法,將paddingData進行加密處理,將加密處理後的值賦值到cipherText中
	blockMode.CryptBlocks(cipherText, paddingData)
	//加密結果使用hex轉成字元串,方便外部調用
	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模式 優點:具有較好的安全性,能夠隱藏明文的模式和重覆性。 缺點:加密過程是串列的,不適合併行處理。
	blockMode := cipher.NewCBCDecrypter(block, iv)
	//下文有詳解這段代碼的含義
	blockMode.CryptBlocks(decodeString, decodeString)
	//去掉明文後面的填充數據
	plainText := unPaddingLastGroup(decodeString)
	//直接返回字元串類型,方便外部調用
	return string(plainText), nil
}

// 明文數據填充
func paddingLastGroup(plainText []byte, blockSize int) []byte {
	//1.計算最後一個分組中明文後需要填充的位元組數
	padNum := blockSize - len(plainText)%blockSize
	//2.將位元組數轉換為byte類型
	char := []byte{byte(padNum)}
	//3.創建切片並初始化
	newPlain := bytes.Repeat(char, padNum)
	//4.將填充數據追加到原始數據後
	newText := append(plainText, newPlain...)
	return newText
}

// 去掉明文後面的填充數據
func unPaddingLastGroup(plainText []byte) []byte {
	//1.拿到切片中的最後一個位元組
	length := len(plainText)
	lastChar := plainText[length-1]
	//2.將最後一個數據轉換為整數
	number := int(lastChar)
	return plainText[:length-number]
}

func main() {
	//待加密的數據 模擬18位的身份證號
	plainText := "131229199907097219"
	//SM4加密
	decrypt, err := SM4Encrypt(plainText)
	if err != nil {
		return
	}
	fmt.Printf("sm4加密結果:%s\n", decrypt)
	//cipherString := hex.EncodeToString(cipherText)
	//fmt.Printf("sm4加密結果轉成字元串:%s\n", cipherString)

	//SM4解密
	sm4Decrypt, err := SM4Decrypt(decrypt)
	if err != nil {
		return
	}
	fmt.Printf("plainText:%s\n", sm4Decrypt)
	flag := plainText == sm4Decrypt
	fmt.Println("解密是否成功:", flag)
}

運行結果如下:

6. 核心方法的源碼解析

細心的小伙伴應該又發現,(或者通過你真實的敲代碼一定能發現。

在加密和解密部分有一個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方法用於加密或解密多個數據塊。src的長度必須是塊大小的倍數。dst和src必須完全重疊或完全不重疊。

如果len(dst) < len(src),CryptBlocks方法應該引發panic。允許傳遞比src更大的dst,此時CryptBlocks只會更新dst[:len(src)],不會觸及dst的其餘部分。

在這段代碼註釋中,dst表示目標緩衝區,用於存儲加密或解密後的結果。src表示源緩衝區,包含要加密或解密的數據。這兩個緩衝區可以是相同的記憶體區域,也可以是不同的記憶體區域。CryptBlocks方法會將src中的數據進行加密或解密,並將結果存儲在dst中。

需要註意的是,dst和src的長度必須是塊大小的倍數,否則CryptBlocks方法可能會引發panic。如果dst的長度小於src的長度,CryptBlocks方法只會更新dst的前len(src)個位元組,並不會修改dst的其餘部分。

此外,CryptBlocks方法可以多次調用,多次調用的效果相當於將所有src緩衝區的數據連接在一起,然後進行加密或解密。這意味著BlockMode會保持狀態,並且不會在每次CryptBlocks調用時重置。

如果你看註釋翻譯理解起來還是比較抽象的話,我換個方式介紹一下:

用我的話來說

在SM4加密中,CryptBlocks()方法是用於加密或解密多個數據塊的方法。它是SM4演算法中的一個核心函數。

具體來說,CryptBlocks()方法接受一個源數據緩衝區(src)和一個目標數據緩衝區(dst),並對源數據進行加密或解密操作,將結果存儲在目標數據緩衝區中。

在加密過程中,CryptBlocks()方法會將源數據分成多個數據塊,然後對每個數據塊進行加密操作,並將結果存儲在目標數據緩衝區中。加密過程中使用的密鑰和其他參數由SM4演算法的實現確定。

在解密過程中,CryptBlocks()方法會對源數據緩衝區中的數據塊進行解密操作,並將解密後的結果存儲在目標數據緩衝區中。

需要註意的是:CryptBlocks()方法要求源數據緩衝區和目標數據緩衝區的長度必須是SM4演算法的塊大小的倍數。否則,可能會引發錯誤或產生不可預測的結果。

CryptBlocks()方法是SM4加密演算法中用於加密或解密多個數據塊的關鍵方法,它實現了SM4演算法的核心功能。

7. 總結回顧

我之前也寫過一篇解密解密的文章,歡迎大家閱讀指教:保障網路請求數據傳輸的安全性、一致性和防篡改:對稱加密與非對稱加密的結合

相信你讀了這篇文章能對SM4加密有個整體理解,通過我在文章中提供的示例代碼可以快速跑通加密和解密流程。我還帶著你分析了CryptBlocks()源碼的作用。

歡迎大家收藏、點贊、轉發。你的關註,是我更文的最大動力!

歡迎關註 ❤

我的微信:wangzhongyang1993

視頻號:王中陽Go

公眾號:程式員升職加薪之旅


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 來源:blog.csdn.net/qq_35387940/article/details/129167329 ## **前言** 平時做一些統計數據,經常從資料庫或者是從介面獲取出來的數據,單位是跟業務需求不一致的。 - 比如, 我們拿出來的 分, 實際上要是元 - 又比如,我們拿到的數據需要 乘以 ...
  • # 1.文件路徑 我們發現不管是寫入還是寫出操作,我們提供的都是文件名,其實這裡準確說應該是文件路徑。當我們簡單把文件名傳遞給open函數時,Python將在當前執行程式的文件所在的目錄中查找文件名所代表的文件。 根據組織文件的方式,可能需要打開不在當前執行程式文件所屬目錄中的文件。如果此時我們把該 ...
  • ## 初級指針 本篇主要介紹:`指針和變數的關係`、指針類型、`指針的運算符`、空指針和野指針、`指針和數組`、`指針和字元串`、const 和指針、以及`gdb 調試段錯誤`。 ### 基礎概念 > 指針是一種特殊的變數。存放地址的變數就是指針。 `int num = 1;` 會申請4個位元組的記憶體 ...
  • 原文在[這裡](https://go.dev/blog/pgo-preview)。 > 原文發佈於2023年2月8日 在構建Go二進位文件時,Go編譯器會進行優化,以儘可能生成性能最佳的二進位文件。例如,常量傳播可以在編譯時對常量表達式進行求值,避免了運行時的計算開銷;逃逸分析可以避免對局部作用域對 ...
  • # 記錄一個在寫項目中遇到的Maven依賴無法導入的問題 項目是一個父項目做依賴管理,三個子項目,今天遇到一個問題: 子項目中導入的依賴,怎麼都導入不進去,maven倉庫中已經有了,idea提示也沒有問題,如圖: ![](https://img2023.cnblogs.com/blog/295824 ...
  • ## 教程簡介 Bootstrap入門教程 - 從基本到高級概念的簡單簡單步驟學習Bootstrap,其中包括概述,環境設置,基本結構,全局樣式,網格系統,流體網格系統,佈局,響應式設計,排版,表格,按鈕,圖像,圖標,下拉,按鈕組,導航元素,導航欄,麵包屑,分頁,標簽,徽章,版式,縮略圖,警報,進度 ...
  • ## 教程簡介 CouchDB 是一個開源的面向文檔的資料庫管理系統,可以通過 RESTful JavaScript Object Notation (JSON) API 訪問。術語 “Couch” 是 “Cluster Of Unreliable Commodity Hardware” 的首字母縮 ...
  • ## B+樹相關介紹 > B+樹是一棵**多叉排序樹**,即每個非葉子節點可以包含多個子節點,其整體結構呈扁平化,所以其非常適配於資料庫和操作系統的文件系統中。且B+樹能夠保持數據的穩定有序,插入和刪除都擁有較穩定的**對數時間複雜度**。 **B+樹的特性**:以 m 階為例,m 表示內部節點即非 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...