C# .NET 云南农信国密签名(SM2)简要解析
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)简要解析的更多相关文章
- 国密算法SM2证书制作
国密算法sm2非对称算法椭圆曲线 原文:http://www.jonllen.cn/jonllen/work/162.aspx 前段时间将系统的RSA算法全部升级为SM2国密算法,密码机和UKey硬件 ...
- C#.NET 国密SM3withSM2签名与验签 和JAVA互通
C# 基于.NET FRAMEWORK 4.5 JAVA 基于 JDK1.8 一.要点 1.签名算法:SM3withSM2. 2.签名值byte[] 转字符串时,双方要统一,这里是BASE64. 二. ...
- 关于国密HTTPS 的那些事(一)
关于国密HTTPS 的那些事(一) 随着<密码法>密码法的颁布与实施,国密的应用及推广终于有法可依.而对于应用国密其中的一个重要组成部分----国密HTTPS通信也应运而生.为了大家更好的 ...
- 部署国密SSL证书,如何兼容国际主流浏览器?
国密算法在主流操作系统.浏览器等客户端中,还没有实现广泛兼容.因此,在面向开放互联网的产品应用中,国密算法无法得到广泛应用.比如,在SSL证书应用领域,由于国际主流浏览器不信任国密算法,如果服务器部署 ...
- SM系列国密算法(转)
原文地址:科普一下SM系列国密算法(从零开始学区块链 189) 众所周知,为了保障商用密码的安全性,国家商用密码管理办公室制定了一系列密码标准,包括SM1(SCB2).SM2.SM3.SM4.SM7. ...
- 2017-2018-2 20179204《网络攻防实践》第十三周学习总结 python实现国密算法
国密商用算法是指国密SM系列算法,包括基于椭圆曲线的非对称公钥密码SM2算法.密码杂凑SM3算法.分组密码SM4算法,还有只以IP核形式提供的非公开算法流程的对称密码SM1算法等. 第1节 SM2非对 ...
- 腾讯云短信 nodejs 接入, 通过验证码修改手机示例
腾讯云短信 nodejs 接入, 通过验证码修改手机示例 参考:腾讯云短信文档国内短信快速入门qcloudsms Node.js SDK文档中心>短信>错误码 nodejs sdk 使用示 ...
- java实现阿里云短信服务发送验证码
由于做项目的时候遇到了接第三方短信服务,所以记录一下. 一.新建一个maven项目并导入相关依赖 <!--手机发送短信验证码--> <dependency> <group ...
- Linux实现树莓派3B的国密SM9算法交叉编译——(三)国密SM9算法实现
先参考这篇文章 Linux实现树莓派3B的国密SM9算法交叉编译——(二)miracl库的测试与静态库的生成 进行miracl库的交叉编译测试,并生成miracl静态链接库. 这篇文章主要介绍基于mi ...
- SM2国密证书合法性验证
通常我们遇到过的X509证书都是基于RSA-SHA1算法的,目前国家在大力推行国密算法,未来银行发行的IC卡也都是基于PBOC3.0支持国密算法的,因此我们来学习一下如何验证SM2国密证书的合法性.至 ...
随机推荐
- 力扣122(java&python)-买卖股票的最佳时机 II(中等)
题目: 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格. 在每一天,你可以决定是否购买和/或出售股票.你在任何时候 最多 只能持有 一股 股票.你也可以先购买 ...
- 双11特刊 | 一文揭秘云数据库RDS如何顺滑应对流量洪峰
简介:从绿色低碳到硬核科技,看RDS如何用绿色科技助力2021"双11"? 双十一回顾 从平台到商家,再从物流到客户手中,云数据库RDS支撑着双11集团电商的在线业务.RDS首次 ...
- Facebook宕机背后,我们该如何及时发现DNS问题
简介: 国庆期间,Facebook 及其旗下 Instagram 和 WhatsApp 等应用全网宕机,停机时间将近 7 小时 5 分钟,Facebook 市值损失 643 亿美元.针对Facebo ...
- PyQt5编程学习之控件基类QObject深入
一.QObject描述: PyQt几乎所有的类都是从QObject直接或间接继承的,QObject是所有PyQt类的基类,是PyQt对象模型的核心. 二.基类QObject的功能: (一)操作对象名称 ...
- netcore5下js请求跨域
后端代码如下: using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System ...
- java.lang.NoSuchMethodException: tk.mybatis.mapper.provider.base.BaseSelectProvider
解决错误: java.lang.NoSuchMethodException: tk.mybatis.mapper.provider.base.BaseSelectProvider 整合一遍通用mapp ...
- 应急响应--windows入侵排查
- golang 并发问题
如何使用channel实现定时器? 使用channel的阻塞,里面放一个sleep就可以了 Go语言--goroutine并发模型: 视频地址: https://www.bilibili.com/vi ...
- 简易版跳板机-teleport使用
目录 1 环境搭建 2 teleport工具搭建 3 teleport使用示例 3.1 资产管理-添加主机 3.2 资产管理-添加账号 3.3 创建用户 3.4 运维授权 3.5 安装客户端助手 3. ...
- DB2 关联更新
update GIS_TER_ADDRESS_MSG set (POS_X,POS_Y)=(select LAT,LON from TEMP_ATM where GIS_TER_ADDRESS_MSG ...