diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..84f1a1f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,245 @@
+# Visual Studio
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+[Xx]64/
+[Xx]86/
+[Bb]uild/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+*.publishproj
+*.pubxml
+PublishProfiles/
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directory
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# LightSwitch generated files
+GeneratedArtifacts/
+ModelManifest.xml
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+.DS_Store
\ No newline at end of file
diff --git a/SMCryptoUtils/EasyGmUtils.cs b/SMCryptoUtils/EasyGmUtils.cs
new file mode 100644
index 0000000..f954e64
--- /dev/null
+++ b/SMCryptoUtils/EasyGmUtils.cs
@@ -0,0 +1,460 @@
+using System;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.GM;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace SMCryptoUtils
+{
+ [ClassInterface(ClassInterfaceType.None)]
+ public class EasyGmUtils:IEasyGmUtils
+ {
+ private static X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1");
+ private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N);
+
+
+ /**
+ *
+ * @param msg
+ * @param userId
+ * @param privateKey
+ * @return r||s,直接拼接byte数组的rs
+ */
+ public byte[] signSm3WithSm2(byte[] msg, byte[] userId, byte[] privateKeyBytes)
+ {
+ ECPrivateKeyParameters privateKeyParameters = getPrivatekeyFromD(new BigInteger(1, privateKeyBytes));
+ return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKeyParameters));
+ }
+
+
+ /**
+ * @param msg
+ * @param userId
+ * @param privateKey
+ * @return rs in asn1 format
+ */
+ public byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, AsymmetricKeyParameter privateKey)
+ {
+ try
+ {
+ ISigner signer = SignerUtilities.InitSigner("SM3withSM2", true, privateKey, new SecureRandom());
+ signer.BlockUpdate(msg, 0, msg.Length);
+ byte[] sig = signer.GenerateSignature();
+ return sig;
+ }
+ catch (Exception e)
+ {
+ //log.Error("SignSm3WithSm2Asn1Rs error: " + e.Message, e);
+ return null;
+ }
+ }
+
+
+ /**
+ *
+ * @param msg
+ * @param userId
+ * @param rs r||s,直接拼接byte数组的rs
+ * @param publicKey
+ * @return
+ */
+ public bool verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, byte[] publicKeyBytes)
+ {
+ if (rs == null || msg == null || userId == null) return false;
+ if (rs.Length != RS_LEN * 2) return false;
+
+ if (publicKeyBytes.Length != 64 && publicKeyBytes.Length != 65) throw new ArgumentException("err key length");
+ BigInteger x, y;
+ if (publicKeyBytes.Length > 64)
+ {
+ x = fromUnsignedByteArray(publicKeyBytes, 1, 32);
+ y = fromUnsignedByteArray(publicKeyBytes, 33, 32);
+ }
+ else
+ {
+ x = fromUnsignedByteArray(publicKeyBytes, 0, 32);
+ y = fromUnsignedByteArray(publicKeyBytes, 32, 32);
+ }
+ ECPublicKeyParameters publicKey = getPublickeyFromXY(x, y);
+ return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey);
+ }
+
+ public BigInteger fromUnsignedByteArray(byte[] var0, int var1, int var2)
+ {
+ byte[] var3 = var0;
+ if (var1 != 0 || var2 != var0.Length)
+ {
+ var3 = new byte[var2];
+ Array.Copy(var0, var1, var3, 0, var2);
+ }
+
+ return new BigInteger(1, var3);
+ }
+
+ /**
+ *
+ * @param msg
+ * @param userId
+ * @param rs in asn1 format
+ * @param publicKey
+ * @return
+ */
+
+ public bool verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] sign, AsymmetricKeyParameter publicKey)
+ {
+ try
+ {
+ ISigner signer = SignerUtilities.GetSigner("SM3withSM2");
+ signer.Init(false, publicKey);
+ signer.BlockUpdate(msg, 0, msg.Length);
+ return signer.VerifySignature(sign);
+ }
+ catch (Exception e)
+ {
+ //log.Error("VerifySm3WithSm2Asn1Rs error: " + e.Message, e);
+ return false;
+ }
+ }
+
+
+ /**
+ * bc加解密使用旧标c1||c2||c3,此方法在加密后调用,将结果转化为c1||c3||c2
+ * @param c1c2c3
+ * @return
+ */
+ private static byte[] changeC1C2C3ToC1C3C2(byte[] c1c2c3)
+ {
+ int c1Len = (x9ECParameters.Curve.FieldSize + 7) / 8 * 2 + 1; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。
+ const int c3Len = 32; //new SM3Digest().getDigestSize();
+ byte[] result = new byte[c1c2c3.Length];
+ Buffer.BlockCopy(c1c2c3, 0, result, 0, c1Len); //c1
+ Buffer.BlockCopy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3
+ Buffer.BlockCopy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2
+ return result;
+ }
+
+
+ /**
+ * bc加解密使用旧标c1||c3||c2,此方法在解密前调用,将密文转化为c1||c2||c3再去解密
+ * @param c1c3c2
+ * @return
+ */
+ private static byte[] changeC1C3C2ToC1C2C3(byte[] c1c3c2)
+ {
+ int c1Len = (x9ECParameters.Curve.FieldSize + 7) / 8 * 2 + 1; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。
+ const int c3Len = 32; //new SM3Digest().GetDigestSize();
+ byte[] result = new byte[c1c3c2.Length];
+ Buffer.BlockCopy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65
+ Buffer.BlockCopy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2
+ Buffer.BlockCopy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3
+ return result;
+ }
+
+ /**
+ * c1||c3||c2
+ * @param data
+ * @param key
+ * @return
+ */
+ public byte[] sm2Decrypt(byte[] data, AsymmetricKeyParameter key)
+ {
+ return sm2DecryptOld(changeC1C3C2ToC1C2C3(data), key);
+ }
+
+ /**
+ * c1||c3||c2
+ * @param data
+ * @param key
+ * @return
+ */
+
+ public byte[] sm2Encrypt(byte[] data, AsymmetricKeyParameter key)
+ {
+ return changeC1C2C3ToC1C3C2(sm2EncryptOld(data, key));
+ }
+
+ /**
+ * c1||c2||c3
+ * @param data
+ * @param key
+ * @return
+ */
+ public byte[] sm2EncryptOld(byte[] data, AsymmetricKeyParameter pubkey)
+ {
+ try
+ {
+ SM2Engine sm2Engine = new SM2Engine();
+ sm2Engine.Init(true, new ParametersWithRandom(pubkey, new SecureRandom()));
+ return sm2Engine.ProcessBlock(data, 0, data.Length);
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm2EncryptOld error: " + e.Message, e);
+ return null;
+ }
+ }
+
+ /**
+ * c1||c2||c3
+ * @param data
+ * @param key
+ * @return
+ */
+ public byte[] sm2DecryptOld(byte[] data, AsymmetricKeyParameter key)
+ {
+ try
+ {
+ SM2Engine sm2Engine = new SM2Engine();
+ sm2Engine.Init(false, key);
+ return sm2Engine.ProcessBlock(data, 0, data.Length);
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm2DecryptOld error: " + e.Message, e);
+ return null;
+ }
+ }
+
+ /**
+ * @param bytes
+ * @return
+ */
+ public byte[] sm3(byte[] bytes)
+ {
+ try
+ {
+ SM3Digest digest = new SM3Digest();
+ digest.BlockUpdate(bytes, 0, bytes.Length);
+ byte[] result = DigestUtilities.DoFinal(digest);
+ return result;
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm3 error: " + e.Message, e);
+ return null;
+ }
+ }
+
+ private const int RS_LEN = 32;
+
+ private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS)
+ {
+ // for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123,
+ // r and s are the result of mod n, so they should be less than n and have length<=32
+ byte[] rs = rOrS.ToByteArray();
+ if (rs.Length == RS_LEN) return rs;
+ else if (rs.Length == RS_LEN + 1 && rs[0] == 0) return Arrays.CopyOfRange(rs, 1, RS_LEN + 1);
+ else if (rs.Length < RS_LEN)
+ {
+ byte[] result = new byte[RS_LEN];
+ Arrays.Fill(result, (byte)0);
+ Buffer.BlockCopy(rs, 0, result, RS_LEN - rs.Length, rs.Length);
+ return result;
+ }
+ else
+ {
+ throw new ArgumentException("err rs: " + Hex.ToHexString(rs));
+ }
+ }
+
+ /**
+ * BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s
+ * @param rsDer rs in asn1 format
+ * @return sign result in plain byte array
+ */
+ private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer)
+ {
+ Asn1Sequence seq = Asn1Sequence.GetInstance(rsDer);
+ byte[] r = bigIntToFixexLengthBytes(DerInteger.GetInstance(seq[0]).Value);
+ byte[] s = bigIntToFixexLengthBytes(DerInteger.GetInstance(seq[1]).Value);
+ byte[] result = new byte[RS_LEN * 2];
+ Buffer.BlockCopy(r, 0, result, 0, r.Length);
+ Buffer.BlockCopy(s, 0, result, RS_LEN, s.Length);
+ return result;
+ }
+
+ /**
+ * BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式
+ * @param sign in plain byte array
+ * @return rs result in asn1 format
+ */
+ private static byte[] rsPlainByteArrayToAsn1(byte[] sign)
+ {
+ if (sign.Length != RS_LEN * 2) throw new ArgumentException("err rs. ");
+ BigInteger r = new BigInteger(1, Arrays.CopyOfRange(sign, 0, RS_LEN));
+ BigInteger s = new BigInteger(1, Arrays.CopyOfRange(sign, RS_LEN, RS_LEN * 2));
+ Asn1EncodableVector v = new Asn1EncodableVector();
+ v.Add(new DerInteger(r));
+ v.Add(new DerInteger(s));
+ try
+ {
+ return new DerSequence(v).GetEncoded("DER");
+ }
+ catch (IOException e)
+ {
+ //log.Error("RsPlainByteArrayToAsn1 error: " + e.Message, e);
+ return null;
+ }
+ }
+
+ public byte[] sm4DecryptCBC(byte[] keyBytes, byte[] cipher, byte[] iv, String algo)
+ {
+ if (keyBytes.Length != 16) throw new ArgumentException("err key length");
+ if (cipher.Length % 16 != 0) throw new ArgumentException("err data length");
+
+ try
+ {
+ KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+ IBufferedCipher c = CipherUtilities.GetCipher(algo);
+ if (iv == null) iv = zeroIv(algo);
+ c.Init(false, new ParametersWithIV(key, iv));
+ return c.DoFinal(cipher);
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm4DecryptCBC error: " + e.Message, e);
+ return null;
+ }
+ }
+
+
+ public byte[] sm4EncryptCBC(byte[] keyBytes, byte[] plain, byte[] iv, String algo)
+ {
+ if (keyBytes.Length != 16) throw new ArgumentException("err key length");
+ if (plain.Length % 16 != 0) throw new ArgumentException("err data length");
+
+ try
+ {
+ KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+ IBufferedCipher c = CipherUtilities.GetCipher(algo);
+ if (iv == null) iv = zeroIv(algo);
+ c.Init(true, new ParametersWithIV(key, iv));
+ return c.DoFinal(plain);
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm4EncryptCBC error: " + e.Message, e);
+ return null;
+ }
+ }
+
+
+ public byte[] sm4EncryptECB(byte[] keyBytes, byte[] plain, string algo)
+ {
+ if (keyBytes.Length != 16) throw new ArgumentException("err key length");
+ if (plain.Length % 16 != 0) throw new ArgumentException("err data length");
+
+ try
+ {
+ KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+ IBufferedCipher c = CipherUtilities.GetCipher(algo);
+ c.Init(true, key);
+ return c.DoFinal(plain);
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm4EncryptECB error: " + e.Message, e);
+ return null;
+ }
+ }
+
+ public byte[] sm4DecryptECB(byte[] keyBytes, byte[] cipher, string algo)
+ {
+ if (keyBytes.Length != 16) throw new ArgumentException("err key length");
+ if (cipher.Length % 16 != 0) throw new ArgumentException("err data length");
+
+ try
+ {
+ KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+ IBufferedCipher c = CipherUtilities.GetCipher(algo);
+ c.Init(false, key);
+ return c.DoFinal(cipher);
+ }
+ catch (Exception e)
+ {
+ //log.Error("Sm4DecryptECB error: " + e.Message, e);
+ return null;
+ }
+ }
+
+
+ public ECPrivateKeyParameters getPrivatekeyFromD(BigInteger d)
+ {
+ return new ECPrivateKeyParameters(d, ecDomainParameters);
+ }
+
+ public ECPublicKeyParameters getPublickeyFromXY(BigInteger x, BigInteger y)
+ {
+ return new ECPublicKeyParameters(x9ECParameters.Curve.CreatePoint(x, y), ecDomainParameters);
+ }
+
+ public byte[] sm4Encrypt(byte[] keyBytes, byte[] plain)
+ {
+ if (keyBytes.Length != 16) throw new ArgumentException("err key length");
+ // if (plain.length % 16 != 0) throw new RuntimeException("err data length");
+
+ try
+ {
+ KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+ IBufferedCipher c = CipherUtilities.GetCipher("SM4/ECB/PKCS7Padding");
+ c.Init(true, key);
+
+ return c.DoFinal(plain);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ public byte[] sm4Decrypt(byte[] keyBytes, byte[] cipher)
+ {
+ // if (keyBytes.length != 16) throw new RuntimeException("err key length");
+ if (cipher.Length % 16 != 0) throw new ArgumentException("err data length");
+
+ try
+ {
+ KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
+ IBufferedCipher c = CipherUtilities.GetCipher("SM4/ECB/PKCS7Padding");
+ c.Init(false, key);
+ return c.DoFinal(cipher);
+
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ public const String SM4_ECB_NOPADDING = "SM4/ECB/NoPadding";
+ public const String SM4_CBC_NOPADDING = "SM4/CBC/NoPadding";
+ public const String SM4_CBC_PKCS7PADDING = "SM4/CBC/PKCS7Padding";
+
+ public byte[] zeroIv(String algo)
+ {
+
+ try
+ {
+ IBufferedCipher cipher = CipherUtilities.GetCipher(algo);
+ int blockSize = cipher.GetBlockSize();
+ byte[] iv = new byte[blockSize];
+ Arrays.Fill(iv, (byte)0);
+ return iv;
+ }
+ catch (Exception e)
+ {
+ //log.Error("ZeroIv error: " + e.Message, e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/SMCryptoUtils/IEasyGmUtils.cs b/SMCryptoUtils/IEasyGmUtils.cs
new file mode 100644
index 0000000..b1aa095
--- /dev/null
+++ b/SMCryptoUtils/IEasyGmUtils.cs
@@ -0,0 +1,34 @@
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SMCryptoUtils
+{
+ public interface IEasyGmUtils
+ {
+ BigInteger fromUnsignedByteArray(byte[] var0, int var1, int var2);
+ ECPrivateKeyParameters getPrivatekeyFromD(BigInteger d);
+ ECPublicKeyParameters getPublickeyFromXY(BigInteger x, BigInteger y);
+ byte[] signSm3WithSm2(byte[] msg, byte[] userId, byte[] privateKeyBytes);
+ byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, AsymmetricKeyParameter privateKey);
+ byte[] sm2Decrypt(byte[] data, AsymmetricKeyParameter key);
+ byte[] sm2DecryptOld(byte[] data, AsymmetricKeyParameter key);
+ byte[] sm2Encrypt(byte[] data, AsymmetricKeyParameter key);
+ byte[] sm2EncryptOld(byte[] data, AsymmetricKeyParameter pubkey);
+ byte[] sm3(byte[] bytes);
+ byte[] sm4Decrypt(byte[] keyBytes, byte[] cipher);
+ byte[] sm4DecryptCBC(byte[] keyBytes, byte[] cipher, byte[] iv, string algo);
+ byte[] sm4DecryptECB(byte[] keyBytes, byte[] cipher, string algo);
+ byte[] sm4Encrypt(byte[] keyBytes, byte[] plain);
+ byte[] sm4EncryptCBC(byte[] keyBytes, byte[] plain, byte[] iv, string algo);
+ byte[] sm4EncryptECB(byte[] keyBytes, byte[] plain, string algo);
+ bool verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, byte[] publicKeyBytes);
+ bool verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] sign, AsymmetricKeyParameter publicKey);
+ byte[] zeroIv(string algo);
+ }
+}
diff --git a/SMCryptoUtils/ISMUtil.cs b/SMCryptoUtils/ISMUtil.cs
new file mode 100644
index 0000000..7f8983c
--- /dev/null
+++ b/SMCryptoUtils/ISMUtil.cs
@@ -0,0 +1,17 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SMCryptoUtils
+{
+ public interface ISMUtil
+ {
+ string decrypt(string data, string appId, string appSecret);
+ string encrypt(string data, string appId, string appSecret);
+ string sign(JObject jsonObject, string appSecret, string privateKey);
+ bool verify(JObject jsonObject, string appSecret, string publicKey, string responseSign);
+ }
+}
diff --git a/SMCryptoUtils/ISignUtil.cs b/SMCryptoUtils/ISignUtil.cs
new file mode 100644
index 0000000..0e16d56
--- /dev/null
+++ b/SMCryptoUtils/ISignUtil.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SMCryptoUtils
+{
+ public interface ISignUtil
+ {
+ string getObjString(object obj);
+ string getSignText(JObject jsonObject, string appSecret);
+ }
+}
diff --git a/SMCryptoUtils/Properties/AssemblyInfo.cs b/SMCryptoUtils/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8f2f4bc
--- /dev/null
+++ b/SMCryptoUtils/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("SMCryptoUtils")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SMCryptoUtils")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(true)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("164f1097-1277-42dd-a4ad-dc9bd59be320")]
+
+// 程序集的版本信息由下列四个值组成:
+//
+// 主版本
+// 次版本
+// 生成号
+// 修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SMCryptoUtils/SMCryptoUtils.csproj b/SMCryptoUtils/SMCryptoUtils.csproj
new file mode 100644
index 0000000..52ff29f
--- /dev/null
+++ b/SMCryptoUtils/SMCryptoUtils.csproj
@@ -0,0 +1,63 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {164F1097-1277-42DD-A4AD-DC9BD59BE320}
+ Library
+ Properties
+ SMCryptoUtils
+ SMCryptoUtils
+ v4.7.2
+ 512
+ true
+
+
+ true
+ full
+ true
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 9.0.1
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SMCryptoUtils/SMCryptoUtils.sln b/SMCryptoUtils/SMCryptoUtils.sln
new file mode 100644
index 0000000..0fc1985
--- /dev/null
+++ b/SMCryptoUtils/SMCryptoUtils.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32014.148
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMCryptoUtils", "SMCryptoUtils.csproj", "{164F1097-1277-42DD-A4AD-DC9BD59BE320}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMCryptoUtilsTest", "..\SMCryptoUtilsTest\SMCryptoUtilsTest.csproj", "{8B808C3C-450E-4771-AB15-11B980CEC34B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {164F1097-1277-42DD-A4AD-DC9BD59BE320}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {164F1097-1277-42DD-A4AD-DC9BD59BE320}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {164F1097-1277-42DD-A4AD-DC9BD59BE320}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {164F1097-1277-42DD-A4AD-DC9BD59BE320}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8B808C3C-450E-4771-AB15-11B980CEC34B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B808C3C-450E-4771-AB15-11B980CEC34B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B808C3C-450E-4771-AB15-11B980CEC34B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B808C3C-450E-4771-AB15-11B980CEC34B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E73D7C28-BBBC-4505-A75C-E46856110DA8}
+ EndGlobalSection
+EndGlobal
diff --git a/SMCryptoUtils/SMUtil.cs b/SMCryptoUtils/SMUtil.cs
new file mode 100644
index 0000000..95e0faf
--- /dev/null
+++ b/SMCryptoUtils/SMUtil.cs
@@ -0,0 +1,91 @@
+using System;
+using Org.BouncyCastle.Utilities.Encoders;
+using System.Text;
+using Newtonsoft.Json.Linq;
+using System.Runtime.InteropServices;
+
+namespace SMCryptoUtils
+{
+
+ [ClassInterface(ClassInterfaceType.None)]
+ public class SMUtil:ISMUtil
+ {
+
+ private readonly static EasyGmUtils easyGmUtils = new EasyGmUtils();
+
+ private readonly static SignUtil signUtil = new SignUtil();
+
+ /**
+ * 加密
+ *
+ * @param data
+ * @param appId
+ * @param appSecret
+ * @return
+ */
+ public string encrypt(string data, string appId, string appSecret)
+ {
+ //加密流程
+ //用appId加密appSecret获取新秘钥
+ byte[] appSecretEncData = easyGmUtils.sm4Encrypt(Encoding.UTF8.GetBytes(appId.Substring(0, 16)), Encoding.UTF8.GetBytes(appSecret));
+ //新秘钥串
+ byte[] secKey = Encoding.UTF8.GetBytes(Hex.ToHexString(appSecretEncData).ToUpper().Substring(0, 16));
+ //加密0数据
+ string encryptDataStr = Hex.ToHexString(easyGmUtils.sm4Encrypt(secKey, Encoding.UTF8.GetBytes(data))).ToUpper();
+ return encryptDataStr;
+ }
+
+ /**
+ * 解密
+ *
+ * @param data
+ * @param appId
+ * @param appSecret
+ * @return
+ */
+ public string decrypt(string data, string appId, string appSecret)
+ {
+ byte[] appSecretEncDataDecode = easyGmUtils.sm4Encrypt(Encoding.UTF8.GetBytes(appId.Substring(0, 16)), Encoding.UTF8.GetBytes(appSecret));
+ byte[] secKeyDecode = Encoding.UTF8.GetBytes(Hex.ToHexString(appSecretEncDataDecode).ToUpper().Substring(0, 16));
+ string decryptDataStr = Encoding.UTF8.GetString(easyGmUtils.sm4Decrypt(secKeyDecode, Hex.Decode(data)));
+ return decryptDataStr;
+ }
+
+ /**
+ * 签名
+ *
+ * @param jsonObject
+ * @param appSecret
+ * @param privateKey
+ * @return
+ */
+ public string sign(JObject jsonObject, string appSecret, string privateKey)
+ {
+ // 获取签名串
+ byte[] signText = Encoding.UTF8.GetBytes(signUtil.getSignText(jsonObject, appSecret));
+ byte[] userId = Encoding.UTF8.GetBytes(appSecret);
+ byte[] prvkey = Base64.Decode(privateKey);
+ string responseSign = Base64.ToBase64String(easyGmUtils.signSm3WithSm2(signText, userId, prvkey));
+ return responseSign;
+ }
+
+ /**
+ * 验签
+ *
+ * @param jsonObject
+ * @param appSecret
+ * @param publicKey
+ * @param responseSign
+ * @return
+ */
+ public Boolean verify(JObject jsonObject, string appSecret, string publicKey, string responseSign)
+ {
+ //验签
+ byte[] msg = Encoding.UTF8.GetBytes(signUtil.getSignText(jsonObject, appSecret));
+ byte[] userIdDecode = Encoding.UTF8.GetBytes(appSecret);
+ byte[] pubkey = Base64.Decode(publicKey);
+ byte[] signData = Base64.Decode(responseSign);
+ return easyGmUtils.verifySm3WithSm2(msg, userIdDecode, signData, pubkey);
+ }
+ }
+}
diff --git a/SMCryptoUtils/SignUtil.cs b/SMCryptoUtils/SignUtil.cs
new file mode 100644
index 0000000..6a880f5
--- /dev/null
+++ b/SMCryptoUtils/SignUtil.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace SMCryptoUtils
+{
+ [ClassInterface(ClassInterfaceType.None)]
+ public class SignUtil:ISignUtil
+ {
+ private static List ignoreSign = new List() { "signData", "encData", "extra" };
+
+ public string getSignText(JObject jsonObject, string appSecret)
+ {
+ SortedDictionary signMap = new SortedDictionary(StringComparer.Ordinal);
+
+ foreach (var entry in jsonObject)
+ {
+ if (!string.IsNullOrEmpty(entry.Value.ToString()) && !ignoreSign.Contains(entry.Key))
+ {
+ signMap.Add(entry.Key, getValue(entry.Value));
+ }
+ }
+
+
+ List list = new List();
+
+ foreach (var entry in signMap)
+ {
+ if (!string.IsNullOrEmpty(getObjString(entry.Value)))
+ {
+ list.Add((string)entry.Key + "=" + (string)entry.Value + "&");
+ }
+ }
+
+ int size = list.Count();
+ string[] arrayToSort = (string[])list.ToArray();
+ Array.Sort(arrayToSort, new CaseInsensitiveComparer());
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < size; ++i)
+ {
+ sb.Append(arrayToSort[i]);
+ }
+
+ string signText = sb.Append("key=").Append(appSecret).ToString();
+ return signText;
+ }
+
+ public string getObjString(Object obj)
+ {
+ return obj == null ? "" : (string)obj;
+ }
+
+ private string getValue(Object value)
+ {
+ return value is string ? getObjString(value) : treeJsonParam(value);
+ }
+
+ private static string treeJsonParam(Object value)
+ {
+ string jsonParam = null;
+ if (value is Dictionary