国密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项目中集成国密加密功能。
{% tabs test4 %}
Python GMSSL 库介绍
gmssl是一个开源的Python库,实现了国密SM2、SM3、SM4等算法,提供了简单易用的API,方便开发者在Python项目中集成国密加密功能。
SM2算法实现示例-python
SM2加密基于椭圆曲线离散对数问题(ECDLP):在特定椭圆曲线上,已知基点 G 和点 kG,求解整数 k 是计算困难的,主要解决 "非对称场景" 的安全问题(如无预先共享密钥的加密、身份认证),其价值在于用短密钥实现高安全强度;
创建SM2加密需要生成密钥对,合理正确的密钥对为hex格式或者base64格式,以下采用hex格式进行实验。
1. 配置文件(存储密钥)
创建项目根目录/config/sm2.json文件存储密钥:
json
{
"privateKey": "b909cbc0a0dc8f9c61450756cd6af3156fa58ae49fcc75ba9786721c46cdf9f5",
"publickey": "04d235a280f0d8d17d09704dabcf0fd8152b1a23a620c561b9e16ebc9cd93901eab28926d8439a758c8f86ed4bfb6a3f8e20b23a949ba28013a92db40582f3b29f"
}
2. 配置加载工具
创建与/config同级目录/utils,增加方法获取sm2.json文件内容,命名为loadConfig.py:
注:这份代码的主要目的是通过中间件进行读取,后续可扩展增加记录服务器、token的ACL策略及缓存节点等功能
python
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,实现加解密功能:
python
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}")
运行结果
plaintext
72a71b6c50833ab41b1daba8e95c5c6a83463679710e25ab7865e6277d523e86d91d60f8ef7185a082aa597d570f4da1015c589a1115e622ca6af926855f9222c5661ab1c5151835b87bce12a8abde5e87b038e293cf1cfd33890bd92a31897d07227a28137a
SM2算法实现示例-Thinkphp8
环境准备
- 安装 php-8.1 以及 composer
bash
apt install php && apt install composer
- 安装thinkphp8环境
bash
composer create-project topthink/think tp
- 安装国密加密需要的库
请注意在项目根目录执行
bash
sudo apt install php8.1-gmp && composer require lpilp/guomi
创建配置文件
创建项目根目录/config/sm2.php(需要手动创建):
php
<?php
// +----------------------------------------------------------------------
// | SM2配置
// +----------------------------------------------------------------------
return [
// 公钥
'publickey' => "04d235a280f0d8d17d09704dabcf0fd8152b1a23a620c561b9e16ebc9cd93901eab28926d8439a758c8f86ed4bfb6a3f8e20b23a949ba28013a92db40582f3b29f",
// 私钥
'privatekey' => "b909cbc0a0dc8f9c61450756cd6af3156fa58ae49fcc75ba9786721c46cdf9f5"
];
创建公共函数文件
由于加密是常用功能,利用tp8的common方法定义公共函数,文件路径:/项目根目录/app/common.php
php
<?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' => "解密成功"
]
]);
}
创建控制器
创建控制器类实现加解密接口:
php
<?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;
}
}
接口测试结果
加密测试
json
{
"code": 200,
"data": {
"e_msg": "e27c3780e7069bda7082a23a489d77587ce309583ed99253f66e1d9833ed1a1d0b5ce86dc6714e9974cf258589139d7b1855e8c9fa2f2c1175ee123a95a23e9b7a394ee3a1cf399400f88e15dec5321911e79baf3390b35a9a49a3833cd6f97724342852beb5",
"msg": "加密成功"
}
}
解密测试
json
{
"code": 200,
"data": {
"e_msg": "202019",
"msg": "加密成功"
}
}
SM2算法实现示例-Node.js-gm-crypto
- nvm 版本号
0.39.7 - node 版本号
v20.10.0
安装库
首先在你的项目根目录下执行
bash
npm install gm-crypto && touch sm2Encrypt.js
代码如下
javascript
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);
解密如图
{% endtabs %}
一、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):python# 签名 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作为国密体系的核心非对称算法,在安全性、性能与合规性上均能满足关键场景需求。其正确应用的核心在于:规范的密钥管理、符合标准的实现流程、与对称算法的合理配合 。开发者在集成时,需优先使用经过验证的库(如gmssl、lpilp/guomi),并关注密钥生命周期管理与跨平台兼容性,以充分发挥其安全价值。