C# SM2算法 加密,解密,签名,验签
最近时间在整SM2算法,在网上看到不少代码,基本都是使用BouncyCastle库,现在这个版本算比较好的拿来分享给大家。
首先引入包 Portable.BouncyCastle

完整代码见Gitee:https://gitee.com/Karl_Albright/CryptoHelper/blob/master/src/CryptoHelper/SM2CryptoUtil.cs
public class SM2CryptoUtil
{
public SM2CryptoUtil(byte[] pubkey, byte[] privkey, Mode mode)
{
this.pubkey = pubkey;
this.privkey = privkey;
this.mode = mode;
}
public SM2CryptoUtil(string pubkey, string privkey, Mode mode = Mode.C1C2C3, bool isPkcs8 = false)
{
if (!isPkcs8)
{
if (pubkey != null) this.pubkey = Decode(pubkey);
if (privkey != null) this.privkey = Decode(privkey);
}
else
{
if (pubkey != null) this.pubkey = ((ECPublicKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(pubkey))).Q.GetEncoded();
if (privkey != null) this.privkey = ((ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privkey))).D.ToByteArray();
}
this.mode = mode;
}
byte[] pubkey;
byte[] privkey;
Mode mode;
ICipherParameters _privateKeyParameters;
ICipherParameters PrivateKeyParameters
{
get
{
var r = _privateKeyParameters;
if (r == null) r = _privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, privkey), new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")));
return r;
}
}
ICipherParameters _publicKeyParameters;
ICipherParameters PublicKeyParameters
{
get
{
var r = _publicKeyParameters;
if (r == null)
{
var x9ec = GMNamedCurves.GetByName("SM2P256V1");
r = _publicKeyParameters = new ECPublicKeyParameters(x9ec.Curve.DecodePoint(pubkey), new ECDomainParameters(x9ec));
}
return r;
}
} public static void GenerateKeyHex(out string pubkey, out string privkey)
{
GenerateKey(out var a, out var b);
pubkey = Hex.ToHexString(a);
privkey = Hex.ToHexString(b);
}
public static void GenerateKey(out byte[] pubkey, out byte[] privkey)
{
var g = new ECKeyPairGenerator();
g.Init(new ECKeyGenerationParameters(new ECDomainParameters(GMNamedCurves.GetByName("SM2P256V1")), new SecureRandom()));
var k = g.GenerateKeyPair();
pubkey = ((ECPublicKeyParameters)k.Public).Q.GetEncoded(false);
privkey = ((ECPrivateKeyParameters)k.Private).D.ToByteArray();
} public byte[] Decrypt(byte[] data)
{
if (mode == Mode.C1C3C2) data = C132ToC123(data);
var sm2 = new SM2Engine(new SM3Digest());
sm2.Init(false, this.PrivateKeyParameters);
return sm2.ProcessBlock(data, 0, data.Length);
}
public byte[] Encrypt(byte[] data)
{
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;
}
public byte[] Sign(byte[] msg, byte[] id = null)
{
var sm2 = new SM2Signer(new SM3Digest());
ICipherParameters cp;
if (id != null) cp = new ParametersWithID(new ParametersWithRandom(PrivateKeyParameters), id);
else cp = new ParametersWithRandom(PrivateKeyParameters);
sm2.Init(true, cp);
sm2.BlockUpdate(msg, 0, msg.Length);
return sm2.GenerateSignature();
}
public bool VerifySign(byte[] msg, byte[] signature, byte[] id = null)
{
var sm2 = new SM2Signer(new SM3Digest());
ICipherParameters cp;
if (id != null) cp = new ParametersWithID(PublicKeyParameters, id);
else cp = PublicKeyParameters;
sm2.Init(false, cp);
sm2.BlockUpdate(msg, 0, msg.Length);
return sm2.VerifySignature(signature);
}
static byte[] C123ToC132(byte[] c1c2c3)
{
var gn = GMNamedCurves.GetByName("SM2P256V1");
int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
int c3Len = 32;
byte[] 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;
}
static byte[] C132ToC123(byte[] c1c3c2)
{
var gn = GMNamedCurves.GetByName("SM2P256V1");
int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
int c3Len = 32;
byte[] 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;
}
static byte[] Decode(string key)
{
return Regex.IsMatch(key, "^[0-9a-f]+$", RegexOptions.IgnoreCase) ? Hex.Decode(key) : Convert.FromBase64String(key);
}
public enum Mode
{
C1C2C3, C1C3C2
}
}
踩坑记录:
1. 网上有不少教程也是使用 Portable.BouncyCastle库类,但SM2算法写了很多代码(我也没看懂,估计是自己计算椭圆曲线),但加密起来通过在线工具 有时可以解密有时又不行。估计也是使用旧版本。
2. 由于客户提供的密钥是pkcs8,我使用的在线工具是 https://aks.jd.com/tools/sec/。其他的在线工具不一定很解密。估计也是使用旧版本。
3. 客户签名后在我这里验签一直不通过,后来客户把 org.bouncycastle的jar包从1.58升级到1.62才解决这个问题,他们自己也说1.58的jar包签名的用1.62的jar包是验签不通过的。建议大家使用时都使用最新版本。
4. 签名和验签时 id 如果不传会使用默认值 1234567812345678,下面代码里面前两种写法是等效的
signTool.VerifySign(dataBytes, signBytes); //true
signTool.VerifySign(dataBytes, signBytes, Encoding.ASCII.GetBytes("1234567812345678")); // true
signTool.VerifySign(dataBytes, signBytes, new byte[] { }); // false

5. SM2签名就是 SM3WithSM2签名。
C# SM2算法 加密,解密,签名,验签的更多相关文章
- RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密
原文:RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密 C#在using System.Security.Cryptograph ...
- C# RSACryptoServiceProvider加密解密签名验签和DESCryptoServic
C#在using System.Security.Cryptography下有 DESCryptoServiceProvider RSACryptoServiceProvider DESCryptoS ...
- js rsa sign使用笔记(加密,解密,签名,验签)
你将会收获: js如何加密, 解密 js如何签名, 验签 js和Java交互如何相互解密, 验签(重点) 通过谷歌, 发现jsrsasign库使用者较多. 查看api发现这个库功能很健全. 本文使用方 ...
- Java RSA 加密 解密 签名 验签
原文:http://gaofulai1988.iteye.com/blog/2262802 import java.io.FileInputStream; import java.io.FileOut ...
- 支付接口中常用的加密解密以及验签rsa,md5,sha
一.常用加密类型分类 1.对称加密:采用单钥对信息进行加密和解密,即同一个秘钥既可以对信息进行加密,也可以进行解密.此类型称之为对称加密.特点速度快,常用于对大量数据信息或文件加密时使用.常用例子:D ...
- .NET Core 使用RSA算法 加密/解密/签名/验证签名
前言 前不久移植了支付宝官方的SDK,以适用ASP.NET Core使用支付宝支付,但是最近有好几位用户反应在Linux下使用会出错,调试发现是RSA加密的错误,下面具体讲一讲. RSA在.NET C ...
- C# Pkcs8 1024位 加密 解密 签名 解签
部分代码来至 https://www.cnblogs.com/dj258/p/6049786.html using System; using System.Collections.Generic; ...
- RSA密钥生成、加密解密、签名验签
RSA 非对称加密公钥加密,私钥解密 私钥签名,公钥验签 下面是生成随机密钥对: //随机生成密钥对 KeyPairGenerator keyPairGen = null; try { keyPair ...
- [Python3] RSA的加解密和签名/验签实现 -- 使用pycrytodome
Crypto 包介绍: pycrypto,pycrytodome 和 crypto 是一个东西,crypto 在 python 上面的名字是 pycrypto 它是一个第三方库,但是已经停止更新,所以 ...
- RSA加密解密与加签验签
RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest).阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.1987年7月首次在美国公布 ...
随机推荐
- 手把手教你如何构建 WPF 官方开源框架源代码
从去年微软就将 WPF 开源了,差不多现在所有 WPF 的源代码都开源了.在学习框架的时候,我会做一些改动,期望能构建一个自己的版本进行测试.但是作为一个特别大的框架,想要构建跑起来可不是直接在 Vi ...
- Java Spring项目中的CORS跨域开启的几种方式
引 在服务器端开启跨域的原理,一般都是通过在HTTP Headers中的响应头的Access-Control-Allow-Origin指定放行的域,来完成的. Access-Control-Allow ...
- spannerlib优雅的go异常处理
蹩脚的go 异常处理 一般写go的人,如果他不是写算法,正常写业务代码的话,可能都会为优雅的异常处理而烦恼,因为脑子抽筋的go设计者们,总是感觉语法糖是一种很低级的东西.但是在我们大多数公司的业务逻辑 ...
- Splashtop 免费60天 大赠送
这两天又是双11,又是 EDG 夺冠,可谓喜事连连.热闹不断.我们也给大家准备了一份长达两个月的免费福利,快乐加倍嗷. 福利详情: 1.分享这篇文章(不要设置分组可见). 2.发送您的 Splasht ...
- java学习之旅(day.07)
面向对象编程(oop) 面向过程思想:线性思维 步骤清晰简单,每一步做什么很明确 适合处理较为简单地问题 面向对象思想:总分 抽象 属性+方法=类 分类的思维模式,思考问题首先会解决问题需要哪些分类, ...
- AIRIOT答疑第4期|如何使用数据分析引擎?
灵活报表曲线,满足各类分析需求! AIRIOT物联网低代码平台的数据分析引擎满足各类型数据分类及分析需求,毫秒级数据反馈速度,快速响应客户分析条件变换查询需求.通过机器学习.融合各种计算模型.人工智能 ...
- MySql类型转换函数cast
这两天处理一个现网的MySQL慢查询工单,学到了一个好用的MySQL函数:cast.事情是这样的: 我们有两张表 t_user 和 t_rule 表,需要关联这两张表去查询,关联字段是 t_user ...
- 【C#】预处理指令的学习 条件编译
预处理指令,我的理解是,编译器在编译过程中所做的指令,能根据用户定义的条件选择性的编译代码. 比如在debug下要打印些信息,然而release之后,不能出现debug的信息,这个时候就能用预处理指令 ...
- Servlet转发与重定向的资源路径问题解析
一. 问题引入 转发和重定向可以说是Servlet中最重要的知识点也不为过,因为它决定着整个向Servlet体系中,执行流程的问题. 转发: request.getRequestDispat ...
- Vue——方法(methods)
我们用 methods 选项向组件实例添加方法,它应该是一个包含所需方法的对象: <div id="app"></div> <script> c ...