<返回更多

C# 实现SM2国密加密帮助类

2022-03-29    中年农码工
加入收藏

本人在了解对接国家医疗保障信息平台中定点医药机构接口文档中,传输加密方式使用国密算法之SM2.然后本人在隔离期间研究了一下,将内容进行总结,本文主要讲解“国密加密算法”SM系列之SM2的C#实现方法,加密规则请详阅国密局发布的文档。

首先需第三方Nuget包:Portable.BouncyCastle

1.SM2密码计算

using Org.BouncyCastle.Crypto;

using Org.BouncyCastle.Crypto.Digests;

using Org.BouncyCastle.Crypto.Parameters;

using Org.BouncyCastle.Math;

using Org.BouncyCastle.Math.EC;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace Core.Common

{

/// <summary>

/// 密码计算

/// </summary>

public class Cipher

{

private int ct = 1;

/// <summary>

/// 椭圆曲线E上点P2

/// </summary>

private ECPoint p2;

private SM3Digest sm3keybase;

private SM3Digest sm3c3;

private readonly byte[] key = new byte[32];

private byte keyOff = 0;

public Cipher()

{

}

private void Reset()

{

sm3keybase = new SM3Digest();

sm3c3 = new SM3Digest();

byte[] p;

p = p2.Normalize().XCoord.ToBigInteger().ToByteArray();

sm3keybase.BlockUpdate(p, 0, p.Length);

sm3c3.BlockUpdate(p, 0, p.Length);

p = p2.Normalize().YCoord.ToBigInteger().ToByteArray();

sm3keybase.BlockUpdate(p, 0, p.Length);

ct = 1;

NextKey();

}

private void NextKey()

{

SM3Digest sm3keycur = new SM3Digest(sm3keybase);

sm3keycur.Update((byte)(ct >> 24 & 0x00ff));

sm3keycur.Update((byte)(ct >> 16 & 0x00ff));

sm3keycur.Update((byte)(ct >> 8 & 0x00ff));

sm3keycur.Update((byte)(ct & 0x00ff));

sm3keycur.DoFinal(key, 0);

keyOff = 0;

ct++;

}

public virtual ECPoint InitEnc(SM2 sm2, ECPoint userKey)

{

AsymmetricCipherKeyPair key = sm2.EccKeyPairGenerator.GenerateKeyPair();

ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;

ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;

BigInteger k = ecpriv.D;

ECPoint c1 = ecpub.Q;

p2 = userKey.Multiply(k);

Reset();

return c1;

}

public virtual void Encrypt(byte[] data)

{

//p2.Normalize();

sm3c3.BlockUpdate(data, 0, data.Length);

for (int i = 0; i < data.Length; i++)

{

if (keyOff == key.Length)

NextKey();

data[i] ^= key[keyOff++];

}

}

public virtual void InitDec(BigInteger userD, ECPoint c1)

{

p2 = c1.Multiply(userD);

Reset();

}

public virtual void Decrypt(byte[] data)

{

for (int i = 0; i < data.Length; i++)

{

if (keyOff == key.Length)

NextKey();

data[i] ^= key[keyOff++];

}

sm3c3.BlockUpdate(data, 0, data.Length);

}

public virtual void Dofinal(byte[] c3)

{

byte[] p = p2.Normalize().YCoord.ToBigInteger().ToByteArray();

sm3c3.BlockUpdate(p, 0, p.Length);

sm3c3.DoFinal(c3, 0);

Reset();

}

}

}

 

2. 加密处理中心

using System;

using Org.BouncyCastle.Crypto.Generators;

using Org.BouncyCastle.Math.EC;

using Org.BouncyCastle.Math;

using Org.BouncyCastle.Crypto;

using Org.BouncyCastle.Crypto.Parameters;

using Org.BouncyCastle.Security;

using System.Text;

using Org.BouncyCastle.Crypto.Digests;

namespace Core.Common

{

/// <summary>

/// SM2主类

/// </summary>

/// <summary>

/// 加密处理中心

/// </summary>

public class SM2

{

public static SM2 Instance

{

get

{

return new SM2();

}

}

public static SM2 InstanceTest

{

get

{

return new SM2();

}

}

#region 曲线参数

/// <summary>

/// 曲线参数

/// </summary>

public static readonly string[] CurveParameter = {

"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",// p,0

"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",// a,1

"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",// b,2

"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",// n,3

"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",// gx,4

"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" // gy,5

};

/// <summary>

/// 椭圆曲线参数

/// </summary>

public string[] EccParam = CurveParameter;

/// <summary>

/// 椭圆曲线参数P

/// </summary>

public readonly BigInteger EccP;

/// <summary>

/// 椭圆曲线参数A

/// </summary>

public readonly BigInteger EccA;

/// <summary>

/// 椭圆曲线参数B

/// </summary>

public readonly BigInteger EccB;

/// <summary>

/// 椭圆曲线参数N

/// </summary>

public readonly BigInteger EccN;

/// <summary>

/// 椭圆曲线参数Gx

/// </summary>

public readonly BigInteger EccGx;

/// <summary>

/// 椭圆曲线参数Gy

/// </summary>

public readonly BigInteger EccGy;

#endregion

/// <summary>

/// 椭圆曲线

/// </summary>

public readonly ECCurve EccCurve;

/// <summary>

/// 椭圆曲线的点G

/// </summary>

public readonly ECPoint EccPointG;

/// <summary>

/// 椭圆曲线 bc规范

/// </summary>

public readonly ECDomainParameters EccBcSpec;

/// <summary>

/// 椭圆曲线密钥对生成器

/// </summary>

public readonly ECKeyPairGenerator EccKeyPairGenerator;

private SM2()

{

EccParam = CurveParameter;

EccP = new BigInteger(EccParam[0], 16);

EccA = new BigInteger(EccParam[1], 16);

EccB = new BigInteger(EccParam[2], 16);

EccN = new BigInteger(EccParam[3], 16);

EccGx = new BigInteger(EccParam[4], 16);

EccGy = new BigInteger(EccParam[5], 16);

ECFieldElement ecc_gx_fieldelement = new FpFieldElement(EccP, EccGx);

ECFieldElement ecc_gy_fieldelement = new FpFieldElement(EccP, EccGy);

EccCurve = new FpCurve(EccP, EccA, EccB);

EccPointG = new FpPoint(EccCurve, ecc_gx_fieldelement, ecc_gy_fieldelement);

EccBcSpec = new ECDomainParameters(EccCurve, EccPointG, EccN);

ECKeyGenerationParameters ecc_ecgenparam;

ecc_ecgenparam = new ECKeyGenerationParameters(EccBcSpec, new SecureRandom());

EccKeyPairGenerator = new ECKeyPairGenerator();

EccKeyPairGenerator.Init(ecc_ecgenparam);

}

/// <summary>

/// 获取杂凑值H

/// </summary>

/// <param name="z">Z值</param>

/// <param name="data">待签名消息</param>

/// <returns></returns>

public virtual byte[] Sm2GetH(byte[] z, byte[] data)

{

SM3Digest sm3 = new SM3Digest();

//Z

sm3.BlockUpdate(z, 0, z.Length);

//待签名消息

sm3.BlockUpdate(data, 0, data.Length);

// H

byte[] md = new byte[sm3.GetDigestSize()];

sm3.DoFinal(md, 0);

return md;

}

/// <summary>

/// 获取Z值

/// Z=SM3(ENTL∣∣userId∣∣a∣∣b∣∣gx∣∣gy ∣∣x∣∣y)

/// </summary>

/// <param name="userId">签名方的用户身份标识</param>

/// <param name="userKey">签名方公钥</param>

/// <returns></returns>

public virtual byte[] Sm2GetZ(byte[] userId, ECPoint userKey)

{

SM3Digest sm3 = new SM3Digest();

byte[] p;

// ENTL由2个字节标识的ID的比特长度

int len = userId.Length * 8;

sm3.Update((byte)(len >> 8 & 0x00ff));

sm3.Update((byte)(len & 0x00ff));

// userId用户身份标识ID

sm3.BlockUpdate(userId, 0, userId.Length);

// a,b为系统曲线参数;

p = EccA.ToByteArray();

sm3.BlockUpdate(p, 0, p.Length);

p = EccB.ToByteArray();

sm3.BlockUpdate(p, 0, p.Length);

// gx、gy为基点

p = EccGx.ToByteArray();

sm3.BlockUpdate(p, 0, p.Length);

p = EccGy.ToByteArray();

sm3.BlockUpdate(p, 0, p.Length);

// x,y用户的公钥的X和Y

p = userKey.Normalize().XCoord.ToBigInteger().ToByteArray();

sm3.BlockUpdate(p, 0, p.Length);

p = userKey.Normalize().YCoord.ToBigInteger().ToByteArray();

sm3.BlockUpdate(p, 0, p.Length);

// Z

byte[] md = new byte[sm3.GetDigestSize()];

sm3.DoFinal(md, 0);

return md;

}

}

}

 

3 加密调用

using Org.BouncyCastle.Crypto;

using Org.BouncyCastle.Crypto.Parameters;

using Org.BouncyCastle.Math;

using Org.BouncyCastle.Math.EC;

using Org.BouncyCastle.Utilities.Encoders;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace Core.Common

{

/// <summary>

/// Sm2算法

/// 对标国际RSA算法

/// </summary>

public class Sm2Crypto

{

/// <summary>

/// 数据

/// </summary>

public string Str { get; set; }

/// <summary>

/// 数据

/// </summary>

public byte[] Data { get; set; }

/// <summary>

/// 公钥

/// </summary>

public string PublicKey { get; set; }

/// <summary>

/// 私钥

/// </summary>

public string PrivateKey { get; set; }

/// <summary>

/// 获取密钥

/// </summary>

/// <param name="privateKey">私钥</param>

/// <param name="publicKey">公钥</param>

public static void GetKey(out string privateKey, out string publicKey)

{

SM2 sm2 = SM2.Instance;

AsymmetricCipherKeyPair key = sm2.EccKeyPairGenerator.GenerateKeyPair();

ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;

ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;

publicKey = Encoding.Default.GetString(Hex.Encode(ecpub.Q.GetEncoded())).ToUpper();

privateKey = Encoding.Default.GetString(Hex.Encode(ecpriv.D.ToByteArray())).ToUpper();

}

#region 解密

public object Decrypt(Sm2Crypto entity)

{

var data = !string.IsNullOrEmpty(entity.Str) ?

Hex.Decode(entity.Str) :

entity.Data;

return Encoding.Default.GetString(Decrypt(Hex.Decode(entity.PrivateKey), data));

}

/// <summary>

/// 解密

/// </summary>

/// <param name="privateKey"></param>

/// <param name="encryptedData"></param>

/// <returns></returns>

private static byte[] Decrypt(byte[] privateKey, byte[] encryptedData)

{

if (null == privateKey || privateKey.Length == 0)

{

return null;

}

if (encryptedData == null || encryptedData.Length == 0)

{

return null;

}

String data = Encoding.Default.GetString(Hex.Encode(encryptedData));

byte[] c1Bytes = Hex.Decode(Encoding.Default.GetBytes(data.Substring(0, 130)));

int c2Len = encryptedData.Length - 97;

byte[] c2 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130, 2 * c2Len)));

byte[] c3 = Hex.Decode(Encoding.Default.GetBytes(data.Substring(130 + 2 * c2Len, 64)));

SM2 sm2 = SM2.Instance;

BigInteger userD = new BigInteger(1, privateKey);

ECPoint c1 = sm2.EccCurve.DecodePoint(c1Bytes);

//c1.Normalize();

Cipher cipher = new Cipher();

cipher.InitDec(userD, c1);

cipher.Decrypt(c2);

cipher.Dofinal(c3);

return c2;

}

#endregion

#region 加密

public string Encrypt(Sm2Crypto entity)

{

var data = !string.IsNullOrEmpty(entity.Str) ?

Encoding.Default.GetBytes(entity.Str) :

entity.Data;

return Encrypt(Hex.Decode(entity.PublicKey), data);

}

/// <summary>

/// 加密

/// </summary>

/// <param name="publicKey"></param>

/// <param name="data"></param>

/// <returns></returns>

private static string Encrypt(byte[] publicKey, byte[] data)

{

if (null == publicKey || publicKey.Length == 0)

{

return null;

}

if (data == null || data.Length == 0)

{

return null;

}

byte[] source = new byte[data.Length];

Array.Copy(data, 0, source, 0, data.Length);

Cipher cipher = new Cipher();

SM2 sm = SM2.Instance;

ECPoint userKey = sm.EccCurve.DecodePoint(publicKey);

//userKey.Normalize();

ECPoint c1 = cipher.InitEnc(sm, userKey);

cipher.Encrypt(source);

byte[] c3 = new byte[32];

cipher.Dofinal(c3);

String sc1 = Encoding.Default.GetString(Hex.Encode(c1.GetEncoded()));

String sc2 = Encoding.Default.GetString(Hex.Encode(source));

String sc3 = Encoding.Default.GetString(Hex.Encode(c3));

return (sc1 + sc2 + sc3).ToUpper();

}

#endregion

}

}

 

4.最终调用结果

 

C# 实现SM2国密加密帮助类

 


C# 实现SM2国密加密帮助类

 

希望这边文章能帮助相关人员,欢迎评论+转发,谢谢所有水友们!!!

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>