BouncyCastle库(BC库)与云南农信最大的区别是 :

BC库 SM2Signer.Init()  方法比云南农信多了最后3行代码:

digest.Reset();
z = GetZ(userID); digest.BlockUpdate(z, 0, z.Length);

云南农信这3行是没有的。

多了这3行会导致云南农信,验证签名不同过。

我们需要新写一个SM2Signer名为:SM2Signer1,继承 ISigner,然后在Init 方法里去掉这3行。

我觉得原有的 GenerateSignature() 对 r,s 的包装太复杂,就新写了个 GenerateSignatureWay2()。

云南农信的JAVA DEMO 中是先用 SHA256 HASH,注释中说是再用 SM3withSM2 签名。但看起来,直接就是 SHA256withSM2 签名,不是 (SHA256 HASH  +  SM3withSM2)。

Nuget 中安装 BouncyCastle 库,1.8.9 及以上版本。

SM2Signer1 类:

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Math.EC.Multiplier;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using System; namespace TheSM2.Utils
{
public class SM2Signer1 : ISigner
{ private readonly IDsaKCalculator kCalculator = new RandomDsaKCalculator(); private readonly IDigest digest; private readonly IDsaEncoding encoding; private ECDomainParameters ecParams; private ECPoint pubPoint; private ECKeyParameters ecKey; private byte[] z; public virtual string AlgorithmName
{
get { return "SM2Sign"; }
} public SM2Signer1(IDsaEncoding encoding, IDigest digest)
{
this.encoding = encoding;
this.digest = digest;
} public SM2Signer1(IDigest digest)
: this(StandardDsaEncoding.Instance, digest)
{
} public virtual void Init(bool forSigning, ICipherParameters parameters)
{
byte[] userID; ICipherParameters cipherParameters;
if (parameters is ParametersWithID)
{
cipherParameters = ((ParametersWithID)parameters).Parameters;
userID = ((ParametersWithID)parameters).GetID();
if (((ParametersWithID)parameters).GetID().Length >= 8192)
{
throw new ArgumentException("SM2 user ID must be less than 2^16 bits long");
}
}
else
{
cipherParameters = parameters;
userID = Hex.DecodeStrict("1234567812345678");
} if (forSigning)
{
if (cipherParameters is ParametersWithRandom)
{
ParametersWithRandom parametersWithRandom = (ParametersWithRandom)cipherParameters;
ecKey = (ECKeyParameters)parametersWithRandom.Parameters;
ecParams = ecKey.Parameters;
kCalculator.Init(ecParams.N, parametersWithRandom.Random);
}
else
{
ecKey = (ECKeyParameters)cipherParameters;
ecParams = ecKey.Parameters;
kCalculator.Init(ecParams.N, new SecureRandom());
}
pubPoint = CreateBasePointMultiplier().Multiply(ecParams.G, ((ECPrivateKeyParameters)ecKey).D).Normalize();
} else
{
ecKey = (ECKeyParameters)cipherParameters;
ecParams = ecKey.Parameters;
pubPoint = ((ECPublicKeyParameters)ecKey).Q;
} //BC的 SM2Signer 是带下面3行的。而云南农信则不带。
//digest.Reset();
//z = GetZ(userID);
//digest.BlockUpdate(z, 0, z.Length);
} protected virtual ECMultiplier CreateBasePointMultiplier()
{
return new FixedPointCombMultiplier();
} public virtual void Update(byte b)
{
digest.Update(b);
}
public virtual void BlockUpdate(byte[] buf, int off, int len)
{
digest.BlockUpdate(buf, off, len);
}
protected virtual BigInteger CalculateE(BigInteger n, byte[] message)
{
// TODO Should hashes larger than the order be truncated as with ECDSA?
return new BigInteger(1, message);
} /// <summary>
/// 返回了N,r,s 并用StandardDsaEncoding 做了包装。需要反向解析出 r,s 。
/// </summary>
/// <returns></returns>
/// <exception cref="CryptoException"></exception>
public virtual byte[] GenerateSignature()
{
byte[] eHash = DigestUtilities.DoFinal(digest); BigInteger n = ecParams.N;
BigInteger e = CalculateE(n, eHash);
BigInteger d = ((ECPrivateKeyParameters)ecKey).D; BigInteger r, s; ECMultiplier basePointMultiplier = CreateBasePointMultiplier(); // 5.2.1 Draft RFC: SM2 Public Key Algorithms
do // generate s
{
BigInteger k;
do // generate r
{
// A3
k = kCalculator.NextK(); // A4
ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize(); // A5
r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
}
while (r.SignValue == 0 || r.Add(k).Equals(n)); // A6
BigInteger dPlus1ModN = BigIntegers.ModOddInverse(n, d.Add(BigIntegers.One)); s = k.Subtract(r.Multiply(d)).Mod(n);
s = dPlus1ModN.Multiply(s).Mod(n);
}
while (s.SignValue == 0); // A7
try
{
return encoding.Encode(ecParams.N, r, s);
}
catch (Exception ex)
{
throw new CryptoException("unable to encode signature: " + ex.Message, ex);
}
} private bool VerifySignature(BigInteger r, BigInteger s)
{
BigInteger n = ecParams.N; // 5.3.1 Draft RFC: SM2 Public Key Algorithms
// B1
if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0)
return false; // B2
if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0)
return false; // B3
byte[] eHash = DigestUtilities.DoFinal(digest); // B4
BigInteger e = CalculateE(n, eHash); // B5
BigInteger t = r.Add(s).Mod(n);
if (t.SignValue == 0)
return false; // B6
ECPoint q = ((ECPublicKeyParameters)ecKey).Q;
ECPoint x1y1 = ECAlgorithms.SumOfTwoMultiplies(ecParams.G, s, q, t).Normalize();
if (x1y1.IsInfinity)
return false; // B7
return r.Equals(e.Add(x1y1.AffineXCoord.ToBigInteger()).Mod(n));
} public virtual bool VerifySignature(byte[] signature)
{
try
{
BigInteger[] rs = encoding.Decode(ecParams.N, signature); return VerifySignature(rs[0], rs[1]);
}
catch (Exception)
{
} return false;
} public virtual void Reset()
{
if (z != null)
{
digest.Reset();
digest.BlockUpdate(z, 0, z.Length);
}
} private byte[] GetZ(byte[] userID)
{
AddUserID(digest, userID); AddFieldElement(digest, ecParams.Curve.A);
AddFieldElement(digest, ecParams.Curve.B);
AddFieldElement(digest, ecParams.G.AffineXCoord);
AddFieldElement(digest, ecParams.G.AffineYCoord);
AddFieldElement(digest, pubPoint.AffineXCoord);
AddFieldElement(digest, pubPoint.AffineYCoord); return DigestUtilities.DoFinal(digest);
}
private void AddUserID(IDigest digest, byte[] userID)
{
int len = userID.Length * 8;
digest.Update((byte)(len >> 8));
digest.Update((byte)len);
digest.BlockUpdate(userID, 0, userID.Length);
}
private void AddFieldElement(IDigest digest, ECFieldElement v)
{
byte[] p = v.GetEncoded();
digest.BlockUpdate(p, 0, p.Length);
} /// <summary>
/// 直接返回 R,S BigInteger数组
/// </summary>
/// <returns></returns>
/// <exception cref="CryptoException"></exception>
public virtual BigInteger[] GenerateSignatureWay2()
{
byte[] eHash = DigestUtilities.DoFinal(digest); BigInteger n = ecParams.N;
BigInteger e = CalculateE(n, eHash);
BigInteger d = ((ECPrivateKeyParameters)ecKey).D; BigInteger r, s; ECMultiplier basePointMultiplier = CreateBasePointMultiplier(); // 5.2.1 Draft RFC: SM2 Public Key Algorithms
do // generate s
{
BigInteger k;
do // generate r
{
// A3
k = kCalculator.NextK(); // A4
ECPoint p = basePointMultiplier.Multiply(ecParams.G, k).Normalize(); // A5
r = e.Add(p.AffineXCoord.ToBigInteger()).Mod(n);
}
while (r.SignValue == 0 || r.Add(k).Equals(n)); // A6
BigInteger dPlus1ModN = BigIntegers.ModOddInverse(n, d.Add(BigIntegers.One)); s = k.Subtract(r.Multiply(d)).Mod(n);
s = dPlus1ModN.Multiply(s).Mod(n);
}
while (s.SignValue == 0); // A7
try
{
//return encoding.Encode(ecParams.N, r, s);
BigInteger[] biRS = new BigInteger[] { r,s};
return biRS;
}
catch (Exception ex)
{
throw new CryptoException("unable to encode signature: " + ex.Message, ex);
}
} }
}

SM2SignUtil 类:

using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Text; namespace TheSM2.Utils
{
public class SM2SignUtil
{
public static string SM2Sign(string data,string userId,string priKey)
{ byte[] msgBytes = Encoding.UTF8.GetBytes(data); byte[] byUserId = Encoding.UTF8.GetBytes(userId); ICipherParameters param = getCipherParam(priKey, userId); Sha256Digest sha256Digest = new Sha256Digest(); //SM2Signer signer = new SM2Signer(sha256Digest); // BC 自带
SM2Signer1 signer = new SM2Signer1(sha256Digest); // 参照云南农信 //BouncyCastle SM2Signer 自带的Init 方法比 云南农信JAVA 代码多了3行,需要新写一个类 继承ISigner,去掉那3行代码,否则云南农信验证签名不通过。
signer.Init(true, param);
signer.BlockUpdate(msgBytes, 0, msgBytes.Length); //GenerateSignature 返回值用StandardDsaEncoding做了包装,包含 N、r、s 3个值。需要配合decodeSignResNRS2RS 反向解析出r、s,再拼接。
var generateSignature = signer.GenerateSignature(); var sign = decodeSignResNRS2RS(generateSignature);
return sign;
} /// <summary>
/// 只是对signer.GenerateSignature()方法返回值做了改动,直接返回BigInteger 数组。
/// </summary>
/// <param name="data"></param>
/// <param name="userId"></param>
/// <param name="priKey"></param>
/// <returns></returns>
public static string SM2SignWay2(string data, string userId, string priKey)
{ byte[] msgBytes = Encoding.UTF8.GetBytes(data); byte[] byUserId = Encoding.UTF8.GetBytes(userId); ICipherParameters param = getCipherParam(priKey, userId); Sha256Digest sha256Digest = new Sha256Digest(); //SM2Signer signer = new SM2Signer(sha256Digest); // BC 自带
SM2Signer1 signer = new SM2Signer1(sha256Digest); // 参照云南农信 // BouncyCastle SM2Signer 自带的Init 方法比 云南农信JAVA 代码多了3行,需要新写一个类 继承ISigner,去掉那3行代码,否则云南农信验证签名不通过。
signer.Init(true, param);
signer.BlockUpdate(msgBytes, 0, msgBytes.Length); var bigIntegers = signer.GenerateSignatureWay2(); byte[] rBytes = modifyRSFixedBytes(bigIntegers[0].ToByteArray());
byte[] sBytes = modifyRSFixedBytes(bigIntegers[1].ToByteArray()); byte[] signBytes = concatenate(rBytes, sBytes);
String sign = Hex.ToHexString(signBytes).ToUpper();
return sign;
} public static ICipherParameters getCipherParam(String privateKey, String userId)
{
// 反序列化私钥
ParametersWithRandom parameters = getParameterRandom(privateKey);
if (userId != null)
{
ICipherParameters param = new ParametersWithID(parameters, Encoding.UTF8.GetBytes(userId));
return param;
}
return parameters;
} private static ParametersWithRandom getParameterRandom(String privateKey)
{
BigInteger privateKeyD = new BigInteger(privateKey, 16);
X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
// //构造domain参数
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.Curve, sm2ECParameters.G,
sm2ECParameters.N);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
ParametersWithRandom parameters = new ParametersWithRandom(privateKeyParameters,
SecureRandom.GetInstance("SHA1PRNG"));
ICipherParameters param = parameters;
return parameters;
} public static String decodeSignResNRS2RS(byte[] generateSignature) {
X9ECParameters sm2ECParameters = GMNamedCurves.GetByName("sm2p256v1");
StandardDsaEncoding dsaEncoding = StandardDsaEncoding.Instance;
BigInteger[] bigIntegers = dsaEncoding.Decode(sm2ECParameters.N, generateSignature);
// BigInteger[] bigIntegers = sm2Signer.generateSignature(message); byte[] rBytes = modifyRSFixedBytes(bigIntegers[0].ToByteArray());
byte[] sBytes = modifyRSFixedBytes(bigIntegers[1].ToByteArray()); byte[] signBytes = concatenate(rBytes, sBytes);
String sign = Hex.ToHexString(signBytes).ToUpper();
return sign;
} private static byte[] modifyRSFixedBytes(byte[] rs)
{
int length = rs.Length;
int fixedLength = 32;
byte[] result = new byte[fixedLength];
if (length < 32)
{
Array.Copy(rs, 0, result, fixedLength - length, length);
}
else
{
Array.Copy(rs, length - fixedLength, result, 0, fixedLength);
}
return result;
} public static byte[] concatenate(byte[] var0, byte[] var1)
{
byte[] var2 = new byte[var0.Length + var1.Length];
Array.Copy(var0, 0, var2, 0, var0.Length);
Array.Copy(var1, 0, var2, var0.Length, var1.Length);
return var2;
} }
}

使用:

String data = "123";
string userId = "1234567812345678";
String priKey = "BDD9D484017EB3CB03D48579680EEF038BD7F4419980EE08AEACEA2A8A039062"; textBox1.Text = SM2SignUtil.SM2SignWay2(data, userId, priKey);

THE END

C# .NET 云南农信国密签名(SM2)简要解析的更多相关文章

  1. 国密算法SM2证书制作

    国密算法sm2非对称算法椭圆曲线 原文:http://www.jonllen.cn/jonllen/work/162.aspx 前段时间将系统的RSA算法全部升级为SM2国密算法,密码机和UKey硬件 ...

  2. C#.NET 国密SM3withSM2签名与验签 和JAVA互通

    C# 基于.NET FRAMEWORK 4.5 JAVA 基于 JDK1.8 一.要点 1.签名算法:SM3withSM2. 2.签名值byte[] 转字符串时,双方要统一,这里是BASE64. 二. ...

  3. 关于国密HTTPS 的那些事(一)

    关于国密HTTPS 的那些事(一) 随着<密码法>密码法的颁布与实施,国密的应用及推广终于有法可依.而对于应用国密其中的一个重要组成部分----国密HTTPS通信也应运而生.为了大家更好的 ...

  4. 部署国密SSL证书,如何兼容国际主流浏览器?

    国密算法在主流操作系统.浏览器等客户端中,还没有实现广泛兼容.因此,在面向开放互联网的产品应用中,国密算法无法得到广泛应用.比如,在SSL证书应用领域,由于国际主流浏览器不信任国密算法,如果服务器部署 ...

  5. SM系列国密算法(转)

    原文地址:科普一下SM系列国密算法(从零开始学区块链 189) 众所周知,为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,包括SM1(SCB2).SM2.SM3.SM4.SM7. ...

  6. 2017-2018-2 20179204《网络攻防实践》第十三周学习总结 python实现国密算法

    国密商用算法是指国密SM系列算法,包括基于椭圆曲线的非对称公钥密码SM2算法.密码杂凑SM3算法.分组密码SM4算法,还有只以IP核形式提供的非公开算法流程的对称密码SM1算法等. 第1节 SM2非对 ...

  7. 腾讯云短信 nodejs 接入, 通过验证码修改手机示例

    腾讯云短信 nodejs 接入, 通过验证码修改手机示例 参考:腾讯云短信文档国内短信快速入门qcloudsms Node.js SDK文档中心>短信>错误码 nodejs sdk 使用示 ...

  8. java实现阿里云短信服务发送验证码

    由于做项目的时候遇到了接第三方短信服务,所以记录一下. 一.新建一个maven项目并导入相关依赖 <!--手机发送短信验证码--> <dependency> <group ...

  9. Linux实现树莓派3B的国密SM9算法交叉编译——(三)国密SM9算法实现

    先参考这篇文章 Linux实现树莓派3B的国密SM9算法交叉编译——(二)miracl库的测试与静态库的生成 进行miracl库的交叉编译测试,并生成miracl静态链接库. 这篇文章主要介绍基于mi ...

  10. SM2国密证书合法性验证

    通常我们遇到过的X509证书都是基于RSA-SHA1算法的,目前国家在大力推行国密算法,未来银行发行的IC卡也都是基于PBOC3.0支持国密算法的,因此我们来学习一下如何验证SM2国密证书的合法性.至 ...

随机推荐

  1. 二叉查找树的实现C/C++

    二叉查找树是一种关键字有序存放的二叉树.在不含重复关键字的二叉查找树中,关键字"较小"的节点一定在关键字"较大"的节点的左子树中,"较小"一 ...

  2. 消息队列 RocketMQ 遇上可观测:业务核心链路可视化

    ​简介:本篇文章主要介绍 RocketMQ 的可观测性工具在线上生产环境的最佳实践.RocketMQ的可观测性能力领先业界同类产品,RocketMQ 的 Dashboard 和消息轨迹等功能为业务核心 ...

  3. Java对象转换方案分析与mapstruct实践

    简介: 随着系统模块分层不断细化,在Java日常开发中不可避免地涉及到各种对象的转换,如:DO.DTO.VO等等,编写映射转换代码是一个繁琐重复且还易错的工作,一个好的工具辅助,减轻了工作量.提升开发 ...

  4. [DOT] Polkadot-js 的官方资源

    官网:https://polkadot.js.org/ 浏览器扩展(即钱包, 等同以太坊的MetaMask):https://polkadot.js.org/extension/ 钱包的作用方便你管理 ...

  5. Redisant Toolbox——面向开发者的多合一工具箱

    Redisant Toolbox--面向开发者的多合一工具箱 Redisant Toolbox 拥有超过30种常用的开发工具:精心设计,快速.高效:离线使用,尊重您的隐私.官网地址:http://ww ...

  6. pod QoS等级(A)

    一.了解Pod Qos等级 一个节点不一定能提供所有pod所指定的资源limits之和那么多的资源量. 假设有两个pod,pod A使用了节点内存的 90%,pod B突然需要比之前更多的内存,这时节 ...

  7. 国产 Web 组态软件 TopStack V5.0 发布

    简介 TopStack 是一款轻量型 Web 组态软件,提供设备数据采集.定时任务.控制策略.联动控制.设备告警.设备维护管理.设备绩效管理.能源管理.组态开发.报表开发等核心功能.支持移动端访问,支 ...

  8. sqli-labs-master 第二,三,四关

    第二关: 判断注入类型:http://192.168.65.130/sqli-labs-master/Less-2/?id=1 --+ 原因:$sql="SELECT * FROM user ...

  9. 报错:Client does not support authentication protocol requested by server; consider upgrading MySQL cli

    IDEA启动项目登录时显示用户或密码错误 或者 连接mysql数据库时报错 原因: mysql8 之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是ca ...

  10. SQL Server实战七:自定义数据类型、标量值、内嵌表值、多语句表值函数的操作

      本文介绍基于Microsoft SQL Server软件,实现数据库用户自定义数据类型的创建.使用与删除,以及标量值.内嵌表值.多语句表值函数等用户定义函数的创建.使用.删除方法. 目录 1 用S ...