JavaScript逆向SM国密算法

JavaScript逆向SM国密算法

什么是SM国密

SM 国密 指的是由中国国家密码管理局(State Cryptography Administration)发布的一系列国家商用密码标准

简单来说,"国密"是中国自主研发、拥有完全自主知识产权的一套密码算法体系

  • SM 是"商用密码"的拼音首字母缩写。
  • 国密 指的是国家密码标准。

这套体系的诞生,其核心战略目标是实现信息安全领域的自主可控,摆脱对国际通用加密算法(主要是由美国设计和标准化的算法,如 AES、RSA、SHA-2 等)的依赖,确保在中国境内的关键信息基础设施和重要领域的数据安全。

SM国密算法分类

SM2 - 非对称加密算法

  • 类型: 非对称加密 (公钥加密)

  • 技术基础: 椭圆曲线密码学 (ECC)

  • 主要功能:

    • 加密/解密: 类似于 RSA,用于保证数据机密性。
    • 数字签名: 类似于 RSA 和 ECDSA,用于身份认证和数据完整性校验。
    • 密钥交换: 类似于 ECDH,用于安全地协商会话密钥。
  • 对标的国际算法: RSA, ECDH, ECDSA。SM2 的主要优势是,与 RSA 相比,它能用更短的密钥长度达到同等的安全级别,计算效率更高。

JavaScript实现

需先安装sm-crypto

bash 复制代码
npm install sm-crypto
javascript 复制代码
// --- 1. 引入 sm-crypto 库 ---

// 在 Node.js 环境中:
const smCrypto = require('sm-crypto');
const { sm2 } = smCrypto;

// 在浏览器环境中,smCrypto 是一个全局变量,sm2 可以这样获取:
// const { sm2 } = smCrypto;


// --- 2. 生成 SM2 密钥对 ---
console.log("✅ 1. 正在生成 SM2 密钥对...");
const keyPair = sm2.generateKeyPairHex();
const publicKey = keyPair.publicKey;   // 获取十六进制格式的公钥
const privateKey = keyPair.privateKey; // 获取十六进制格式的私钥

console.log("公钥:", publicKey);
console.log("私钥:", privateKey);


// --- 3. 示例 1: 加密与解密 ---
console.log("\n--- 示例 1: 加密与解密 ---");

const messageToEncrypt = "这是一条需要SM2加密的机密信息。";
console.log("原始消息:", messageToEncrypt);

// 使用公钥进行加密
// 注意:加密结果默认会带一个 '04' 的前缀
// cipherMode: 1 表示 C1C3C2 模式,这是国密标准顺序
const encrypted = sm2.encrypt(messageToEncrypt, publicKey, {
    cipherMode: 1 
});
console.log("加密后的十六进制:", encrypted);

// 使用私钥进行解密
const decrypted = sm2.decrypt(encrypted, privateKey, {
    cipherMode: 1
});
console.log("解密后的消息:", decrypted);
console.log("验证成功:", messageToEncrypt === decrypted);


// --- 4. 示例 2: 签名与验证 ---
console.log("\n--- 示例 2: 签名与验证 ---");

const messageToSign = "这条消息需要签名以验证来源。";
console.log("待签名的消息:", messageToSign);

// 使用私钥对消息进行签名
const signature = sm2.doSignature(messageToSign, privateKey);
console.log("生成的签名 (十六进制):", signature);

// 使用公钥验证签名
const isVerified = sm2.doVerifySignature(messageToSign, signature, publicKey);
console.log("签名是否有效:", isVerified);

// 尝试用一个被篡改的消息进行验证
const tamperedMessage = "这条消息是伪造的!";
const isTamperedVerified = sm2.doVerifySignature(tamperedMessage, signature, publicKey);
console.log("篡改后消息的签名是否有效:", isTamperedVerified);

Python 实现

需先安装gmssl

bash 复制代码
pip install gmssl
python 复制代码
from gmssl import sm2, func

# 1. --- 生成 SM2 密钥对 ---
print("✅ 1. 正在生成 SM2 密钥对...")

# 创建 sm2.CryptSM2 实例来处理密钥和加解密
sm2_crypt = sm2.CryptSM2()

# sm2_crypt 实例在创建时会自动生成密钥对
private_key = sm2_crypt.private_key # 这是一个大整数
public_key = sm2_crypt.public_key   # 这是一个椭圆曲线上的点 (x, y)

# 为了方便展示和传输,我们通常使用十六进制格式
private_key_hex = hex(private_key)[2:]
public_key_hex = public_key.hex()

print(f"生成的私钥 (Hex): {private_key_hex}")
print(f"生成的公钥 (Hex): {public_key_hex}")


# --- 2. 示例 1: 加密与解密 ---
print("\n--- 示例 1: 加密与解密 ---")

message_to_encrypt = "这是一条需要SM2加密的机密信息。"
# 在 Python 中,加密操作处理的是字节 (bytes),所以需要编码
message_bytes = message_to_encrypt.encode('utf-8')
print(f"原始消息: '{message_to_encrypt}'")

# 使用公钥进行加密
encrypted = sm2_crypt.encrypt(message_bytes)
# 加密后的结果是字节,我们使用十六进制编码以便显示
encrypted_hex = encrypted.hex()
print(f"加密后的十六进制: {encrypted_hex}")

# 使用私钥进行解密
decrypted_bytes = sm2_crypt.decrypt(encrypted)
decrypted_message = decrypted_bytes.decode('utf-8')
print(f"解密后的消息: '{decrypted_message}'")
print(f"验证成功: {message_to_encrypt == decrypted_message}")


# --- 3. 示例 2: 签名与验证 ---
print("\n--- 示例 2: 签名与验证 ---")

message_to_sign = "这条消息需要签名以验证来源。"
message_bytes_to_sign = message_to_sign.encode('utf-8')

# SM2 签名标准中,通常需要一个用户ID (ID_A),如果没有可以是一个默认值
user_id = '1234567812345678'
print(f"待签名的消息: '{message_to_sign}'")

# 使用私钥对消息进行签名
# 注意:gmssl 的 sign 方法返回的是一个签名的字节表示
signature = sm2_crypt.sign(message_bytes_to_sign, user_id.encode('utf-8'))
signature_hex = signature.hex()
print(f"生成的签名 (十六进制): {signature_hex}")

# 使用公钥验证签名
# verify 方法返回一个布尔值
is_verified = sm2_crypt.verify(signature, message_bytes_to_sign, user_id.encode('utf-8'))
print(f"签名是否有效: {is_verified}")

# 尝试用一个被篡改的消息进行验证
tampered_message = b"This message is tampered!"
is_tampered_verified = sm2_crypt.verify(signature, tampered_message, user_id.encode('utf-8'))
print(f"篡改后消息的签名是否有效: {is_tampered_verified}")

SM3 - 摘要算法

  • 类型: 密码摘要算法 (哈希函数)
  • 主要功能: 生成数据的唯一"数字指纹",用于验证数据完整性、数字签名等。
  • 输出长度: 256 位
  • 对标的国际算法: SHA-256。SM3 的算法结构与 SHA-256 类似,但在设计细节上有所不同,目前被认为是安全的。

JavaScript实现

需先安装sm-crypto

bash 复制代码
npm install sm-crypto
javascript 复制代码
// --- 1. 引入 sm-crypto 库 ---

// 在 Node.js 环境中:
const smCrypto = require('sm-crypto');
const { sm3 } = smCrypto;

// 在浏览器环境中,smCrypto 是一个全局变量,sm3 可以这样获取:
// const { sm3 } = smCrypto;


// --- 2. 准备要计算哈希的消息 ---
const message1 = "1";
const message2 = "这是一条需要计算SM3哈希的消息。";
const message3 = "This is a message that needs to be hashed by SM3.";


// --- 3. 执行哈希计算 ---
console.log("✅ 正在执行 SM3 哈希计算...");

const hash1 = sm3(message1);
const hash2 = sm3(message2);
const hash3 = sm3(message3);

console.log(`
原始消息: "${message1}"
SM3 哈希值: ${hash1}
`);

console.log(`
原始消息: "${message2}"
SM3 哈希值: ${hash2}
`);

console.log(`
原始消息: "${message3}"
SM3 哈希值: ${hash3}
`);

// 演示雪崩效应:只修改一个字符
const message4 = "This is a message that needs to be hashed by SM4."; // 3 -> 4
const hash4 = sm3(message4);
console.log(`
修改后的消息: "${message4}"
SM3 哈希值: ${hash4}
`);

Python 实现

需先安装gmssl

bash 复制代码
pip install gmssl
python 复制代码
from gmssl import sm3

# --- 准备要计算哈希的消息 ---
message1 = "1"
message2 = "这是一条需要计算SM3哈希的消息。"

# --- 执行哈希计算 ---
print("✅ 正在执行 SM3 哈希计算 (函数式调用)...")

# 关键:必须将输入字符串编码为字节 (bytes)
message1_bytes = message1.encode('utf-8')
message2_bytes = message2.encode('utf-8')

# 调用 sm3_hash 函数
hash_bytes1 = sm3.sm3_hash(message1_bytes)
hash_bytes2 = sm3.sm3_hash(message2_bytes)

# 计算出的结果是字节类型,我们通常将其转换为十六进制字符串以便显示
hash_hex1 = hash_bytes1.hex()
hash_hex2 = hash_bytes2.hex()

print(f"""
原始消息: "{message1}"
SM3 哈希值: {hash_hex1}
""")
# 预期输出: 164b71b213c8b67279e88afe5847b71f543a29b53141f53e023f79568b20d860

print(f"""
原始消息: "{message2}"
SM3 哈希值: {hash_hex2}
""")
# 预期输出: f841f32a762c21958b493e83884214b62f43a4a159f893118021183187c337d1

SM4 - 对称加密算法

  • 类型: 对称加密 - 分组密码
  • 主要功能: 对大量数据进行高效的加密和解密。
  • 分组长度: 128 位
  • 密钥长度: 128 位
  • 对标的国际算法: AES-128。SM4 的加密轮数、S盒设计等与 AES 不同,也是一个非常健壮的分组密码算法。它最初用于中国的无线局域网标准(WAPI)

JavaScript实现

需先安装sm-crypto

bash 复制代码
npm install sm-crypto
javascript 复制代码
// --- 1. 引入 sm-crypto 库 ---

// 在 Node.js 环境中:
const smCrypto = require('sm-crypto');
const { sm4 } = smCrypto;

// 在浏览器环境中,smCrypto 是一个全局变量,sm4 可以这样获取:
// const { sm4 } = smCrypto;


// --- 2. 准备加密所需的参数 ---
const message = "这是一条需要使用SM4进行对称加密的机密信息。";

// 密钥 (Key):必须是 16 字节 (128位)。这里我们用一个 16 个字符的字符串,
// 然后转换为十六进制。在实际应用中,密钥应该是随机生成的。
const key = '0123456789abcdeffedcba9876543210'; // 16 字节的十六进制密钥

// 初始化向量 (IV):必须是 16 字节 (128位)。
const iv = '0123456789abcdeffedcba9876543210'; // 16 字节的十六进制 IV


// --- 3. 执行加密 ---
console.log("✅ 正在执行 SM4 加密 (CBC 模式)...");
console.log("原始消息:", message);

// 使用 sm4.encrypt(message, key, options)
const encrypted = sm4.encrypt(message, key, {
    iv: iv,
    mode: 'cbc', // 指定为 CBC 模式
    padding: 'pkcs7' // 默认填充方式
});

console.log("加密后的十六进制:", encrypted);


// --- 4. 执行解密 ---
console.log("\n✅ 正在执行 SM4 解密 (CBC 模式)...");

// 使用 sm4.decrypt(encryptedHex, key, options)
const decrypted = sm4.decrypt(encrypted, key, {
    iv: iv,
    mode: 'cbc',
    padding: 'pkcs7'
});

console.log("解密后的消息:", decrypted);
console.log("验证成功:", message === decrypted);

Python 实现

需先安装gmssl

bash 复制代码
pip install gmssl

确保 pycryptodome 也已安装,以使用其方便的填充功能

bash 复制代码
pip install pycryptodome
python 复制代码
from gmssl import sm4
from Crypto.Util.Padding import pad, unpad
import base64

# --- 1. 准备加密所需的参数 ---
message = "这是一条需要使用SM4进行对称加密的机密信息。"

# 密钥 (Key):必须是 16 字节的 bytes 类型
key = b'0123456789abcdef'

# 初始化向量 (IV):必须是 16 字节的 bytes 类型
iv = b'fedcba9876543210'

print("--- 加解密参数 ---")
print(f"原始消息: '{message}'")
print(f"使用的密钥: {key}")
print(f"使用的 IV: {iv}")
print("-----------------------------------------")


# --- 2. 加密函数 ---
def encrypt_sm4_cbc(plain_text, key, iv):
    """
    使用 SM4-CBC 模式加密文本
    """
    # 将明文消息编码为字节
    plain_text_bytes = plain_text.encode('utf-8')
    
    # 创建一个 SM4 加密器实例
    cipher = sm4.CryptSM4()
    cipher.set_key(key, sm4.SM4_ENCRYPT)
    
    # 对明文进行 PKCS7 填充,使其长度是 16 字节的整数倍
    padded_bytes = pad(plain_text_bytes, sm4.SM4_BLOCK_SIZE)
    
    # 执行 CBC 模式加密
    encrypted_bytes = cipher.crypt_cbc(iv, padded_bytes)
    
    # 将加密后的字节转换为 Base64 编码的字符串,以便传输
    return base64.b64encode(encrypted_bytes).decode('utf-8')

# --- 3. 解密函数 ---
def decrypt_sm4_cbc(encrypted_b64, key, iv):
    """
    使用 SM4-CBC 模式解密文本
    """
    # 将 Base64 字符串解码回字节
    encrypted_bytes = base64.b64decode(encrypted_b64)
    
    # 创建一个 SM4 解密器实例
    decipher = sm4.CryptSM4()
    decipher.set_key(key, sm4.SM4_DECRYPT)
    
    # 执行 CBC 模式解密
    decrypted_padded_bytes = decipher.crypt_cbc(iv, encrypted_bytes)
    
    # 去除填充,得到原始的字节数据
    unpadded_bytes = unpad(decrypted_padded_bytes, sm4.SM4_BLOCK_SIZE)
    
    # 将字节解码回我们可读的字符串
    return unpadded_bytes.decode('utf-8')


# --- 4. 执行并验证 ---
print("正在加密...")
encrypted_message = encrypt_sm4_cbc(message, key, iv)
print(f"加密后的 Base64 字符串: {encrypted_message}")
print("-----------------------------------------")

print("正在解密...")
decrypted_message = decrypt_sm4_cbc(encrypted_message, key, iv)
print(f"解密后的消息: '{decrypted_message}'")
print("-----------------------------------------")

print(f"验证解密是否成功: {message == decrypted_message}")

逆向技巧

案例

相关推荐
图灵信徒2 小时前
2025 ICPC Gran Premio de Mexico 3ra Fecha
c++·算法·codeforcrs
NeverSettle_2 小时前
2025前端网络相关知识深度解析
前端·javascript·http
大锦终2 小时前
【算法】栈专题
数据结构·c++·算法·leetcode
haogexiaole2 小时前
资源图分配算法
算法
堕落年代2 小时前
小红书JS SDK签名过程
开发语言·javascript·ecmascript
天选之女wow2 小时前
【代码随想录算法训练营——Day6(Day5周日休息)】哈希表——242.有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和
数据结构·算法·leetcode·散列表
寒冬没有雪2 小时前
利用归并算法对链表进行排序
c++·算法
CoovallyAIHub2 小时前
AI帮你打标签!这个开源神器让数据标注快了90%
深度学习·算法·计算机视觉
古译汉书2 小时前
蓝桥杯算法之基础知识(7)---排序题的快排和归并排序
算法