GMSSL 库应用:国密算法的 ThinkPHP8 与 Python3、Node.js 脚本开发

国密SM2算法介绍以及使用

国密算法简介

国密算法是国家商用密码算法的简称。自2012年以来,国家密码管理局以《中华人民共和国密码行业标准》的方式,陆续公布了SM2/SM3/SM4等密码算法标准及其应用规范。其中“SM”代表“商密”,即用于商用的、不涉及国家秘密的密码技术。

SM2、SM3、SM4是国密算法体系中三类不同用途的密码算法,由于设计目标和应用场景不同,其安全性的核心考量维度、安全基础及潜在威胁存在显著差异。以下从安全性角度对比三者的核心区别:

一、算法类型与安全基础不同

三者的本质差异源于算法类型,而不同类型的密码算法,其安全性的数学基础完全不同:

算法 类型 核心安全基础 核心功能
SM2 非对称加密算法(椭圆曲线密码,ECC) 基于椭圆曲线离散对数问题(ECDLP):在特定椭圆曲线上,已知基点 ( G ) 和点 ( kG ),求解整数 ( k ) 是计算困难的 数字签名、密钥交换、公钥加密(如身份认证、敏感信息加密)
SM3 密码哈希算法 基于抗碰撞性:难以找到两个不同的输入,使其哈希结果相同(抗碰撞);难以从哈希结果反推原始输入(抗原像) 数据完整性校验、数字签名的摘要生成(如文件校验、签名验证)
SM4 对称分组密码算法 基于混淆与扩散原理:通过复杂的轮函数变换,使明文、密钥与密文之间的关系足够复杂,难以通过密文推导明文或密钥 数据加密(如存储加密、传输加密)

二、安全强度的量化指标不同

由于算法类型不同,安全强度的衡量方式和量化标准也不同:

  • SM2:安全强度由椭圆曲线参数决定,推荐的 SM2P256V1 曲线提供256位安全强度,相当于 3072 位 RSA 的安全级别(远高于 1024 位 RSA,后者已被认为不安全)。其安全强度主要体现在“求解椭圆曲线离散对数”的计算复杂度上。

  • SM3:输出长度为 256 位,提供256位哈希安全强度,与 SHA-256 相当。其安全强度核心是“抗碰撞能力”——目前尚未发现有效的碰撞攻击(即找不到两个不同输入得到相同哈希值)。

  • SM4:分组长度和密钥长度均为 128 位,提供128位对称安全强度,与 AES-128 相当。其安全强度体现在“密钥破解难度”上——暴力破解 128 位密钥需要 ( 2^{128} ) 次尝试,当前计算能力无法实现。

三、潜在安全威胁与攻击面不同

三者面临的主要安全威胁因算法特性而异:

  • SM2 的威胁

    • 核心风险并非算法本身,而是实现缺陷:如随机数生成器不安全(导致私钥可预测)、椭圆曲线参数被篡改(引入后门)、签名/加密流程不规范(如重复使用随机数)。
    • 长期威胁可能来自量子计算:虽然目前量子算法对 ECDLP 的攻击仍需亚指数时间(慢于对 RSA 的多项式时间攻击),但未来强量子计算机可能削弱其安全性。
  • SM3 的威胁

    • 核心风险是密码分析攻击:若未来发现高效的碰撞攻击算法(类似 SHA-1 被破解的方式),可能导致其失去完整性校验能力。
    • 误用风险:如将哈希值直接作为加密密钥(哈希算法不具备密钥拉伸能力)、未加盐存储哈希值(易受彩虹表攻击)。
  • SM4 的威胁

    • 核心风险是密钥管理漏洞:对称算法的安全性完全依赖密钥保密性,一旦密钥泄露,所有加密数据将被破解;此外,密钥重复使用、密钥分发不安全也会导致风险。
    • 模式误用风险:如在 ECB 模式下加密重复明文(会暴露明文规律)、CBC 模式未使用随机 IV(初始化向量)等。

四、安全性的“不可比性”:场景决定价值

三者的安全性无法直接比较“强弱”,因为它们服务于不同场景:

  • SM2 解决“非对称场景”的安全问题(如无预先共享密钥的加密、身份认证),其价值在于用短密钥实现高安全强度;
  • SM3 解决“完整性校验”问题,其价值在于高效生成不可逆的摘要;
  • SM4 解决“对称加密”问题,其价值在于高效加密大量数据(速度远快于非对称算法)。

总结:三者的安全性差异源于设计目标——SM2 依赖椭圆曲线难题,SM3 依赖抗碰撞性,SM4 依赖密钥保密性,它们共同构成了国密算法的安全体系,分别保障不同环节的安全需求。

SM2算法的安全性分析

SM2作为中国国家密码管理局颁布的椭圆曲线公钥密码算法(属于非对称加密算法),目前在安全性上仍然足够可靠,主要基于以下几点:

1. 算法设计的安全性基础稳固

SM2基于椭圆曲线离散对数问题(ECDLP),其安全性依赖于在特定椭圆曲线上求解离散对数的计算复杂度。目前,SM2推荐使用的椭圆曲线参数(如SM2P256V1)经过严格的密码学分析,尚未发现有效的多项式时间求解算法。

从数学原理上,其安全强度与256位椭圆曲线加密(ECC)相当,相当于3072位RSA加密的安全级别,远高于目前主流的1024位RSA(已被认为不安全),也满足当前大部分场景的安全需求。

2. 尚未出现有效的攻击方法

截至目前,无论是学术研究还是实际攻击案例中,都没有公开的、能有效破解SM2的方法。与RSA等传统非对称算法相比,椭圆曲线密码(包括SM2)在相同安全强度下的密钥更短(256位vs 3072位),但抗攻击能力更强。

近年来,量子计算对传统密码算法的威胁受到关注,但SM2与其他ECC算法一样,对量子计算的抵抗能力优于RSA(量子算法可在多项式时间内破解RSA,但对ECC的攻击仍需亚指数时间)。即使未来量子计算实用化,SM2也比RSA有更长的”安全窗口期”。

3. 广泛的标准化与应用验证

SM2是中国国家标准(GB/T 32918)和国家密码行业标准(GMT 0003)规定的算法,已在金融、政务、通信等关键领域大规模应用(如银行加密传输、电子签名、身份认证等)。

其设计和安全性经过了密码学界和工业界的长期验证,且随着应用范围的扩大,持续接受安全性审查和优化,进一步保障了其可靠性。

4. 安全的关键在于正确实现与使用

SM2的安全性不仅取决于算法本身,更依赖于正确的工程实现和密钥管理

  • 密钥生成必须使用密码学安全的随机数(如gmssl库的内置随机数生成器);
  • 私钥需严格保密,避免泄露或重复使用;
  • 加密/解密过程需遵循标准流程(如正确处理明文编码、密文格式等)。

历史上的密码安全事件,多源于实现漏洞(如随机数生成器不安全)而非算法本身的缺陷,因此规范使用是保障安全的核心。

在当前技术条件下,SM2仍然是一种安全可靠的加密算法,能够满足绝大多数场景的安全需求,尤其适合对安全性要求较高的政务、金融等领域。只要遵循标准实现并做好密钥管理,SM2的安全性可以得到充分保障。

随着密码学研究和计算能力的发展,未来可能需要升级到抗量子密码算法,但就目前而言,SM2仍是主流且安全的选择。

Python GMSSL 库介绍

gmssl是一个开源的Python库,实现了国密SM2、SM3、SM4等算法,提供了简单易用的API,方便开发者在Python项目中集成国密加密功能。

Python GMSSL 库介绍

gmssl是一个开源的Python库,实现了国密SM2、SM3、SM4等算法,提供了简单易用的API,方便开发者在Python项目中集成国密加密功能。

SM2算法实现示例-python

SM2加密基于椭圆曲线离散对数问题(ECDLP):在特定椭圆曲线上,已知基点 G 和点 kG,求解整数 k 是计算困难的,主要解决 “非对称场景” 的安全问题(如无预先共享密钥的加密、身份认证),其价值在于用短密钥实现高安全强度;

创建SM2加密需要生成密钥对,合理正确的密钥对为hex格式或者base64格式,以下采用hex格式进行实验。

1. 配置文件(存储密钥)

创建项目根目录/config/sm2.json文件存储密钥:

1
2
3
4
{
"privateKey": "b909cbc0a0dc8f9c61450756cd6af3156fa58ae49fcc75ba9786721c46cdf9f5",
"publickey": "04d235a280f0d8d17d09704dabcf0fd8152b1a23a620c561b9e16ebc9cd93901eab28926d8439a758c8f86ed4bfb6a3f8e20b23a949ba28013a92db40582f3b29f"
}

2. 配置加载工具

创建与/config同级目录/utils,增加方法获取sm2.json文件内容,命名为loadConfig.py

注:这份代码的主要目的是通过中间件进行读取,后续可扩展增加记录服务器、token的ACL策略及缓存节点等功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import os
import json

def load_sm2_config(config_path=None):
if config_path is None:
current_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(current_dir, '..', 'config', 'sm2.json')

try:
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
return config
except Exception as e:
print(f"配置文件异常或者服务处于故障: {e}")
return None

3. SM2加解密工具类

/utils目录下创建类文件gmsm2.py,实现加解密功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from gmssl import sm2
from loadConfig import load_sm2_config


class GmSm2:
def __init__(self):
self.sm2_data = load_sm2_config()
self.sm2_public_key = self.sm2_data['publickey']
self.sm2_private_key = self.sm2_data['privateKey']

self.sm2_crypt = sm2.CryptSM2(
public_key=self.sm2_public_key,
private_key=self.sm2_private_key
)

def encrypt(self, plain_text):
if not plain_text:
raise ValueError("明文不能为空")

data = plain_text.encode('utf-8')
encrypt_data = self.sm2_crypt.encrypt(data)
return encrypt_data.hex()

def decrypt(self, cipher_text_hex):
if not cipher_text_hex:
raise ValueError("密文不能为空")

try:
cipher_text = bytes.fromhex(cipher_text_hex)
decrypt_data = self.sm2_crypt.decrypt(cipher_text)
return decrypt_data.decode('utf-8')
except Exception as e:
raise ValueError(f"解密失败: {str(e)}")


# 使用示例
if __name__ == "__main__":
try:
sm2_util = GmSm2()
test_text = "202019"
print(f"=============加密前{test_text}=============\n\n\n")

encrypted = sm2_util.encrypt(test_text)

print(f"=============加密后{encrypted}=============\n\n\n")
decrypted = sm2_util.decrypt(encrypted)
print(f"=============解密后{decrypted}=============\n\n\n")

assert test_text == decrypted, "加密解密不一致"
print("加密解密验证成功")
except Exception as e:
print(f"发生错误: {e}")

运行结果

1
72a71b6c50833ab41b1daba8e95c5c6a83463679710e25ab7865e6277d523e86d91d60f8ef7185a082aa597d570f4da1015c589a1115e622ca6af926855f9222c5661ab1c5151835b87bce12a8abde5e87b038e293cf1cfd33890bd92a31897d07227a28137a

SM2 Python运行结果

SM2算法实现示例-Thinkphp8

环境准备

  • 安装 php-8.1 以及 composer

    1
    apt install php && apt install composer
  • 安装thinkphp8环境

    1
    composer create-project topthink/think tp

ThinkPHP8安装结果

  • 安装国密加密需要的库

    请注意在项目根目录执行

    1
    sudo apt install php8.1-gmp && composer require lpilp/guomi

国密库安装结果

创建配置文件

创建项目根目录/config/sm2.php(需要手动创建):

1
2
3
4
5
6
7
8
9
10
11
<?php
// +----------------------------------------------------------------------
// | SM2配置
// +----------------------------------------------------------------------

return [
// 公钥
'publickey' => "04d235a280f0d8d17d09704dabcf0fd8152b1a23a620c561b9e16ebc9cd93901eab28926d8439a758c8f86ed4bfb6a3f8e20b23a949ba28013a92db40582f3b29f",
// 私钥
'privatekey' => "b909cbc0a0dc8f9c61450756cd6af3156fa58ae49fcc75ba9786721c46cdf9f5"
];

创建公共函数文件

由于加密是常用功能,利用tp8的common方法定义公共函数,文件路径:/项目根目录/app/common.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
// 应用公共文件
use Rtgm\sm\RtSm2;
use think\facade\Config;
use think\response\Json;


/**
* SM2加密函数
* @param string $msg 需要加密的内容
* @return Json 返回JSON响应
*/
function sm2Encrypt(string $msg): Json
{
$sm2 = new RtSm2();
$public_key = Config::get("sm2.publickey");
$encrypted = $sm2->doEncrypt($msg, $public_key);

if (empty($encrypted)) {
throw new \Exception("加密结果为空");
}

return Json([
'code' => 200,
'data' => [
'e_msg' => $encrypted,
'msg' => "加密成功"
]
]);
}


/**
* SM2解密函数
* @param string $e_msg 需要解密的内容
* @return Json 返回JSON响应
*/
function sm2Decrypt(string $e_msg): Json
{
$sm2 = new RtSm2();
$private_key = Config::get("sm2.privatekey");
$decrypted = $sm2->doDecrypt($e_msg, $private_key);
return Json([
'code' => 200,
'data' => [
'e_msg' => $decrypted,
'msg' => "解密成功"
]
]);
}

创建控制器

创建控制器类实现加解密接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

namespace app\controller;

use app\BaseController;
use think\facade\Request;

class Index extends BaseController
{
/**
* 加密接口
*/
public function encryp()
{
$msg = Request()->param("msg");
$e_msg = sm2Encrypt($msg);
return $e_msg;
}

/**
* 解密接口
*/
public function decryp()
{
$msg = Request()->param("msg");
$d_msg = sm2Decrypt($msg);
return $d_msg;
}
}

接口测试结果

加密测试

1
2
3
4
5
6
7
{
"code": 200,
"data": {
"e_msg": "e27c3780e7069bda7082a23a489d77587ce309583ed99253f66e1d9833ed1a1d0b5ce86dc6714e9974cf258589139d7b1855e8c9fa2f2c1175ee123a95a23e9b7a394ee3a1cf399400f88e15dec5321911e79baf3390b35a9a49a3833cd6f97724342852beb5",
"msg": "加密成功"
}
}

SM2加密测试结果

解密测试

1
2
3
4
5
6
7
{
"code": 200,
"data": {
"e_msg": "202019",
"msg": "加密成功"
}
}

SM2解密测试结果

SM2算法实现示例-Node.js-gm-crypto

  • nvm 版本号0.39.7
  • node 版本号 v20.10.0

安装库

首先在你的项目根目录下执行

1
npm install gm-crypto && touch sm2Encrypt.js

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const { SM2 } = require('gm-crypto');


function encryptWithPublicKey(publicKey, plaintext) {
  try {
    const ciphertext = SM2.encrypt(plaintext, publicKey, {
      inputEncoding: 'utf8',
      outputEncoding: 'base64'
    });
    return ciphertext;
  } catch (error) {
    console.error('加密失败:', error.message);
    throw error;
  }
}



function decryptWithPrivateKey(privateKey, ciphertext) {
  try {
    const plaintext = SM2.decrypt(ciphertext, privateKey, {
      inputEncoding: 'base64',
      outputEncoding: 'utf8'
    });
    return plaintext;
  } catch (error) {
    console.error('解密失败:', error.message);
    throw error;
  }
}




async function main() {
  const publicKey = '04d235a280f0d8d17d09704dabcf0fd8152b1a23a620c561b9e16ebc9cd93901eab28926d8439a758c8f86ed4bfb6a3f8e20b23a949ba28013a92db40582f3b29f';  // 例如:'04d1a1...'(长度较长,具体以你的实际公钥为准)
  const privateKey = 'b909cbc0a0dc8f9c61450756cd6af3156fa58ae49fcc75ba9786721c46cdf9f5';
 
  console.log('使用的密钥对:');
  console.log('公钥:', publicKey);
  console.log('私钥:', privateKey);
 
  const originalData = '202019';
  console.log('\n原始数据:', originalData);
 
  const encryptedData = encryptWithPublicKey(publicKey, originalData);
  console.log('加密后的数据:', encryptedData);
 


  const decryptedData = decryptWithPrivateKey(privateKey, encryptedData);
  console.log('解密后的数据:', decryptedData);
 


  if (originalData === decryptedData) {
    console.log('\n加密解密验证成功');
  } else {
    console.log('\n加密解密验证失败');
  }
}

main().catch(console.error);

解密如图

一、SM2加密核心总结

SM2作为国密体系中的非对称椭圆曲线加密算法,核心价值在于以256位密钥长度实现相当于3072位RSA的安全强度,兼顾安全性与性能。其加密过程基于椭圆曲线离散对数问题(ECDLP),通过公钥加密、私钥解密的非对称机制,解决无预先共享密钥场景下的安全通信问题(如敏感信息加密、身份认证等)。

从实现角度,SM2加密需依赖密码学安全的随机数生成(确保加密过程的唯一性)、规范的密钥格式(通常为十六进制或Base64),以及符合国密标准的椭圆曲线参数(如SM2P256V1)。无论是Python的gmssl库还是ThinkPHP8中使用的lpilp/guomi库,核心流程均为:明文编码→公钥加密→密文编码(如十六进制),解密则反之。

二、重要补充内容

1. 密钥管理是安全核心

SM2的安全性高度依赖密钥管理:

  • 密钥生成:必须使用密码学安全的随机数生成器(如gmssl内置的CSPRNG),禁止使用伪随机数(如基于系统时间的简单随机数),否则可能导致私钥可预测。
  • 密钥存储:私钥需加密存储(可结合SM4对称加密),避免明文写入配置文件或数据库;公钥可公开,但需确保来源可信(防止中间人替换公钥)。
  • 密钥轮换:定期更新密钥对(如每3-6个月),降低密钥泄露后的风险扩散范围。
2. 加密场景的局限性与配合策略

SM2作为非对称算法,加密速度慢于对称算法(如SM4),因此实际应用中通常采用“混合加密”策略:

  • 用SM2加密对称密钥(如SM4的128位密钥),而非直接加密大文件或长文本;
  • 对称密钥用于加密实际数据,通过SM2实现密钥的安全分发,兼顾效率与安全性。
3. 除加解密外的核心功能:数字签名

SM2不仅支持加密,还具备数字签名功能(符合GB/T 32918标准),可用于数据完整性校验与身份认证:

  • 签名过程:用私钥对数据哈希(通常结合SM3)进行签名;
  • 验签过程:用公钥验证签名有效性,确保数据未被篡改且来源于合法发送方。
  • 示例(Python gmssl):
    # 签名
    sign = sm2_crypt.sign(data, random_hex_str)  # random_hex_str为随机数
    # 验签
    verify = sm2_crypt.verify(sign, data)  # 返回True/False
    
4. 合规性与标准化要求

在政务、金融等关键领域,SM2的使用需符合国家密码管理局规范:

  • 必须使用推荐的曲线参数(SM2P256V1),禁止自定义未认证的曲线;
  • 加密/签名流程需严格遵循《GMT 0003-2012 SM2椭圆曲线公钥密码算法》,避免因流程简化导致安全漏洞(如签名时重复使用随机数)。
5. 跨语言实现的兼容性注意

不同语言库(如Python gmssl与PHP lpilp/guomi)实现SM2时,需确保:

  • 密钥格式一致(如公钥是否带前缀04,私钥是否为纯整数十六进制);
  • 密文编码格式统一(如均使用十六进制或Base64);
  • 随机数生成策略兼容(避免因随机数格式差异导致解密失败)。

三、总结

SM2作为国密体系的核心非对称算法,在安全性、性能与合规性上均能满足关键场景需求。其正确应用的核心在于:规范的密钥管理、符合标准的实现流程、与对称算法的合理配合。开发者在集成时,需优先使用经过验证的库(如gmssllpilp/guomi),并关注密钥生命周期管理与跨平台兼容性,以充分发挥其安全价值。