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) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + Dictionary nestedMap = (Dictionary)value; + + foreach (var entry in nestedMap) + { + treeNestedMap.Add(entry.Key.ToString(), entry.Value); + } + jsonParam = JsonConvert.SerializeObject(treeParams(treeNestedMap), Formatting.None); + } + else if (value is List) + { + List ar = (List)value; + jsonParam = JsonConvert.SerializeObject(treeList(ar), Formatting.None); + } + else if (value is JObject) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + JObject nestedMap = (JObject)value; + foreach (var entry in nestedMap) + { + treeNestedMap.Add(entry.Key.ToString(), entry.Value); + } + jsonParam = JsonConvert.SerializeObject(treeParams(treeNestedMap), Formatting.None); + } + else if (value is JArray) + { + JArray jarr = (JArray)value; + jsonParam = JsonConvert.SerializeObject(treeJsonArray(jarr), Formatting.None); + } + else if (value is JValue) + { + JValue jval = (JValue)value; + if (jval != null) + jsonParam = jval.Value.ToString(); + + } + else if (value is JProperty) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + JProperty nestedMap = (JProperty)value; + treeNestedMap.Add(nestedMap.Name, nestedMap.Value); + jsonParam = JsonConvert.SerializeObject(treeParams(treeNestedMap), Formatting.None); + } + else + { + jsonParam = value.ToString(); + } + + return jsonParam; + } + + private static SortedDictionary treeParams(SortedDictionary param) + { + if (param == null) + { + return new SortedDictionary(StringComparer.Ordinal); + } + else + { + SortedDictionary treeParam = new SortedDictionary(StringComparer.Ordinal); + + while (true) + { + foreach (var entry in param) + { + + string key = (string)entry.Key; + Object value = entry.Value; + if (value is Dictionary) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + Dictionary nestedMap = (Dictionary)value; + + foreach (var nestedEntry in nestedMap) + { + treeNestedMap.Add(nestedEntry.Key.ToString(), nestedEntry.Value); + } + + treeParam.Add(key, treeParams(treeNestedMap)); + } + else if (value is List) + { + List ar = (List)value; + treeParam.Add(key, treeList(ar)); + } + else if (value is JArray) + { + JArray ar = (JArray)value; + treeParam.Add(key, treeJsonArray(ar)); + } + else if (value is JObject) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + JObject nestedMap = (JObject)value; + foreach (var nestedEntry in nestedMap) + { + treeNestedMap.Add(nestedEntry.Key.ToString(), nestedEntry.Value); + } + treeParam.Add(key, treeParams(treeNestedMap)); + } + else if (value is JValue) + { + JValue jval = (JValue)value; + if (jval != null && !string.IsNullOrEmpty(jval.ToString())) + treeParam.Add(key, jval.ToString()); + } + else if (value is JProperty) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + JProperty nestedMap = (JProperty)value; + treeNestedMap.Add(nestedMap.Name, nestedMap.Value); + treeParam.Add(key, treeParams(treeNestedMap)); + } + else if (!"".Equals(value) && value != null) + { + treeParam.Add(key, value.ToString()); + } + } + return treeParam; + } + } + } + + private static List treeList(List list) + { + if (list != null && list.Count() != 0) + { + JArray jsonArray = new JArray(); + int size = list.Count(); + + for (int i = 0; i < size; ++i) + { + jsonArray.Add(list[i]); + } + + return treeJsonArray(jsonArray); + } + else + { + return null; + } + } + + private static List treeJsonArray(JArray jarr) + { + if (jarr != null && jarr.Count() != 0) + { + List jsonArray = new List(); + int size = jarr.Count(); + + for (int i = 0; i < size; ++i) + { + Object value = jarr[i]; + if (value is List) + { + List ar = (List)value; + jsonArray.Add(treeList(ar)); + } + else if (value is JArray) + { + JArray ar = (JArray)value; + jsonArray.Add(treeJsonArray(ar)); + } + else if (value is JObject) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + JObject nestedMap = (JObject)value; + foreach (var nestedEntry in nestedMap) + { + treeNestedMap.Add(nestedEntry.Key.ToString(), nestedEntry.Value); + } + jsonArray.Add(treeParams(treeNestedMap)); + + } + else if (value is JValue) + { + JValue jval = (JValue)value; + if (jval != null && !string.IsNullOrEmpty(jval.ToString())) + jsonArray.Add(jval.ToString()); + } + else if (value is JProperty) + { + SortedDictionary treeNestedMap = new SortedDictionary(StringComparer.Ordinal); + JProperty nestedMap = (JProperty)value; + treeNestedMap.Add(nestedMap.Name, nestedMap.Value); + jsonArray.Add(treeParams(treeNestedMap)); + } + else if (!"".Equals(value)) + { + jsonArray.Add(value.ToString()); + } + + } + + return jsonArray; + } + else + { + return null; + } + } + } +} diff --git a/SMCryptoUtilsTest/App.config b/SMCryptoUtilsTest/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/SMCryptoUtilsTest/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SMCryptoUtilsTest/Program.cs b/SMCryptoUtilsTest/Program.cs new file mode 100644 index 0000000..34cbf78 --- /dev/null +++ b/SMCryptoUtilsTest/Program.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SMCryptoUtils; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace SMCryptoUtilsTest +{ + internal class Program + { + static void Main(string[] args) + { + + //应用ID + String appId = "57E2D561E97141A3871DEFB410ADD920"; + //应用秘钥 + String appSecret = "F30D0D261BA04063A0BCAEF19F8ADBCC"; + //应用私钥 + String privateKey = "AKyhfvPbT+tIJHJkZSDILrw7t+FOG1U58UcupYYTzCOj"; + //平台公钥(为了测试用例能验签成功,平台公钥当前赋值为应用公钥的值,应用公钥存放于处方中心不对外提供,实际上拿到的平台公钥是用于验签处方中心返回的数据,不能用于此demo) + String publicKey = "BIFd2+2CgjuPAj5FMj5L/L3azTWu86suPtlIJkCo8zjQ44R7SQUUkTgZGdVelRQCM5pW+x9tZGzDPaUNbfD499w="; + + JObject requestData = new JObject(); + requestData.Add("appId", appId); + requestData.Add("encType", "SM4"); + requestData.Add("signType", "SM2"); + requestData.Add("timestamp", (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds); + + JObject data = new JObject(); + // data.Add("userName", "张三"); + /***********说明:此次data对象替换为实际传递的业务数据结构,以下结构仅供测试参考。***********/ + data = JObject.Parse("{\n" + + " \"insuPlcNo\": \"350100\",\n" + + " \"hospRxno\": \"3504874534935555553312259665232132\",\n" + + " \"prscTime\": \"2022-06-06 10:10:00\",\n" + + " \"valiDays\": 1,\n" + + " \"valiEndTime\": \"2022-06-07 10:10:00\",\n" + + " \"authNo\": \"\",\n" + + " \"bizTypeCode\": \"01\",\n" + + " \"diseinfo\": [\n" + + " {\n" + + " \"diagCode\": \"E11.900\",\n" + + " \"diagDept\": \"心血管内科\",\n" + + " \"diagDrName\": \"吴丽雅\",\n" + + " \"diagDrNo\": \"PTYS0000818231\",\n" + + " \"diagName\": \"急性胃炎\",\n" + + " \"diagSrtNo\": \"1\",\n" + + " \"diagTime\": \"2022-04-07 10:10:00\",\n" + + " \"diagType\": \"1\",\n" + + " \"maindiagFlag\": \"1\",\n" + + " \"tcmDiseCode\": \"\",\n" + + " \"tcmDiseName\": \"\",\n" + + " \"tcmsymp\": \"\",\n" + + " \"tcmsympCode\": \"\"\n" + + " }\n" + + " ],\n" + + " \"ecToken\": \"\",\n" + + " \"initRxno\": \"\",\n" + + " \"longRxFlag\": \"1\",\n" + + " \"maxReptCnt\": 0,\n" + + " \"mdtrtCertType\": \"02\",\n" + + " \"mdtrtCertNo\": \"110101198811090049\",\n" + + " \"cardSn\":\"\",\n" + + " \"mdtrtinfo\": {\n" + + " \"algsHis\": \"注射过敏\",\n" + + " \"certno\": \"110101198811090049\",\n" + + " \"diseCodg\": \"\",\n" + + " \"diseCondDscr\": \"\",\n" + + " \"diseName\": \"\",\n" + + " \"drCode\": \"YS9083982313\",\n" + + " \"drDeptCode\": \"KSBM89981\",\n" + + " \"drDeptName\": \"外科\",\n" + + " \"drProfttlCodg\": \"ZCBM29931\",\n" + + " \"drProfttlName\": \"主任医师\",\n" + + " \"extras\": {\n" + + " \"1\": \"1\"\n" + + " },\n" + + " \"fixmedinsCode\": \"H35010200364\",\n" + + " \"fixmedinsName\": \"福建中医药大学附属第二人民医院\",\n" + + " \"fstdiagFlag\": \"0\",\n" + + " \"gend\": \"1\",\n" + + " \"gesoVal\": 0,\n" + + " \"hiFeesetlName\": \"本地医疗保险\",\n" + + " \"hiFeesetlType\": \"2\",\n" + + " \"insutype\": \"310\",\n" + + " \"iptOtpNo\": \"ZYMZ8909232131231\",\n" + + " \"maindiagCode\": \"E11.900\",\n" + + " \"maindiagName\": \"急性胃炎\",\n" + + " \"mdtrtId\": \"JZID888888\",\n" + + " \"mdtrtTime\": \"2022-05-19 14:10:00\",\n" + + " \"medType\": \"11\",\n" + + " \"medTypeName\": \"普通门诊\",\n" + + " \"medfeeSumamt\": 36.88,\n" + + " \"nwbAge\": \"\",\n" + + " \"nwbFlag\": \"0\",\n" + + " \"otpIptFlag\": \"1\",\n" + + " \"patnAge\": 27,\n" + + " \"patnHgt\": 180,\n" + + " \"patnName\": \"张三三\",\n" + + " \"patnWt\": 80,\n" + + " \"pharCertType\": \"01\",\n" + + " \"pharCertno\": \"350401198510101010\",\n" + + " \"pharChkTime\": \"2022-04-07 10:10:01\",\n" + + " \"pharCode\": \"PTYS000088232\",\n" + + " \"pharDeptCode\": \"KS989831\",\n" + + " \"pharDeptName\": \"内科\",\n" + + " \"pharName\": \"林玉英\",\n" + + " \"pharPracCertNo\": \"YSBH99032313\",\n" + + " \"pharProfttlCodg\": \"01\",\n" + + " \"pharProfttlName\": \"高级药师\",\n" + + " \"prscDeptCode\": \"KFKS98231\",\n" + + " \"prscDeptName\": \"内科\",\n" + + " \"prscDrCertType\": \"01\",\n" + + " \"prscDrCertno\": \"350422196510101010\",\n" + + " \"prscDrName\": \"吴丽雅\",\n" + + " \"psnCertType\": \"01\",\n" + + " \"psnNo\": \"RYBH38384\",\n" + + " \"rgstFee\": 15.88,\n" + + " \"spDiseFlag\": \"1\",\n" + + " \"suckPrdFlag\": \"0\"\n" + + " },\n" + + " \"minInrvDays\": 2,\n" + + " \"reptFlag\": \"0\",\n" + + " \"reptdCnt\": 0,\n" + + " \"rxCotnFlag\": \"0\",\n" + + " \"rxDoscnt\": 1,\n" + + " \"rxDosePrdDays\": 3,\n" + + " \"rxDosunt\": \"包\",\n" + + " \"rxDrordDscr\": \"多喝水\",\n" + + " \"rxDrugCnt\": 1,\n" + + " \"rxFrquCodg\": \"13\",\n" + + " \"rxFrquName\": \"每天三次(tid)\",\n" + + " \"rxTypeCode\": \"1\",\n" + + " \"rxUsedWayCodg\": \"1\",\n" + + " \"rxUsedWayName\": \"口服\",\n" + + " \"rxdrugdetail\": [\n" + + " {\n" + + " \"basMednFlag\": \"1\",\n" + + " \"chemname\": \"恩替卡韦口服溶液\",\n" + + " \"diseCodg\": \"BZML000000001\",\n" + + " \"drugCnt\": 1,\n" + + " \"drugDosform\": \"片剂\",\n" + + " \"drugDosunt\": \"盒\",\n" + + " \"drugGenname\": \"恩替卡韦口服溶液\",\n" + + " \"drugPric\": 1.5,\n" + + " \"drugProdname\": \"恩替卡韦口服溶液\",\n" + + " \"drugSpec\": \"1支/盒\",\n" + + " \"drugSumamt\": 27,\n" + + " \"drugTotlcnt\": \"1\",\n" + + " \"drugTotlcntEmp\": \"盒\",\n" + + " \"drugstdcode\": \"BWM99-03131\",\n" + + " \"extras\": {},\n" + + " \"fixmedinsHilistId\": \"\",\n" + + " \"hospApprFlag\": \"1\",\n" + + " \"hospPrepFlag\": \"0\",\n" + + " \"impDrugFlag\": \"0\",\n" + + " \"mainMedcFlag\": \"\",\n" + + " \"medListCodg\": \"XJ05AFE013X001010108569\",\n" + + " \"medcBegntime\": \"2022-04-08 10:10:00\",\n" + + " \"medcDays\": 0,\n" + + " \"medcEndtime\": \"2022-04-08 10:10:00\",\n" + + " \"medcWayCodg\": \"1\",\n" + + " \"medcWayDscr\": \"口服\",\n" + + " \"mednTypeCode\": \"0100\",\n" + + " \"mednTypeName\": \"抗生素类抗感染药物\",\n" + + " \"prdrName\": \"成都第一制药有限公司\",\n" + + " \"prodBarc\": \"111111111\",\n" + + " \"rxItemTypeCode\": \"11\",\n" + + " \"rxItemTypeName\": \"西药\",\n" + + " \"sinDoscnt\": 2,\n" + + " \"sinDosunt\": \"片\",\n" + + " \"tcmdrugTypeCode\": \"1\",\n" + + " \"tcmdrugTypeName\": \"\",\n" + + " \"tcmherbFoote\": \"\",\n" + + " \"urgtFlag\": \"\",\n" + + " \"usedFrquCodg\": \"13\",\n" + + " \"usedFrquName\": \"每天三次(tid)\"\n" + + " }\n" + + " ]\n" + + "}"); + + SMUtil sMUtil = new SMUtil(); + + //加密 + String encData = sMUtil.encrypt(JsonConvert.SerializeObject(data, Formatting.None), appId, appSecret); + Console.WriteLine("加密:" + encData); + + JObject signDto = (JObject)JObject.Parse(JsonConvert.SerializeObject(requestData, Formatting.None)); + signDto.Add("data", data); + + //加签 + String signData = sMUtil.sign(signDto, appSecret, privateKey); + Console.WriteLine("加签:" + signData); + //报文 + requestData.Add("encData", encData); + requestData.Add("signData", signData); + Console.WriteLine("报文:" + JsonConvert.SerializeObject(requestData, Formatting.None)); + //解密 + String decData = sMUtil.decrypt(encData, appId, appSecret); + Console.WriteLine("解密:" + decData); + //验签 + Boolean isVerify = sMUtil.verify(signDto, appSecret, publicKey, signData); + Console.WriteLine("验签:" + isVerify); + Console.ReadKey(); + } + } +} diff --git a/SMCryptoUtilsTest/Properties/AssemblyInfo.cs b/SMCryptoUtilsTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c53b44e --- /dev/null +++ b/SMCryptoUtilsTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("SMCryptoUtilsTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SMCryptoUtilsTest")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("8b808c3c-450e-4771-ab15-11b980cec34b")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SMCryptoUtilsTest/SMCryptoUtilsTest.csproj b/SMCryptoUtilsTest/SMCryptoUtilsTest.csproj new file mode 100644 index 0000000..c2cbbd5 --- /dev/null +++ b/SMCryptoUtilsTest/SMCryptoUtilsTest.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {8B808C3C-450E-4771-AB15-11B980CEC34B} + Exe + SMCryptoUtilsTest + SMCryptoUtilsTest + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + E:\DingDownload\医保局加解密方法工具类及调用demo(医保电子处方中心)V1.0\医保局加解密方法工具类及调用demo(医保电子处方中心)V1.0\C#\SMCryptoUtils\SMCryptoUtils\bin\Debug\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + {164f1097-1277-42dd-a4ad-dc9bd59be320} + SMCryptoUtils + + + + \ No newline at end of file diff --git a/dll/BouncyCastle.Crypto.dll b/dll/BouncyCastle.Crypto.dll new file mode 100644 index 0000000..9059e64 Binary files /dev/null and b/dll/BouncyCastle.Crypto.dll differ diff --git a/dll/Newtonsoft.Json.dll b/dll/Newtonsoft.Json.dll new file mode 100644 index 0000000..7af125a Binary files /dev/null and b/dll/Newtonsoft.Json.dll differ