跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

SM2 国密算法原理与 C++跨平台实现

综述由AI生成介绍国密 SM2 非对称加密算法的原理与 C++ 跨平台实现。SM2 基于椭圆曲线密码学,提供数字签名、公钥加密及密钥交换功能。文章详细解析了有限域运算、点运算等数学基础,并给出了完整的 C++ 头文件源码,包含 SM3 哈希、大数运算及 ECC 核心逻辑。通过单元测试验证了签名、验签、加解密流程的正确性。该实现支持 Windows 与类 Unix 系统,适用于金融、政务等需要自主可控安全技术的场景。

ByteFlow发布于 2026/3/30更新于 2026/5/2449 浏览
SM2 国密算法原理与 C++跨平台实现

引言

SM2 是我国自主研发的非对称加密算法(国密算法),于 2010 年由国密局发布,2017 年成为 ISO/IEC 国际标准。其基于椭圆曲线密码学(ECC),具备安全性高(256 位密钥强度相当于 RSA-3072 位)、运算速度快、密钥短(256 位)等优势,广泛应用于数字签名、公钥加密、密钥交换等领域,是金融、政务、物联网等场景的核心安全技术。本文从算法原理到代码实现,系统解析 SM2 的核心技术。


一、SM2 算法基础

1.1 算法概述

SM2 属于非对称加密算法,包含三个核心功能:

  • 数字签名:基于椭圆曲线离散对数难题(ECDLP),保障数据来源认证与完整性
  • 公钥加密:使用接收方公钥加密数据,私钥解密
  • 密钥交换:通过 ECDH 协议生成共享密钥,用于后续对称加密
1.2 核心数学原理

SM2 的安全性依赖于椭圆曲线离散对数问题:已知椭圆曲线点 P 和 Q=kP,求 k 在计算上不可行。其数学基础包括:

  • 有限域运算:素域 Fp 上的加法、乘法、逆运算
  • 椭圆曲线方程:标准方程为 y^2 = x^3 + ax + b (mod p),推荐参数为 256 位素数域
  • 点运算:点加(P+Q)、倍点(2P)和标量乘法(kP)构成核心运算
1.3 核心参数

国密推荐曲线参数如下(十六进制):

  • 素数 p:FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
  • 系数 a:FFFFFFFE ... FFFFFFFC
  • 基点 G:(x, y) 坐标,阶 n 为 FFFFFFFE ... 39D54123
1.4 算法组件

SM2 包含四大核心算法模块:

  1. 密钥生成:随机数 d∈[1,n-1] 作为私钥,公钥 P=dG。根据上述密钥生成的流程,是先生成私钥 d,然后计算公钥 P,因此在 SM2 的密钥中,可以根据私钥计算出公钥。
  2. 数字签名:使用私钥对消息哈希值签名,生成 (r,s)
  3. 加密/解密:基于 KDF 生成会话密钥,结合椭圆曲线点运算
  4. 密钥协商:通过两次握手协议生成共享密钥

二、算法原理详解

2.1 数字签名流程

签名生成:

  1. 计算消息哈希值 e=HASH(Z_A || M),Z_A 为用户身份标识
  2. 生成随机数 k∈[1,n-1],计算点 (x1,y1)=kG
  3. r=(e + x1) mod n,若 r=0 则重新选 k
  4. s=((1+d_A)^-1 * (k - r*d_A)) mod n,输出签名 (r,s)

验签流程:

  1. 验证 r,s∈[1,n-1]
  2. 计算 t=(r+s) mod n,点 (x1,y1)=sG + tP_A
  3. 验证 r ≡ (e + x1) mod n
2.2 加密与解密流程

加密:

  1. 生成随机数 k,计算 C1=kG(椭圆曲线点)
  2. 计算 S=h*P_B(h 为余因子),若 S 为无穷远点则报错
  3. 计算 (x2,y2)=kP_B,通过 KDF 生成密钥 t
  4. 密文 C2=M⊕t,C3=HASH(x2||M||y2)
  5. 输出 C=C1||C3||C2

注:h 为余因子(通常为 1),KDF 为密钥派生函数。

解密:

  1. 验证 C1 在曲线上,计算 S=h*C1
  2. 计算 (x2,y2)=d_B*C1,生成 t=KDF(x2||y2)
  3. 解密 M'=C2⊕t,验证 C3=HASH(x2||M'||y2)
2.3 密钥交换协议

基于改进的 ECDH 协议,两轮交互生成共享密钥:

  1. 双方生成临时密钥对 (r_A, R_A) 和 (r_B, R_B)
  2. 计算共享点 U=r_A(r_B G + P_B) 和 U'=r_B(r_A G + P_A)
  3. 通过 KDF 处理 U 的坐标生成会话密钥
2.4 椭圆曲线数学优化
  • 坐标系选择:Jacobian 射影坐标减少模逆运算
  • 标量乘法优化:采用滑动窗口法、NAF 编码降低计算量
  • 预计算技术:固定基点 G 的预计算表加速 kG 运算

三、算法实现技术

3.1 硬件实现
  • 专用芯片:如 HS32U2-U,通过 SPI 接口实现低功耗加密
  • FPGA 优化:流水线设计 SM2 协处理器,时钟频率达 50MHz,标量乘仅 0.869ms
  • 抗侧信道技术:随机化 NAF 窗口、盲化标量防御 SPA/DPA 攻击
3.2 软件实现
  • Bouncy Castle 库:Java 示例代码展示密钥生成与加密
  • OpenSSL 引擎:集成 SM2 到 EVP 接口,支持标准 API 调用
  • 性能优化技巧:
    • 预计算常用点坐标(如 16 个预计算点加速 kP)
    • SIMD 指令加速有限域乘法(如 Intel AVX2)

四、安全性分析

4.1 算法设计安全性
  • 数学安全性:基于 ECDLP 难题,256 位密钥抗量子攻击优于 RSA-3072
  • 抗碰撞性:SM3 哈希保障签名与加密数据完整性
  • 标准化认证:通过国密 GM/T 0003.5-2012、ISO/IEC 14888-3
4.2 抗攻击能力
  • 侧信道防护:随机化标量乘顺序、加入噪声指令
  • 故障注入防御:双点校验、错误感染技术
  • 弱曲线防御:强制验证公共参数防止参数替换攻击
4.3 与 RSA/AES 对比
特性SM2RSA-3072AES-256
密钥长度256 位3072 位256 位
安全强度抗量子不抗量子抗量子
签名速度0.98ms(FPGA)3.2msN/A
适用场景非对称加密传统非对称加密对称加密

五、C++跨平台自实现(Header-Only)

5.1 源码封装
/*
 * Copyright (c) 2025, arbboter. All rights reserved.
 * File : filename.cpp
 * Desc : 本文件实现 SM2 模块的核心算法
 *   - SM2 的签名和验签
 *   - SM2 的加密和解密
 * Version : 1.0.0.1
 * License : SPDX-License-Identifier: Apache-2.0
 */
#ifndef __SM2_H__
#define __SM2_H__
#include <stdio.h>
#include <string.h>
#include <cstdint>
#ifdef _WIN32
#include <windows.h>
#include <bcrypt.h>
#pragma comment(lib,"bcrypt.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(status) ((NTSTATUS)(status)>=0)
#endif
#else
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif
// ... (省略部分宏定义以保持简洁,实际实现包含完整逻辑)
// 雅可比行列式
typedef struct {
    SM2_BN X;
    SM2_BN Y;
    SM2_BN Z;
} SM2_JACOBIAN_POINT;

class SM2 {
public:
    enum class CiphertextType { C1C3C2, C1C2C3, DER };

    void GenerateKey(SM2_KEY& key) {
        SM2_BN d;
        SM2_JACOBIAN_POINT Q;
        do {
            if (fn_rand(d) != 1) throw std::runtime_error("Private key generation failed");
        } while (bn_is_zero(d) || bn_cmp(d, SM2_N) >= 0);
        Q = d * G;
        jacobian_point_to_bytes(&Q, reinterpret_cast<uint8_t*>(&key.public_key));
        bn_to_bytes(d, key.private_key);
    }

    int SM2Sign(const unsigned char* src, size_t srcLen, unsigned char* sgn, size_t* sgnLen, bool rawRS = false) {
        const SM2_KEY* sk = &_key;
        sm3_context ctx;
        if (sign_init(&ctx, sk, SM2_DEFAULT_ID, strlen(SM2_DEFAULT_ID)) != 1) return 0;
        if (sign_update(&ctx, src, srcLen) != 1) return 0;
        if (sign_finish(&ctx, sk, sgn, sgnLen, rawRS) != 1) return 0;
        return 1;
    }

    // ... 其他方法实现
};
#endif // !__SM2_H__
5.2 单元测试
void TestSM2() {
    const char* priKey = "ee9751a685f4f1ca1ef355b15b937475f90757c6ea930f04a3242901a13b94a2";
    const char* pubKey = "04de16070290a70b3457fef7651c5e3be8902b5d589f3d0b80efc5f4e38a78d0e7d09b9d82acc2f31eb6314f831f0766cf644c1d5856794b9106b5f964b7b6fb5d";
    const char* pMsg = "Hello, World,I love 中国.";
    SM2 sm2;
    if (!sm2.InitKey(priKey, pubKey)) {
        std::cout << "初始化 SM2 密钥失败" << std::endl;
        return;
    }
    std::cout << "测试数据:" << strlen(pMsg) << "," << pMsg << std::endl;

    char hex[512] = {0};
    char sign[128] = {0};
    size_t len = sizeof(sign);
    if (!sm2.SM2Sign((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)sign, &len) || len == 0) {
        std::cout << "生成签名失败" << std::endl;
        return;
    }
    size_t out_len = sizeof(hex);
    memset(hex, 0, sizeof(hex));
    SM2::Bin2Hex(sign, len, hex, out_len);
    std::cout << "生成签名成功" << std::endl;
    if (!sm2.SM2Verify((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)sign, len)) {
        std::cout << "签名校验失败" << std::endl;
        return;
    }
    std::cout << "签名校验成功:" << out_len << "," << hex << std::endl;

    char ciphertext[512] = {0};
    len = sizeof(ciphertext);
    if (!sm2.SM2Encrypt((unsigned char*)pMsg, strlen(pMsg), (unsigned char*)ciphertext, &len)) {
        std::cout << "加密失败" << std::endl;
        return;
    }
    out_len = sizeof(hex);
    memset(hex, 0, sizeof(hex));
    SM2::Bin2Hex(ciphertext, len, hex, out_len);
    std::cout << "加密成功:" << out_len << "," << hex << std::endl;

    char text[512] = {0};
    size_t text_len = sizeof(text);
    if (!sm2.SM2Decrypt((unsigned char*)ciphertext, len, (unsigned char*)text, &text_len)) {
        std::cout << "解密失败" << std::endl;
        return;
    }
    std::cout << "解密成功:" << text_len << "," << text << std::endl;
}
5.3 输出结果
测试数据:26,Hello, World,I love 中国.
生成签名成功
签名校验成功:144,3046022100945d19ee176dae52533cf0a8af25030204020131d5ac232a0ed1bdb806b2d76d022100b3cdf47895c5e3c390442f7436b0f902b2eb5c8723a70ff8479fcec0b8adae63
加密成功:270,308184022100c1a0e063e981a544f974a9aacd1dd5e193f31378ec52d79e1d4522cd8f0200a0022100de6137f68384f86735c7af776525ab42b1177574305ddeec3f2547161da3ca6a042044e04f1bd288027c43eb99c6a6b1c2071a440f27f07194e0308dfa7530841c26041a926716f58ad2d5bd0c451df6ce1bdef976c608ed03aaf2f7f21f
解密成功:26,Hello, World,I love 中国.

六、结语

SM2 作为我国密码技术的核心成果,已在政务、金融、物联网等领域大规模应用。其设计融合了 ECC 的高效性与抗量子特性,结合国密标准生态(如 SM3/SM4),构建了自主可控的安全体系。未来随着抗量子算法的演进,SM2 将在后量子密码时代持续发挥关键作用。

七、可能用到的代码

class SM2 {
    /**
     * @brief 将 SM2 密文转换为十六进制字符串格式
     * @param[in] C 指向 SM2 密文结构的指针
     * @param[in] mode 输出格式模式:0: C1C3C2, 1: C1C2C3
     * @param[out] out 输出的十六进制字符串缓冲区指针
     * @param[out] outlen 输出缓冲区的实际长度
     * @return 执行结果:1: 成功,-1: 失败
     */
    int ciphertext_to_text(const SM2_CIPHERTEXT* C, int mode, uint8_t** out, size_t* outlen) {
        // 实现细节...
        return 1;
    }
};

目录

  1. 引言
  2. 一、SM2 算法基础
  3. 1.1 算法概述
  4. 1.2 核心数学原理
  5. 1.3 核心参数
  6. 1.4 算法组件
  7. 二、算法原理详解
  8. 2.1 数字签名流程
  9. 2.2 加密与解密流程
  10. 2.3 密钥交换协议
  11. 2.4 椭圆曲线数学优化
  12. 三、算法实现技术
  13. 3.1 硬件实现
  14. 3.2 软件实现
  15. 四、安全性分析
  16. 4.1 算法设计安全性
  17. 4.2 抗攻击能力
  18. 4.3 与 RSA/AES 对比
  19. 五、C++跨平台自实现(Header-Only)
  20. 5.1 源码封装
  21. 5.2 单元测试
  22. 5.3 输出结果
  23. 六、结语
  24. 七、可能用到的代码
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • PromptIR:基于提示学习的通用盲图像复原
  • Adoptium Temurin JDK 下载
  • Linux 进程深度解析(一):从内核视角理解进程本质
  • C1000K 级实时推送引擎:icomet-server 基于 C++ 打造高性能 Web 与移动推送服务
  • Python 编程入门指南:基础语法与核心应用场景
  • Git 连接报错 fatal: unable to access 解决方案
  • C++ 哈希表原理与 unordered_map/set 封装实现
  • llama.cpp 量化大模型部署与运行指南
  • 信创国产化开发为何推荐使用 Java
  • Windows 环境 Claude Code Git Bash 依赖修复方案
  • Python 基础语法练习题:列表、切片与循环
  • 大语言模型提示词编写与应用指南
  • C++ 多态底层实现机制深度解析
  • 自然语言处理(NLP)进阶:前沿技术与实战开发
  • 深入解剖 STL map/multimap:接口使用与核心特性详解
  • AI 产品经理必备的核心能力与知识体系
  • MySQL 表操作实战:创建、修改与删除全解析
  • 移动应用登录接口越权漏洞挖掘实战
  • C++ 类和对象:拷贝构造与赋值运算符重载详解
  • 前端拖拽交互实战:告别原生 API 的卡顿体验

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online