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

news2024/12/23 6:24:20

 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)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot中MD5使用

SpringBoot中MD5使用 新建md5类 public final class MD5 {public static String encrypt(String strSrc) {try {char[] hexChars {0, 1, 2, 3, 4, 5, 6, 7, 8,9, a, b, c, d, e, f};byte[] bytes strSrc.getBytes();MessageDigest md MessageDigest.getInstance("MD5…

设计模式前置了解uml图

在开发前&#xff0c;会进行系统的设计&#xff0c;而数据模型的设计大多通过 UML 类图实现。为了在 UML 类图中清晰地表达类之间的关系&#xff0c;需要对类之间的关系有一定的认识&#xff0c;并且了解相关的表达符号。 类之间的关系有以下几种&#xff1a; 组合 聚合 关联…

IPC:管道

一、管道的概念 1.原理 在进程3G~4G的内核空间中&#xff0c;创建一个特殊的文件&#xff08;管道&#xff09;&#xff0c;管道的数据直接保存在内存中。 2.特性 1&#xff09;管道可以看成是一个特殊的文件&#xff0c;一般的文件存储在外存中&#xff0c;而管道内容是存储…

“光谱视界革新:ChatGPT在成像光谱遥感中的智能革命“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…

docker——启动各种服务

1.Mysql 2.Redis 3.nginx 4.ES 注意&#xff1a;ES7之后环境为 -e ELASTICSEARCH_HOSTS http://ip地址:9200

双场板功率型GaN HEMT中用于精确开关行为的电容建模

来源:Capacitance Modeling in Dual Field-Plate Power GaN HEMT for Accurate Switching Behavior (TED 16年) 摘要 本文提出了一种基于表面电势的紧凑模型&#xff0c;用于描述具有栅极和源极场板&#xff08;FP&#xff09;结构的AlGaN/GaN高电子迁移率晶体管&#xff08;…

在OpenStack架构中,Controller节点的配置(基础)

虚拟机的安装 新建虚拟机&#xff0c;选择自定义 默认选择即可 操作系统的镜像稍后选择 客户及操作系统选择Linux&#xff0c;注意选择centos 7 64位 给虚拟机命名 处理器的配置建议1&#xff1a;2 内存大小选择建议为&#xff1a;4GB 网络连接选择为&#xff1a;NAT 默认即可…

Redis底层核心对象RedisObject源码分析

文章目录 1. redis底层数据结构2. 插入KV底层源码流程分析 1. redis底层数据结构 redis 6数据结构和底层数据结构的关系 String类型本质是SDS动态字符串&#xff0c;即redis层面的数据结构底层会有对应的数据结构实现&#xff0c;上面是redis 6之前的实现 redis 7数据结构和底…

如何保证消息的顺序性

先看看顺序会错乱的场景&#xff1a;RabbitMQ&#xff1a;一个 queue&#xff0c;多个 consumer&#xff0c;这不明显乱了&#xff1a; 解决方案&#xff1a;

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的番茄新鲜程度检测系统(深度学习+UI界面+训练数据集)

摘要&#xff1a;本研究详述了一种采用深度学习技术的番茄新鲜程度检测系统&#xff0c;该系统集成了最新的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行了性能评估对比。该系统能够在各种媒介——包括图像、视频文件、实时视频流及批量文件中——准确地识…

智能泵站智能运维系统

在现代化城市建设和工农业发展中&#xff0c;泵站作为关键的水利设施&#xff0c;其运行效率和稳定性至关重要。然而&#xff0c;传统的泵站运维方式往往依赖于人工巡检和定期维护&#xff0c;这种方式不仅效率低下&#xff0c;而且难以应对突发状况。随着物联网技术的飞速发展…

支小蜜校园防欺凌系统真的能有效遏制欺凌现象吗?

随着社会的快速发展&#xff0c;校园欺凌问题逐渐浮出水面&#xff0c;引起了广泛关注。为了应对这一问题&#xff0c;校园防欺凌系统应运而生&#xff0c;旨在通过一系列措施&#xff0c;有效遏制欺凌现象的发生。然而&#xff0c;这一系统是否真的能够如预期般发挥作用&#…

软考高项(信息系统项目管理师)备考一、介绍

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Cassandra 安装部署

文章目录 一、概述1.官方文档2. 克隆服务器3.安装准备3.1.安装 JDK 113.2.安装 Python3.3.下载文件 二、安装部署1.配置 Cassandra2.启动 Cassandra3.关闭Cassandra4.查看状态5.客户端连接服务器6.服务运行脚本 开源中间件 # Cassandrahttps://iothub.org.cn/docs/middleware/…

干货!不懂Python的math模块和random模块操作还不赶紧来学!

1.导入math模块 import math 2.向上取整&#xff1a;math.ceil() num 9.12print(math.ceil(num)) # 10 3.向下取整&#xff1a;math.floor() num1 9.99print(math.floor(num1)) # 9 4.开平方&#xff1a;math.sqrt()​​​​​​​ num2 16print(math.sqrt(num…

Python程序控制

一、程序的描述方式 1.1自然语言 &#xff08;1&#xff09;概念&#xff1a;自然语言就是使用人类语言、直接描述程序 &#xff08;2&#xff09;比如&#xff1a;之前提过的Input&#xff08;输入&#xff09;、Process&#xff08;处理&#xff09;、Output&#xff08;输…

RabbitMQ - 02 - 基本消息模型

目录 部署demo项目 什么是基本消息模型 实现基本消息模型 部署demo项目 首先配置好一个mq的练习demo,并配置好相关依赖 链接&#xff1a;https://pan.baidu.com/s/1oXAqgoz9Y_5V7YxC_rLa-Q?pwdv2sg 提取码&#xff1a;v2sg 如图 父xml文件已经配置好了 AMQP依赖了 什么…

1.Python是什么?——跟老吕学Python编程

1.Python是什么&#xff1f;——跟老吕学Python编程 Python是一种什么样的语言&#xff1f;Python的优点Python的缺点 Python发展历史Python的起源Python版本发展史 Python的价值学Python可以做什么职业&#xff1f;Python可以做什么应用&#xff1f; Python是一种什么样的语言…

【Java从入门到精通】Java异常处理

异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误有时候是可以避免的。 比如说&#xff0c;你的代码少了一个分号&#xff0c;那么运行出来结果是提示是错误 java.lang.Error&#xff1b;如果你用System.out.println(11/0)&#xff0c;那么…

Linux 安装Nginx (Nginx-1.25.4)

一、下载Nginx安装包 1.服务器联网的情况下&#xff0c;使用wget命令把Nginx安装包下载到/usr/local/目录中&#xff0c;如果没有wget命令&#xff0c;需要先安装&#xff1a;yum install -y wget cd /usr/local wget -c https://nginx.org/download/nginx-1.25.4.tar.gz ng…