C# SM2加解密 ——国密SM2算法

 SM2 是国家密码管理局组织制定并提出的椭圆曲线密码算法标准。

本文使用第三方密码库 BouncyCastle 实现 SM2 加解密,使用 NuGet 安装即可,包名:Portable.BouncyCastle,目前最新版本为:1.9.0

using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Collections.Generic;
using System.Text;

namespace AI_SXPA.Utility
{
    /// <summary>
    /// 可用
    /// </summary>
    public class Sm2Util
    {
        /// <summary>
        ///     加密模式
        /// </summary>
        public enum Mode
        {
            C1C2C3,
            C1C3C2
        }

        private readonly Mode _mode;
        private readonly string _privkey;

        private ICipherParameters _privateKeyParameters;
        private string _pubkey;

        private ICipherParameters _publicKeyParameters;

        public Sm2Util(string pubkey, string privkey, Mode mode = Mode.C1C3C2, bool isPkcs8 = false)
        {
            if (pubkey != null)
                _pubkey = pubkey;
            if (privkey != null)
                _privkey = privkey;
            _mode = mode;
        }

        public Sm2Util(string pubkey, Mode mode = Mode.C1C3C2, bool isPkcs8 = false)
        {
            if (pubkey != null)
                _pubkey = pubkey;
            _mode = mode;
        }

        private ICipherParameters PrivateKeyParameters
        {
            get
            {
                try
                {
                    var r = _privateKeyParameters;
                    if (r == null)
                        r = _privateKeyParameters =
                            new ECPrivateKeyParameters(new BigInteger(_privkey, 16),
                                new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
                    return r;
                }
                catch (Exception ex)
                {
                    return null;
                }
            }
        }

        private ICipherParameters PublicKeyParameters
        {
            get
            {
                try
                {
                    var r = _publicKeyParameters;
                    if (r == null)
                    {
                        //截取64字节有效的SM2公钥(如果公钥首个字节为0x04)
                        if (_pubkey.Length > 128) _pubkey = _pubkey.Substring(_pubkey.Length - 128);
                        //将公钥拆分为x,y分量(各32字节)
                        var stringX = _pubkey.Substring(0, 64);
                        var stringY = _pubkey.Substring(stringX.Length);
                        //将公钥x、y分量转换为BigInteger类型
                        var x = new BigInteger(stringX, 16);
                        var y = new BigInteger(stringY, 16);
                        //通过公钥x、y分量创建椭圆曲线公钥规范
                        var x9Ec = GMNamedCurves.GetByName("SM2P256V1");
                        r = _publicKeyParameters = new ECPublicKeyParameters(x9Ec.Curve.CreatePoint(x, y),
                            new ECDomainParameters(x9Ec));
                    }

                    return r;
                }
                catch (Exception ex)
                {
                    return null;
                }
            }
        }

        /// <summary>
        ///     生成秘钥对
        /// </summary>
        /// <returns></returns>
        public static Dictionary<string, string> GenerateKeyPair()
        {
            string[] param =
            {
                "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", // p,0
                "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", // a,1
                "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", // b,2
                "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", // n,3
                "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", // gx,4
                "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5
            };
            var eccParam = param;

            var eccP = new BigInteger(eccParam[0], 16);
            var eccA = new BigInteger(eccParam[1], 16);
            var eccB = new BigInteger(eccParam[2], 16);
            var eccN = new BigInteger(eccParam[3], 16);
            var eccGx = new BigInteger(eccParam[4], 16);
            var eccGy = new BigInteger(eccParam[5], 16);
            ECFieldElement element = new FpFieldElement(eccP, eccGx);
            ECFieldElement ecFieldElement = new FpFieldElement(eccP, eccGy);

            ECCurve eccCurve = new FpCurve(eccP, eccA, eccB);
            ECPoint eccPointG = new FpPoint(eccCurve, element, ecFieldElement);

            var bcSpec = new ECDomainParameters(eccCurve, eccPointG, eccN);
            var ecgenparam = new ECKeyGenerationParameters(bcSpec, new SecureRandom());
            var generator = new ECKeyPairGenerator();
            generator.Init(ecgenparam);

            var key = generator.GenerateKeyPair();
            var ecpriv = (ECPrivateKeyParameters)key.Private;
            var ecpub = (ECPublicKeyParameters)key.Public;
            var privateKey = ecpriv.D;
            var publicKey = ecpub.Q;
            var dic = new Dictionary<string, string>();
            dic.Add("pubkey", Encoding.Default.GetString(Hex.Encode(publicKey.GetEncoded())));
            dic.Add("prikey", Encoding.Default.GetString(Hex.Encode(privateKey.ToByteArray())));
            //dic.Add("pubkey", Encoding.Default.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper());           
            //dic.Add("prikey", Encoding.Default.GetString(Hex.Encode(privateKey.ToByteArray())).ToUpper());
            return dic;
        }
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public byte[] Decrypt(byte[] data)
        {
            try
            {
                if (_mode == Mode.C1C3C2)
                    data = C132ToC123(data);
                var sm2 = new SM2Engine(new SM3Digest());
                sm2.Init(false, PrivateKeyParameters);
                return sm2.ProcessBlock(data, 0, data.Length);
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public byte[] Encrypt(byte[] data)
        {
            try
            {
                var sm2 = new SM2Engine(new SM3Digest());
                sm2.Init(true, new ParametersWithRandom(PublicKeyParameters));
                data = sm2.ProcessBlock(data, 0, data.Length);
                if (_mode == Mode.C1C3C2)
                    data = C123ToC132(data);
                return data;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        private static byte[] C123ToC132(byte[] c1c2c3)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            var c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
            var c3Len = 32;
            var result = new byte[c1c2c3.Length];
            Array.Copy(c1c2c3, 0, result, 0, c1Len); //c1
            Array.Copy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3
            Array.Copy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2
            return result;
        }

        private static byte[] C132ToC123(byte[] c1c3c2)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            var c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
            var c3Len = 32;
            var result = new byte[c1c3c2.Length];
            Array.Copy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65
            Array.Copy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2
            Array.Copy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3
            return result;
        }

        /// <summary>
        ///     字节数组转16进制原码字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string BytesToHexStr(byte[] bytes)
        {
            var str = "";
            if (bytes != null)
                for (var i = 0; i < bytes.Length; i++)
                    str += bytes[i].ToString("X2");
            return str;
        }

        /// <summary>
        ///     16进制原码字符串转字节数组
        /// </summary>
        /// <param name="hexStr">"AABBCC"或"AA BB CC"格式的字符串</param>
        /// <returns></returns>
        public static byte[] HexStrToBytes(string hexStr)
        {
            hexStr = hexStr.Replace(" ", "");
            if (hexStr.Length % 2 != 0) throw new ArgumentException("参数长度不正确,必须是偶数位。");
            var bytes = new byte[hexStr.Length / 2];
            for (var i = 0; i < bytes.Length; i++)
            {
                var b = Convert.ToByte(hexStr.Substring(i * 2, 2), 16);
                bytes[i] = b;
            }

            return bytes;
        }
    }
}

SM2 加解密联调时踩坑
1、密文数据,有些加密硬件出来密文结构为 C1|C2|C3 ,有些为 C1|C3|C2 , 需要对应密文结构做解密操作
2、有些加密硬件,公钥前加04 ,私钥前加00,密文前加04 ,在处理时候,可以根据长度处理,尤其 04 的处理。

在线验证:在线SM2加密解密,生成公钥/私钥对 (config.net.cn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/452614.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker部署黑马商城项目笔记

部署后端 创建mysql目录如下&#xff0c;上传对应的文件 运行以下命令 docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT_PASSWORD123 \-v ./mysql/data:/var/lib/mysql \-v ./mysql/conf:/etc/mysql/conf.d \-v ./mysql/init:/docker-entry…

swagger踩坑之请求类不显示具体字段

swagger踩坑之请求类不显示具体字段 省流&#xff1a;枚举字段需要加上ApiModelProperty注解 过程复现&#xff1a; TestEnum 枚举不加注解&#xff0c;swagger的UI类不显示详细字段 Data Accessors(chain true) ApiModel(value "test对象", description &quo…

管理交换机

文章目录 本地管理交换机物理交换机如何本地管理ensp上的虚拟交换机如何本地管理认证模式的三种方式 远程管理交换机配置通过Telnet登录设备配置通过STelnet登录设备 --推荐的方式检查配置结果使用Cloud管理多个交换机时 华为官网配置信息 本地管理交换机 当交换机首次使用时&…

语音合成技术:从概念到应用的全面解析

目录 前言1 语音合成技术简介2 技术解析2.1 语音合成的基本流程2.2 传统语音合成技术2.3 基于深度学习的语音合成 3 语音合成技术应用3.1 虚拟助手与聊天机器人3.2 无障碍通信3.3 语言学习3.4 媒体和娱乐 4 语音合成技术的挑战4.1 自然性的提升4.2 情感表达的深化4.3 多样性与包…

Docker自建蜜罐系统【失陷检测、外网威胁感知、威胁情报】

项目地址&#xff1a; https://hfish.net Hfish是一款基于Docker的网络钓鱼平台&#xff0c;它能够帮助安全团队模拟各种网络钓鱼攻击&#xff0c;以测试和提高组织的安全防御能力。 Hfish的优点 为什么选择Hfish&#xff1f; 蜜罐通常被定义为具有轻量级检测能力、低误报率…

【3GPP】【核心网】【5G】NG接口介绍(超详细)

目录 1. NG接口定义 2. 接口原则和功能 3. NG 接口控制面 5. NG接口主要信令流程 6. NG SETUP过程 1. NG接口定义 NG接口指无线接入网与5G核心网之间的接口。在5G SA网络中&#xff0c;gNB之间通过Xn接口进行连接,gNB与5GC之间通过NG接口进行连接。NG接口分为NG-C接口和NG…

O2OA开发的新版考勤管理

O2OA(翱途)开发平台对考勤管理重新进行了开发&#xff0c;全新的版本更好用&#xff0c;更直观。 考勤管理对员工的工作出勤情况进行记录、分析和报告的过程。它是对员工工作表现评估的重要依据&#xff0c;也是企业管理中的重要组成部分。考勤管理包括对员工的工作时间、迟到…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十三)-DL-AoD定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

绝地求生:PUBG官方公布2024工作计划

大家好&#xff0c;我是闲游盒。 首先今天官方公布了2024工作计划&#xff0c;下面我们一起来了解一下2024工作重点&#xff0c;官方提到的2点&#xff1a;一是通过对PUBG的维护和优化来改善线上服务的质量&#xff0c;二是为玩家们提供更加多姿多彩的游戏体验。我个人看完了全…

新品发布:广州大彩科技COF系列2.1寸480*480 IPS 串口屏发布!

一、产品介绍 该产品是一款2.1寸分辨率为 480480的医用级工业组态串口屏&#xff0c;拥有2.1寸IPS液晶屏&#xff0c;分辨率有480480&#xff08;实际显示为R240内切圆区域&#xff09;&#xff0c;支持电容触摸。采用COF超薄结构工艺设计&#xff0c;用户安装便捷灵活&#x…

离子束铣削(Ion Beam milling)

离子束铣削 (Ion Beam milling) 是一种利用离子源在基板上进行材料去除工艺的薄膜技术。Ion Beam milling 是一种离子束溅射&#xff0c;无论是用于预清洁还是图案蚀刻&#xff0c;它都有助于确保出色的附着力和 3D 结构的精确形成。主要用于微电子制造、光学元件制造和材料科学…

python讲解(2)

目录 一.变量与赋值 二.字符串类型 引号&#xff1a; 三引号&#xff1a; 字符串拼接 三.len函数 四.注释 注释的方法 一.# 二.文档字符串 注释的要求 群体注释 五.python的报错 六.bool类型 一.变量与赋值 python中的变量是不需要声明的&#xff0c;直接定义即…

牛客网 MYSQL进阶挑战 详细知识点总结(一)

目录 前言: 一.插入记录 1.1普通插入&#xff08;全字段&#xff09;&#xff1a; 1.2普通插入&#xff08;限定字段&#xff09;&#xff1a; 1.3多条一次性插入&#xff1a; 1.4从另一个表导入&#xff1a; 1.5 replace 二.更新记录 2.1设置为新值&#xff1a; 图 2-1…

岩土工程渗流问题之有限单元法:理论、模块化编程实现、开源程序应用

有限单元法在岩土工程问题中应用非常广泛&#xff0c;很多商业软件如Plaxis/Abaqus/Comsol等都采用有限单元解法。尽管各类商业软件使用方便&#xff0c;但其使用对用户来说往往是一个“黑箱子”。相比而言&#xff0c;开源的有限元程序计算方法透明、计算过程可控&#xff0c;…

制造行业大数据应用:四大领域驱动产业升级与智慧发展

一、大数据应用&#xff1a;制造行业的智慧引擎 随着大数据技术的不断突破与普及&#xff0c;制造行业正迎来一场前所未有的变革。大数据应用&#xff0c;如同智慧引擎一般&#xff0c;为制造行业注入了新的活力&#xff0c;推动了产业升级与创新发展。 二、大数据应用在制造行…

2.Windows平台Python的下载、安装和配置环境变量——跟老吕学Python编程

2.Windows平台Python的下载、安装和配置环境变量——跟老吕学Python编程 一、下载Windows版Python1.Python官网2.Windows版Python下载网址 二、在Windows安装Python1.全自动安装Python&#xff08;不推荐&#xff09;1.1 启动安装1.2 安装进度1.3 安装完成1.4 查看版本 2.自定义…

EMQX+InfluxDB+Grafana 构建物联网可视化平台

EMQXInfluxDBGrafana 构建物联网可视化平台 本文以常见物联网使用场景为例&#xff0c;介绍了如何利用 EMQ X MQTT 服务器 InfluxDB Grafana 构建物联网数据可视化平台&#xff0c;将物联网设备上传的时序数据便捷地展现出来。 在物联网项目中接入平台的设备数据和数据存储…

DataGrip工具使用技巧

文章目录 一、设置同时查看多个SQL控制台1.1、设置同时查看多个SQL控制台1.2、还原多个窗口为一个窗口 二、设置分别显示多次查询结果 以下整理DataGrip工具使用过程中的一些快捷方式或使用技巧。 一、设置同时查看多个SQL控制台 有时候我们需要同时查看多个SQL编辑器、SQL控制…

Java中出现中文乱码浅析与问题解决

一、编码介绍 字符编码是一种将字符映射到数字代码的规则或方式。在计算机中&#xff0c;所有的数据最终都以二进制形式存储&#xff0c;包括文本数据。因此&#xff0c;要在计算机中存储和处理文本&#xff0c;就需要将字符转换为对应的数字编码。 字符编码可以分为两种基本…

基于Java的高校学院网站(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学院院系模块2.2 竞赛报名模块2.3 教育教学模块2.4 招生就业模块2.5 实时信息模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学院院系表3.2.2 竞赛报名表3.2.3 教育教学表3.2.4 招生就业表3.2.5 实时信息表 四、系…