爬蟲逆向基礎,認識 SM1-SM9、ZUC 國密演算法

K哥爬蟲發表於2021-11-11

關注微信公眾號:K哥爬蟲,QQ交流群:808574309,持續分享爬蟲進階、JS/安卓逆向等技術乾貨!

【01x00】 簡介

國密即國家密碼局認定的國產加密演算法,爬蟲工程師在做 JS 逆向的時候,會遇到各種各樣的加密演算法,其中 RSA、AES、SHA 等演算法是最常見的,這些演算法都是國外的,在 K 哥以前的文章裡也有介紹:《【爬蟲知識】爬蟲常見加密解密演算法》

事實上從 2010 年開始,我國國家密碼管理局就已經開始陸續釋出了一系列國產加密演算法,這其中就包括 SM1、SM2、SM3 、SM4、SM7、SM9、ZUC(祖沖之加密演算法)等,SM 代表商密,即商業密碼,是指用於商業的、不涉及國家祕密的密碼技術。SM1 和 SM7 的演算法不公開,其餘演算法都已成為 ISO/IEC 國際標準。

在這些國產加密演算法中,SM2、SM3、SM4 三種加密演算法是比較常見的,在爬取部分 gov 網站時,也可能會遇到這些演算法,所以作為爬蟲工程師是有必要了解一下這些演算法的,如下圖所示某 gov 網站就使用了 SM2 和 SM4 加密演算法:

01.png

【02x00】演算法概述

演算法名稱演算法類別應用領域特點
SM1對稱(分組)加密演算法晶片分組長度、金鑰長度均為 128 位元
SM2非對稱(基於橢圓曲線 ECC)加密演算法資料加密ECC 橢圓曲線密碼機制 256 位,相比 RSA 處理速度快,消耗更少
SM3雜湊(hash)函式演算法完整性校驗安全性及效率與 SHA-256 相當,壓縮函式更復雜
SM4對稱(分組)加密演算法資料加密和區域網產品分組長度、金鑰長度均為 128 位元,計算輪數多
SM7對稱(分組)加密演算法非接觸式 IC 卡分組長度、金鑰長度均為 128 位元
SM9標識加密演算法(IBE)端對端離線安全通訊加密強度等同於 3072 位金鑰的 RSA 加密演算法
ZUC對稱(序列)加密演算法行動通訊 4G 網路流密碼

【03x00】演算法詳解

【03x01】SM1 分組加密演算法

SM1 為分組加密演算法,對稱加密,分組長度和金鑰長度都為 128 位,故對訊息進行加解密時,若訊息長度過長,需要進行分組,要訊息長度不足,則要進行填充。演算法安全保密強度及相關軟硬體實現效能與 AES 相當,該演算法不公開,僅以 IP 核的形式存在於晶片中,呼叫該演算法時,需要通過加密晶片的介面進行呼叫,採用該演算法已經研製了系列晶片、智慧 IC 卡、智慧密碼鑰匙、加密卡、加密機等安全產品,廣泛應用於電子政務、電子商務及國民經濟的各個應用領域(包括國家政務通、警務通等重要領域),一般瞭解的人比較少,爬蟲工程師也不會遇到這種加密演算法。

【03x02】SM2 橢圓曲線公鑰加密演算法

SM2 為橢圓曲線(ECC)公鑰加密演算法,非對稱加密,SM2 演算法和 RSA 演算法都是公鑰加密演算法,SM2 演算法是一種更先進安全的演算法,在我們國家商用密碼體系中被用來替換 RSA 演算法,在不少 gov 網站會見到此類加密演算法。我國學者對橢圓曲線密碼的研究從 20 世紀 80 年代開始,目前已取得不少成果,SM2 橢圓曲線公鑰密碼演算法比 RSA 演算法有以下優勢:

SM2RSA
安全性256 位 SM2 強度已超過 RSA-2048一般
演算法結構基本橢圓曲線(ECC)基於特殊的可逆模冪運算
計算複雜度完全指數級亞指數級
儲存空間(金鑰長度)192-256 bit2048-4096 bit
祕鑰生成速度較 RSA 演算法快百倍以上
解密加密速度較快一般

【03x03】SM3 雜湊演算法

SM3 為密碼雜湊演算法,採用密碼雜湊(hash)函式標準,用於替代 MD5/SHA-1/SHA-2 等國際演算法,是在 SHA-256 基礎上改進實現的一種演算法,訊息分組長度為 512 位,摘要值長度為 256 位,其中使用了異或、模、模加、移位、與、或、非運算,由填充、迭代過程、訊息擴充套件和壓縮函式所構成。在商用密碼體系中,SM3 主要用於數字簽名及驗證、訊息認證碼生成及驗證、隨機數生成等。據國家密碼管理局表示,其安全性及效率要高於 MD5 演算法和 SHA-1 演算法,與 SHA-256 相當。

【03x04】SM4 分組加密演算法

SM4 為無線區域網標準的分組加密演算法,對稱加密,用於替代 DES/AES 等國際演算法,SM4 演算法與 AES 演算法具有相同的金鑰長度和分組長度,均為 128 位,故對訊息進行加解密時,若訊息長度過長,需要進行分組,要訊息長度不足,則要進行填充。加密演算法與金鑰擴充套件演算法都採用 32 輪非線性迭代結構,解密演算法與加密演算法的結構相同,只是輪金鑰的使用順序相反,解密輪金鑰是加密輪金鑰的逆序。

SM4DESAES
計算輪數3216(3DES 為 16*3)10/12/14
密碼部件S 盒、非線性變換、線性變換、合成變換標準算術和邏輯運算、先替換後置換,不含線性變換S 盒、行移位變換、列混合變換、圈金鑰加變換(AddRoundKey)

【03x05】SM7 分組加密演算法

SM7 為分組加密演算法,對稱加密,該演算法不公開,應用包括身份識別類應用(非接觸式 IC 卡、門禁卡、工作證、參賽證等),票務類應用(大型賽事門票、展會門票等),支付與通卡類應用(積分消費卡、校園一卡通、企業一卡通等)。爬蟲工程師基本上不會遇到此類演算法。

【03x06】SM9 標識加密演算法

SM9 為標識加密演算法(Identity-Based Cryptography),非對稱加密,標識加密將使用者的標識(如微訊號、郵件地址、手機號碼、QQ 號等)作為公鑰,省略了交換數字證照和公鑰過程,使得安全系統變得易於部署和管理,適用於網際網路應用的各種新興應用的安全保障,如基於雲技術的密碼服務、電子郵件安全、智慧終端保護、物聯網安全、雲端儲存安全等等。這些安全應用可採用手機號碼或郵件地址作為公鑰,實現資料加密、身份認證、通話加密、通道加密等。在商用密碼體系中,SM9 主要用於使用者的身份認證,據新華網公開報導,SM9 的加密強度等同於 3072 位金鑰的 RSA 加密演算法。

【03x07】ZUC 祖沖之演算法

ZUC 為流密碼演算法,對稱加密,該機密性演算法可適用於 3GPP LTE 通訊中的加密和解密,該演算法包括祖沖之演算法(ZUC)、機密性演算法(128-EEA3)和完整性演算法(128-EIA3)三個部分。已經被國際組織 3GPP 推薦為 4G 無線通訊的第三套國際加密和完整性標準的候選演算法。

【04x00】程式語言實現

【04x01】Python 語言實現

在 Python 裡面並沒有比較官方的庫來實現國密演算法,這裡僅列出了其中兩個較為完善的第三方庫,需要注意的是,SM1 和 SM7 演算法不公開,目前大多庫僅實現了 SM2、SM3、SM4 三種密演算法。

其中 gmssl-python 是 gmssl 的改進版,gmssl-python 新增支援了 SM9 演算法,不過截止本文編寫時,gmssl-python 並未釋出 pypi,也未 PR 到 gmssl,使用 pip install gmssl 安裝的 gmssl 不支援 SM9 演算法。若要使用 SM9 演算法,可下載 gmssl-python 原始碼手動安裝。

以 gmssl 的 SM2 演算法為例,實現如下(其他演算法和詳細用法可參考其官方文件):

SM2 加密(encrypt)和解密(decrypt):

from gmssl import sm2


# 16 進位制的公鑰和私鑰
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)

# 待加密資料和加密後資料為 bytes 型別
data = b"this is the data to be encrypted"
enc_data = sm2_crypt.encrypt(data)
dec_data = sm2_crypt.decrypt(enc_data)

print('enc_data: ', enc_data.hex())
print('dec_data: ', dec_data)

# enc_data:  3cb96dd2e0b6c24df8e22a5da3951d061a6ee6ce99f46a446426feca83e501073288b1553ca8d91fad79054e26696a27c982492466dafb5ed06a573fb09947f2aed8dfae243b095ab88115c584bb6f0814efe2f338a00de42b244c99698e81c7913c1d82b7609557677a36681dd10b646229350ad0261b51ca5ed6030d660947

# dec_data:  b'this is the data to be encrypted'

SM2 簽名(sign)和校驗(verify):

from gmssl import sm2, func


# 16 進位制的公鑰和私鑰
private_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'
public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'
sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)

# 待簽名資料為 bytes 型別
data = b"this is the data to be signed"
random_hex_str = func.random_hex(sm2_crypt.para_len)

#  16 進位制
sign = sm2_crypt.sign(data, random_hex_str)
verify = sm2_crypt.verify(sign, data)

print('sign: ', sign)
print('verify: ', verify)

# sign:  45cfe5306b1a87cf5d0034ef6712babdd1d98547e75bcf89a17f3bcb617150a3f111ab05597601bab8c41e2b980754b74ebe9a169a59db37d549569910ae273a

# verify:  True

【04x02】JavaScript 語言實現

在 JavaScript 中已有比較成熟的實現庫,這裡推薦 sm-crypto,目前支援 SM2、SM3 和 SM4,需要注意的是,SM2 非對稱加密的結果由 C1、C2、C3 三部分組成,其中 C1 是生成隨機數的計算出的橢圓曲線點,C2 是密文資料,C3 是 SM3 的摘要值,最開始的國密標準的結果是按 C1C2C3 順序的,新標準的是按 C1C3C2 順序存放的,sm-crypto 支援設定 cipherMode,也就是 C1C2C3 的排列順序。

sm-crypto:https://www.npmjs.com/package...

以 SM2 演算法為例,實現如下(其他演算法和詳細用法可參考其官方文件):

SM2 加密(encrypt)和解密(decrypt):

const sm2 = require('sm-crypto').sm2

// 1 - C1C3C2,0 - C1C2C3,預設為1
const cipherMode = 1

// 獲取金鑰對
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey   // 公鑰
let privateKey = keypair.privateKey // 私鑰

let msgString = "this is the data to be encrypted"
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode)    // 加密結果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密結果

console.log("encryptData: ", encryptData)
console.log("decryptData: ", decryptData)

// encryptData:  ddf261103fae06d0efe20ea0fe0d82bcc170e8efd8eeae24e9559b3835993f0ed2acb8ba6782fc21941ee74ca453d77664a5cb7dbb91517e6a3b0c27db7ce587ae7af54f8df48d7fa822b7062e2af66c112aa57de94d12ba28e5ba96bf4439d299b41da4a5282d054696adc64156d248049d1eb1d0af28d76b542fe8a95d427e

// decryptData:  this is the data to be encrypted

SM2 簽名(sign)和校驗(verify):

const sm2 = require('sm-crypto').sm2

// 獲取金鑰對
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey   // 公鑰
let privateKey = keypair.privateKey // 私鑰

// 純簽名 + 生成橢圓曲線點
let msgString = "this is the data to be signed"
let sigValueHex = sm2.doSignature(msgString, privateKey)                    // 簽名
let verifyResult = sm2.doVerifySignature(msgString, sigValueHex, publicKey) // 驗簽結果

console.log("sigValueHex: ", sigValueHex)
console.log("verifyResult: ", verifyResult)

// sigValueHex:  924cbb9f2b5adb554ef77129ff1e3a00b2da42017ad3ec2f806d824a77646987ba8c8c4fb94576c38bc11ae69cc98ebbb40b5d47715171ec7dcea913dfc6ccc1

// verifyResult:  true

【04x03】其他語言實現以及參考資料

【05x00】附:GM/T 密碼行業標準

相關文章