JavaScript逆向SM国密算法
JavaScript逆向SM国密算法
- 什么是SM国密
- SM国密算法分类
- SM2 - 非对称加密算法
- JavaScript实现
- Python 实现
- SM3 - 摘要算法
- JavaScript实现
- Python 实现
- SM4 - 对称加密算法
- JavaScript实现
- Python 实现
- 逆向技巧
- 案例
什么是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
npm install sm-crypto
// --- 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
pip install gmssl
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
npm install sm-crypto
// --- 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
pip install gmssl
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}
""")
# 预期输出: 164b71b213c8b67279e88afe5847b71f543a29b53141f53e023f79568b20d860print(f"""
原始消息: "{message2}"
SM3 哈希值: {hash_hex2}
""")
# 预期输出: f841f32a762c21958b493e83884214b62f43a4a159f893118021183187c337d1
SM4 - 对称加密算法
- 类型: 对称加密 - 分组密码
- 主要功能: 对大量数据进行高效的加密和解密。
- 分组长度: 128 位
- 密钥长度: 128 位
- 对标的国际算法: AES-128。SM4 的加密轮数、S盒设计等与 AES 不同,也是一个非常健壮的分组密码算法。它最初用于中国的无线局域网标准(WAPI)
JavaScript实现
需先安装sm-crypto
npm install sm-crypto
// --- 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
pip install gmssl
确保 pycryptodome 也已安装,以使用其方便的填充功能
pip install pycryptodome
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}")