爬虫&逆向--Day20&Day21--JS逆向案例之国密算法【SM3、SM4】

一、JS逆向案例之国密算法【SM3、SM4】

1.1、SM3和SM4简介

一、SM3、SM4介绍 SM3和SM4分别是一种哈希算法和对称加密算法,它们都是中国国家密码管理局发布的密码算法,广泛应用于金融、政务、电信等领域。‌

1.1.1、SM3

SM3是一种哈希算法,主要用于生成数据的摘要信息。其特点包括:

‌安全性‌:SM3具有较高的安全性和抗碰撞性,能够抵抗各种攻击手段。 ‌设计思想‌:采用Merkle-Damgård结构,通过多轮处理将输入数据转换为固定长度的摘要信息。 ‌应用场景‌:适用于数字签名、消息完整性验证、数据完整性校验等场景。例如,在数字签名中,SM3用于生成消息的摘要;在区块链技术中,SM3用于哈希计算;在文件传输和存储中,用于数据完整性校验‌12。

对标:MD5、SHA125  SHA256  摘要算法

1.1.2、SM4

SM4是一种对称加密算法,主要用于数据加密和解密。其特点包括:

‌加密效率‌:SM4算法公开,既能在软件上运行,也能在硬件上实现,适应性强。 ‌加密模式‌:支持多种加密模式,如CBC、CFB、OFB等。 ‌应用场景‌:广泛应用于文件加密、数据传输加密等场景。例如,在中国银行业的IC卡应用中,采用SM4算法对敏感数据进行保护‌。 安全性对比,SM3和SM4的安全性都得到了保证:

  • ‌SM3‌:其安全性与SHA-256相当,具有较高的抗碰撞性和抗生日攻击能力。其输出长度为256位,比SHA-256的256位输出更加安全‌。

  • ‌SM4‌:其加密效率高,安全性也不逊色于国际标准的AES算法。128位的密钥长度和多种加密模式使其在数据加密中表现出色‌。

对标:AES、DES、CBC、ECB  加密算法

1.1.3、对别AES、RSA

国密算法是中国自主研发的密码算法标准,相比于传统的国际标准算法,如 AES、RSA 等,国密算法具有以下优点和缺点: 优点:

  1. 安全性:国密算法经过严格的安全性评估和密码学专家的审查,具有较高的安全性。它们采用了更长的密钥长度和更复杂的算法设计,以抵御现代密码攻击。

  2. 自主可控:国密算法是中国自主研发的,不依赖于国外的算法标准和技术,有助于保护国家信息安全和数据主权。

  3. 高效性:国密算法在硬件和软件实现上进行了优化,具有较高的加密和解密速度,适用于大规模数据和高性能计算场景。

  4. 适应性:国密算法涵盖了对称加密、非对称加密、散列函数和数字签名等多个密码学领域,可以满足各种安全需求。

缺点:

  1. 兼容性:国密算法与传统的国际标准算法存在不兼容的问题,导致在与国际系统和标准进行交互时可能存在一些困难。

  2. 成熟度:相对于传统的国际标准算法,国密算法的应用和生态系统相对较新,可能在实际应用中还需要进一步的验证和完善

  3. 开放性:国密算法的设计和实现细节相对闭源,对于研究者和开发者来说,了解和审计算法的难度较大。

  4. 需要注意的是,选择使用国密算法还是传统的国际标准算法应根据具体的应用场景和需求来决定。在保证安全性的前提下,要考虑到兼容性、生态系统支持以及算法的成熟度和广泛应用程度等因素。

1.2 、gmssl模块

gmssl 是一个用于处理国密算法的 Python 模块,它提供了对国密算法的支持,包括对称加密、非对称加密、散列函数和数字签名等,仅列出了其中两个较为完善的第三方库,需要注意的是,SM1 和 SM7 算法不公开,目前大多库仅实现了 SM2、SM3、SM4 三种密算法。

需要导入gmssl库  pip install gmssl

1.2.1、案例一:SM3 哈希算法

SM3 是一种密码学哈希函数,输出长度为 256 位(32 字节)。它常用于数字签名、消息认证码等场景。

from gmssl import sm3, func


def sm3_hash_demo():
    """
    SM3 哈希算法示例
    """
    # 示例消息(可以是字符串或字节)
    message = "Hello, GMSSL! 这是需要计算SM3哈希值的消息。"

    # 将字符串转换为字节串(如果输入是字节串则无需转换)
    data = message.encode('utf-8')
    print("字节串::::",data)

    # 创建 SM3 哈希对象
    print("func.bytes_to_list(data):::", func.bytes_to_list(data))  # 区别1:多了一个转数组的动作 字节-->数组
    hash_obj = sm3.sm3_hash(func.bytes_to_list(data))               # 在针对数据进行实现sm3加密

    # 输出结果
    print("原始消息:", message)
    print("SM3 哈希值:", hash_obj)
    # 验证:相同消息应产生相同哈希
    same_hash = sm3.sm3_hash(func.bytes_to_list(data))
    print("验证一致性:", hash_obj == same_hash)

    # 测试雪崩效应(微小变化导致巨大差异)
    if len(data) > 0:
        modified_data = bytearray(data)
        modified_data[0] ^= 0x01  # 修改第一个字节的一位
        modified_hash = sm3.sm3_hash(func.bytes_to_list(modified_data))
        print("修改后的哈希:", modified_hash)
        print("哈希是否变化:", hash_obj != modified_hash)


if __name__ == "__main__":
    sm3_hash_demo()



"""

字节串:::: b'Hello, GMSSL! \xe8\xbf\x99\xe6\x98\xaf\xe9\x9c\x80\xe8\xa6\x81\xe8\xae\xa1\xe7\xae\x97SM3\xe5\x93\x88\xe5\xb8\x8c\xe5\x80\xbc\xe7\x9a\x84\xe6\xb6\x88\xe6\x81\xaf\xe3\x80\x82'
func.bytes_to_list(data)::: [72, 101, 108, 108, 111, 44, 32, 71, 77, 83, 83, 76, 33, 32, 232, 191, 153, 230, 152, 175, 233, 156, 128, 232, 166, 129, 232, 174, 161, 231, 174, 151, 83, 77, 51, 229, 147, 136, 229, 184, 140, 229, 128, 188, 231, 154, 132, 230, 182, 136, 230, 129, 175, 227, 128, 130]
原始消息: Hello, GMSSL! 这是需要计算SM3哈希值的消息。
SM3 哈希值: 2eb152d3a892898d74ea4dfef93bde584ee51b0fd4c2d009b1deb7ab70a86da2
验证一致性: True

"""

 

1.2.2、案例二:SM4 对称加密

SM4 是一种分组密码算法,密钥和分组长度均为 128 位。支持 ECB、CBC 等多种模式。

from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
import os


def sm4_ecb_demo():
    """
    SM4 ECB 模式加密解密示例   有key没iv
    """
    # 密钥(16字节 = 128位)       生成随机的16位字符串
    key = os.urandom(16)
    # 密钥的十六进制表示,用于显示  把随机的16位字符串转成16进制
    key_hex = key.hex()
    print(f"生成随机密钥: {key_hex}")

    # 创建 SM4 对象
    crypt_sm4 = CryptSM4()

    # 设置密钥 加密密钥   有key没iv  加密SM4_ENCRYPT  解密SM4_DECRYPT
    crypt_sm4.set_key(key, SM4_ENCRYPT)

    # 明文(需要是16字节的倍数,对于非倍数长度需要填充)
    plaintext = "Hello, SM4 ECB Mode! 测试中文".encode('utf-8')
    print(f"原始明文: {plaintext.decode('utf-8')}")  # 原文字节串

    # 加密
    ciphertext = crypt_sm4.crypt_ecb(plaintext)
    print(f"加密结果: {ciphertext.hex()}")

    # 解密
    crypt_sm4.set_key(key, SM4_DECRYPT)
    decrypted_data = crypt_sm4.crypt_ecb(ciphertext)
    print(f"解密结果: {decrypted_data.decode('utf-8')}")

    """
    加密还是解密都是调用crypt_ecb,至于是加密还是解密取决于
    crypt_sm4.set_key(key, SM4_DECRYPT) 中的第二个参数
    加密SM4_ENCRYPT  解密SM4_DECRYPT
    """


def sm4_cbc_demo():
    """
    SM4 CBC 模式加密解密示例   有key有iv  需要用相同的key和iv进行加密和解密
    """
    # 密钥(16字节)
    key = os.urandom(16)
    # 初始化向量(16字节)
    iv = os.urandom(16)

    print(f"密钥: {key.hex()}")
    print(f"IV: {iv.hex()}")

    # 创建 SM4 对象
    crypt_sm4 = CryptSM4()

    # 设置加密密钥
    crypt_sm4.set_key(key, SM4_ENCRYPT)

    # 明文
    plaintext = "Hello, SM4 CBC Mode! 测试中文".encode('utf-8')
    print(f"原始明文: {plaintext.decode('utf-8')}")

    # 加密  key之前设置好了,iv需要单独和明文一起传递
    ciphertext = crypt_sm4.crypt_cbc(iv, plaintext)
    print(f"加密结果: {ciphertext.hex()}")

    # 设置解密密钥(使用相同密钥)
    crypt_sm4.set_key(key, SM4_DECRYPT)

    # 解密  解密需要和加密一样的key和iv,是对称加密,所以iv和key都是一样的
    decrypted_data = crypt_sm4.crypt_cbc(iv, ciphertext)
    print(f"解密结果: {decrypted_data.decode('utf-8')}")


if __name__ == "__main__":
    print("=" * 50)
    print("SM4 ECB 模式示例")   # 有key没iv
    print("=" * 50)
    sm4_ecb_demo()

    print("\n" + "=" * 50)
    print("SM4 CBC 模式示例")   # 有key 有iv
    print("=" * 50)
    sm4_cbc_demo()



"""
备注:
    CryptSM4,       拿这个算法
    SM4_ENCRYPT,    确定加密
    SM4_DECRYPT     确定解密

结果:
    ==================================================
    SM4 ECB 模式示例
    ==================================================
    生成随机密钥: 614cec969d7e270faba4c50eb4fb3818
    原始明文: Hello, SM4 ECB Mode! 测试中文
    加密结果: f3f69acc8d666dd889d613061fd534a6cbecf3e6d60ca3fca048458fd66533cd05b2c743c8574d87a7bdf0d7d222834d
    解密结果: Hello, SM4 ECB Mode! 测试中文
    
    
    ==================================================
    SM4 CBC 模式示例
    ==================================================
    密钥: 17b4e2d99267eef27588e248477cf815
    IV: a88d7b246e9837773124e7136c0b0475
    原始明文: Hello, SM4 CBC Mode! 测试中文
    加密结果: d403e2a623d296b30958f4c6c77d5bf2ed2004351c32e23d2db280e33b73f3bf0bf3f51ea6a5bf9a4375913c148d712e
    解密结果: Hello, SM4 CBC Mode! 测试中文

"""

 

代码说明

  1. SM3 示例

    • 展示了如何计算消息的 SM3 哈希值

    • 验证了哈希函数的一致性(相同输入产生相同输出)

    • 演示了雪崩效应(微小输入变化导致巨大输出变化)

  2. SM4 示例

    • ECB 模式:简单分组加密模式,相同明文生成相同密文

    • CBC 模式:链式加密模式,更安全,相同明文生成不同密文

    • 包含密钥生成、加密和解密完整流程

    • 支持中文字符处理

注意事项

  1. 在实际应用中,密钥需要安全存储和管理,不应使用随机生成且不保存的方式

  2. 对于长文本或非对齐数据,需要添加适当的填充机制(如 PKCS7)

  3. CBC 模式需要确保 IV 的唯一性和随机性

  4. 生产环境应考虑使用更安全的密钥派生和存储方案

 1.3、案例

案例地址链接:https://h.xinhuaxmt.com/vh512/share/11910830?d=134d762

案例爬取链接:https://h.xinhuaxmt.com/1017/n/newsapi/h5/news-detail/newscomment

1.3.1、入口定位

拿到网站以后,首先我们需要先进行检查,看那些是我们需要关注破解的内容,那是无需关注的

经过对比查看得知需要破解的内容只有请求头中的Signature 是需要破解的

image

 这个时候我们通过复制爬取的目标链接,到【https://curlconverter.com/】生成基础爬虫代码

确定数据请求和请求头中的Signature 有关系

image

 然后我们通过key关键字搜索【Signature】

image

1.3.2、代码分析

在控制台打印b

Key=4bb7c7298e0778524f45f240d922d85b5bbc525c313a2f011148273f4ccbd186&Timestamp=1756519772965&Token=&Request=docid=11910830&share=1'

所以b = "Key=" + o + "&Timestamp=" + d + "&Token=" + c + "&Request=" + m

image

 备注:知识点1:是否需要进行序列化

"""
timer = str(int(time.time() * 1000))  # 构建一个13位的时间戳
print("time.time():::", time.time())                            # 1756521316.7621424
print("time.time() * 1000:::", time.time() * 1000)              # 1756521316763.1394 
print("int(time.time() * 1000):::", int(time.time() * 1000))    # 1756521316763



print("b::::", b)
# b:::: Key=【省略若干字符】{"docid": "11910830", "doctype": 0, "loadtype": 0, "lastcommid": 0, "pageSize": 20}
print("m::::", m)  # 进行json.dumps(json_data)  序列化以后的操作
# m:::: {"docid": "11910830", "doctype": 0, "loadtype": 0, "lastcommid": 0, "pageSize": 20}
print("json_data::::", json_data)   # 正常的参数打印
# json_data:::: {'docid': '11910830', 'doctype': 0, 'loadtype': 0, 'lastcommid': 0, 'pageSize': 20}

"""

 

image

 

1.3.3、扣JS--不涉及

用Python纯算实现,不涉及扣JS代码

1.3.4、补充依赖【】--不涉及

用Python纯算实现,不涉及扣JS代码

1.3.5、代码文件:新华社逆向解析.py

import json
from gmssl import sm3, func
import requests
import time

timer = str(int(time.time() * 1000))  # 构建一个13位的时间戳

cookies = {
    'acw_tc': '24839e9917565180911036296e00e60fc8faa298ccd32e030278e3f0f7',
    'nanoId': 'T1YOZJqKAIPIOCrLKdeXc-dLpSqZF8k6',
    'Hm_lvt_12aae9df6cc49c807cea2ed5c9ec8e7e': '1756518093',
    'HMACCOUNT': '598C51D7F5ECFF7C',
    'Hm_lpvt_12aae9df6cc49c807cea2ed5c9ec8e7e': '1756518251',
}

headers = {
    'Accept': 'application/json, text/plain, */*',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Content-Type': 'application/json;charset=UTF-8',
    # 'Cookie': 'acw_tc=24839e9917565180911036296e00e60fc8faa298ccd32e030278e3f0f7; nanoId=T1YOZJqKAIPIOCrLKdeXc-dLpSqZF8k6; Hm_lvt_12aae9df6cc49c807cea2ed5c9ec8e7e=1756518093; HMACCOUNT=598C51D7F5ECFF7C; Hm_lpvt_12aae9df6cc49c807cea2ed5c9ec8e7e=1756518251',
    'Device-Access-Id': '',
    'Origin': 'https://h.xinhuaxmt.com',
    'Referer': 'https://h.xinhuaxmt.com/vh512/share/11910830?d=134d762',
    'Sec-Fetch-Dest': 'empty',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Site': 'same-origin',
    # 'Signature': 'fe67064cc3baa3ab4e26fed846dae58e30759197654d9d6a935f6434b8e90c9e',
    'Timestamp': timer,
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
    'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
}

json_data = {
    'docid': '11910830',
    'doctype': 0,
    'loadtype': 0,
    'lastcommid': 0,
    'pageSize': 20,
}

"""
Key=4bb7c7298e0778524f45f240d922d85b5bbc525c313a2f011148273f4ccbd186
&Timestamp=1756519772965
&Token=
&Request=docid=11910830&share=1'

所以b = "Key=" + o + "&Timestamp=" + d + "&Token=" + c + "&Request=" + m
所以需要构建sm3的参数
"""
o = "4bb7c7298e0778524f45f240d922d85b5bbc525c313a2f011148273f4ccbd186"
d = timer  # 构建13位的时间戳
c = ""
# m = '{"docid":"11910830","doctype":0,"loadtype":0,"lastcommid":0,"pageSize":20}'  不进行序列化操作,也可以这样直接用
m = json.dumps(json_data)  # 键值对都是双引号,所以m是一个json字符串,所以我们需要进行序列化操作
b = "Key=" + o + "&Timestamp=" + d + "&Token=" + c + "&Request=" + m
print("b::::", b)

sign = sm3.sm3_hash(func.bytes_to_list(b.encode()))   # 数据是b,先转字节,在转数组,在加密
headers["Signature"] = sign

url = 'https://h.xinhuaxmt.com/1017/n/newsapi/h5/news-detail/newscomment'
response = requests.post(url=url, cookies=cookies, headers=headers, json=json_data)
print(response.text)


"""
timer = str(int(time.time() * 1000))  # 构建一个13位的时间戳
print("time.time():::", time.time())                            # 1756521316.7621424
print("time.time() * 1000:::", time.time() * 1000)              # 1756521316763.1394 
print("int(time.time() * 1000):::", int(time.time() * 1000))    # 1756521316763



print("b::::", b)
# b:::: Key=【省略若干字符】={"docid": "11910830", "doctype": 0, "loadtype": 0, "lastcommid": 0, "pageSize": 20}
print("m::::", m)  # 进行json.dumps(json_data)  序列化以后的操作
# m:::: {"docid": "11910830", "doctype": 0, "loadtype": 0, "lastcommid": 0, "pageSize": 20}
print("json_data::::", json_data)   # 正常的参数打印
# json_data:::: {'docid': '11910830', 'doctype': 0, 'loadtype': 0, 'lastcommid': 0, 'pageSize': 20}

"""

 

posted @ 2025-09-09 19:31  L遇上J  阅读(51)  评论(0)    收藏  举报