Browse Source

RSA支持PEM

懒得勤快 5 years ago
parent
commit
7408933592

+ 1 - 1
Masuit.Tools.AspNetCore.ResumeFileResults.WebTest/Masuit.Tools.AspNetCore.ResumeFileResults.WebTest.csproj

@@ -24,7 +24,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
-    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.3.1" />
+    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.4.1" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 1
Masuit.Tools.Core.UnitTest/Masuit.Tools.Core.UnitTest.csproj

@@ -11,7 +11,7 @@
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.3" />
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.3" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
       <PrivateAssets>all</PrivateAssets>

+ 3 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>netstandard2.1</TargetFramework>
-    <Version>2.2.8.8</Version>
+    <Version>2.2.8.9</Version>
     <Authors>懒得勤快</Authors>
     <Company>masuit.com</Company>
     <Description>包含一些常用的操作类,大都是静态类,加密解密,反射操作,硬件信息,字符串扩展方法,日期时间扩展操作,大文件拷贝,图像裁剪,html处理,验证码、NoSql等常用封装。
@@ -113,7 +113,9 @@ github:https://github.com/ldqk/Masuit.Tools</Description>
     <Compile Include="..\Masuit.Tools\Reflection\ReflectionUtil.cs" Link="Reflection\ReflectionUtil.cs" />
     <Compile Include="..\Masuit.Tools\Security\Encrypt.cs" Link="Security\Encrypt.cs" />
     <Compile Include="..\Masuit.Tools\Security\HashEncode.cs" Link="Security\HashEncode.cs" />
+    <Compile Include="..\Masuit.Tools\Security\RSA.cs" Link="Security\RSA.cs" />
     <Compile Include="..\Masuit.Tools\Security\RSACrypt.cs" Link="Security\RSACrypt.cs" />
+    <Compile Include="..\Masuit.Tools\Security\RsaPem.cs" Link="Security\RsaPem.cs" />
     <Compile Include="..\Masuit.Tools\Strings\ChineseString.cs" Link="Strings\ChineseString.cs" />
     <Compile Include="..\Masuit.Tools\Strings\NumberFormater.cs" Link="Strings\NumberFormater.cs" />
     <Compile Include="..\Masuit.Tools\Strings\Template.cs" Link="Strings\Template.cs" />

+ 2 - 2
Masuit.Tools.UnitTest/Masuit.Tools.UnitTest.csproj

@@ -56,8 +56,8 @@
     <Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
     </Reference>
-    <Reference Include="Moq, Version=4.13.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
-      <HintPath>..\packages\Moq.4.13.1\lib\net45\Moq.dll</HintPath>
+    <Reference Include="Moq, Version=4.14.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
+      <HintPath>..\packages\Moq.4.14.1\lib\net45\Moq.dll</HintPath>
     </Reference>
     <Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
       <HintPath>..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath>

+ 1 - 1
Masuit.Tools.UnitTest/packages.config

@@ -5,7 +5,7 @@
   <package id="Microsoft.AspNet.Razor" version="3.2.7" targetFramework="net461" />
   <package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net461" />
   <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net461" />
-  <package id="Moq" version="4.13.1" targetFramework="net461" />
+  <package id="Moq" version="4.14.1" targetFramework="net461" />
   <package id="MSTest.TestAdapter" version="2.1.1" targetFramework="net461" />
   <package id="MSTest.TestFramework" version="2.1.1" targetFramework="net461" />
   <package id="NUnit" version="3.12.0" targetFramework="net461" />

+ 3 - 0
Masuit.Tools/Masuit.Tools.csproj

@@ -78,6 +78,7 @@
     <Reference Include="System.Management" />
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
+    <Reference Include="System.Numerics" />
     <Reference Include="System.Web" />
     <Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll</HintPath>
@@ -202,7 +203,9 @@
     <Compile Include="Models\Email.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Security\HashEncode.cs" />
+    <Compile Include="Security\RSA.cs" />
     <Compile Include="Security\RSACrypt.cs" />
+    <Compile Include="Security\RsaPem.cs" />
     <Compile Include="Strings\ChineseString.cs" />
     <Compile Include="Strings\NumberFormater.cs" />
     <Compile Include="Strings\Template.cs" />

BIN
Masuit.Tools/Properties/AssemblyInfo.cs


+ 250 - 0
Masuit.Tools/Security/RSA.cs

@@ -0,0 +1,250 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Masuit.Tools.Security
+{
+    /// <summary>
+    /// RSA操作类
+    /// </summary>
+    public class RSA
+    {
+        /// <summary>
+        /// 导出XML格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public string ToXML(bool convertToPublic = false)
+        {
+            return RSAObject.ToXmlString(!RSAObject.PublicOnly && !convertToPublic);
+        }
+
+        /// <summary>
+        /// 导出PEM PKCS#1格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public string ToPEM_PKCS1(bool convertToPublic = false)
+        {
+            return new RsaPem(RSAObject).ToPEM(convertToPublic, false);
+        }
+
+        /// <summary>
+        /// 导出PEM PKCS#8格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public string ToPEM_PKCS8(bool convertToPublic = false)
+        {
+            return new RsaPem(RSAObject).ToPEM(convertToPublic, true);
+        }
+
+        /// <summary>
+        /// 将密钥对导出成PEM对象,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public RsaPem ToPEM(bool convertToPublic = false)
+        {
+            return new RsaPem(RSAObject, convertToPublic);
+        }
+
+
+        /// <summary>
+        /// 加密字符串(utf-8),出错抛异常
+        /// </summary>
+        public string Encrypt(string str)
+        {
+            return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(str)));
+        }
+
+        /// <summary>
+        /// 加密数据,出错抛异常
+        /// </summary>
+        public byte[] Encrypt(byte[] data)
+        {
+            int blockLen = RSAObject.KeySize / 8 - 11;
+            if (data.Length <= blockLen)
+            {
+                return RSAObject.Encrypt(data, false);
+            }
+
+            using var dataStream = new MemoryStream(data);
+            using var enStream = new MemoryStream();
+            var buffer = new byte[blockLen];
+            int len = dataStream.Read(buffer, 0, blockLen);
+
+            while (len > 0)
+            {
+                var block = new byte[len];
+                Array.Copy(buffer, 0, block, 0, len);
+                var enBlock = RSAObject.Encrypt(block, false);
+                enStream.Write(enBlock, 0, enBlock.Length);
+                len = dataStream.Read(buffer, 0, blockLen);
+            }
+
+            return enStream.ToArray();
+        }
+
+        /// <summary>
+        /// 解密字符串(utf-8),解密异常返回null
+        /// </summary>
+        public string DecryptOrNull(string str)
+        {
+            if (string.IsNullOrEmpty(str))
+            {
+                return null;
+            }
+
+            byte[] byts = null;
+            try
+            {
+                byts = Convert.FromBase64String(str);
+            }
+            catch
+            {
+            }
+
+            if (byts == null)
+            {
+                return null;
+            }
+
+            var val = DecryptOrNull(byts);
+            return val == null ? null : Encoding.UTF8.GetString(val);
+        }
+
+        /// <summary>
+        /// 解密数据,解密异常返回null
+        /// </summary>
+        public byte[] DecryptOrNull(byte[] data)
+        {
+            try
+            {
+                int blockLen = RSAObject.KeySize / 8;
+                if (data.Length <= blockLen)
+                {
+                    return RSAObject.Decrypt(data, false);
+                }
+
+                using var dataStream = new MemoryStream(data);
+                using var deStream = new MemoryStream();
+                byte[] buffer = new byte[blockLen];
+                int len = dataStream.Read(buffer, 0, blockLen);
+
+                while (len > 0)
+                {
+                    var block = new byte[len];
+                    Array.Copy(buffer, 0, block, 0, len);
+                    var deBlock = RSAObject.Decrypt(block, false);
+                    deStream.Write(deBlock, 0, deBlock.Length);
+                    len = dataStream.Read(buffer, 0, blockLen);
+                }
+
+                return deStream.ToArray();
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// 对str进行签名,并指定hash算法(如:SHA256)
+        /// </summary>
+        public string Sign(string hash, string str)
+        {
+            return Convert.ToBase64String(Sign(hash, Encoding.UTF8.GetBytes(str)));
+        }
+
+        /// <summary>
+        /// 对data进行签名,并指定hash算法(如:SHA256)
+        /// </summary>
+        public byte[] Sign(string hash, byte[] data)
+        {
+            return RSAObject.SignData(data, hash);
+        }
+
+        /// <summary>
+        /// 验证字符串str的签名是否是sgin,并指定hash算法(如:SHA256)
+        /// </summary>
+        public bool Verify(string hash, string sgin, string str)
+        {
+            byte[] byts = null;
+            try
+            {
+                byts = Convert.FromBase64String(sgin);
+            }
+            catch
+            {
+            }
+
+            return byts != null && Verify(hash, byts, Encoding.UTF8.GetBytes(str));
+        }
+
+        /// <summary>
+        /// 验证data的签名是否是sgin,并指定hash算法(如:SHA256)
+        /// </summary>
+        public bool Verify(string hash, byte[] sgin, byte[] data)
+        {
+            try
+            {
+                return RSAObject.VerifyData(data, hash, sgin);
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+
+        /// <summary>
+        /// 最底层的RSACryptoServiceProvider对象
+        /// </summary>
+        public RSACryptoServiceProvider RSAObject { get; }
+
+        /// <summary>
+        /// 密钥位数
+        /// </summary>
+        public int KeySize => RSAObject.KeySize;
+
+        /// <summary>
+        /// 是否包含私钥
+        /// </summary>
+        public bool HasPrivate => !RSAObject.PublicOnly;
+
+        /// <summary>
+        /// 用指定密钥大小创建一个新的RSA,出错抛异常
+        /// </summary>
+        public RSA(int keySize)
+        {
+            var rsaParams = new CspParameters
+            {
+                Flags = CspProviderFlags.UseMachineKeyStore
+            };
+            RSAObject = new RSACryptoServiceProvider(keySize, rsaParams);
+        }
+
+        /// <summary>
+        /// 通过公钥或私钥创建RSA,出错抛异常
+        /// </summary>
+        public RSA(string key)
+        {
+            if (!key.StartsWith("<"))
+            {
+                RSAObject = RsaPem.FromPEM(key).GetRSA();
+            }
+            else
+            {
+                var rsaParams = new CspParameters
+                {
+                    Flags = CspProviderFlags.UseMachineKeyStore
+                };
+                RSAObject = new RSACryptoServiceProvider(rsaParams);
+
+                RSAObject.FromXmlString(key);
+            }
+        }
+
+        /// <summary>
+        /// 通过一个pem对象创建RSA,pem为公钥或私钥,出错抛异常
+        /// </summary>
+        public RSA(RsaPem pem)
+        {
+            RSAObject = pem.GetRSA();
+        }
+    }
+}

+ 28 - 87
Masuit.Tools/Security/RSACrypt.cs

@@ -34,13 +34,13 @@ namespace Masuit.Tools.Security
         /// <summary>
         /// 生成 RSA 公钥和私钥
         /// </summary>
-        public static RsaKey GenerateRsaKeys()
+        public static RsaKey GenerateRsaKeys(int length = 1024)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            return RsaKey ??= new RsaKey
+            var rsa = new RSA(length);
+            return RsaKey = new RsaKey
             {
-                PrivateKey = rsa.ToXmlString(true),
-                PublicKey = rsa.ToXmlString(false)
+                PrivateKey = rsa.ToPEM_PKCS1(),
+                PublicKey = rsa.ToPEM_PKCS1(true)
             };
         }
 
@@ -57,11 +57,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicException">The cryptographic service provider (CSP) cannot be acquired. </exception>
         public static string RSAEncrypt(this string mStrEncryptString, string publicKey)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(publicKey);
-            var plainTextBArray = new UnicodeEncoding().GetBytes(mStrEncryptString);
-            var cypherTextBArray = rsa.Encrypt(plainTextBArray, false);
-            return Convert.ToBase64String(cypherTextBArray);
+            var rsa = new RSA(publicKey);
+            return rsa.Encrypt(mStrEncryptString);
         }
 
         /// <summary>
@@ -84,10 +81,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicException">The cryptographic service provider (CSP) cannot be acquired. </exception>
         public static string RSAEncrypt(this byte[] encryptString, string publicKey)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(publicKey);
-            var cypherTextBArray = rsa.Encrypt(encryptString, false);
-            return Convert.ToBase64String(cypherTextBArray);
+            var rsa = new RSA(publicKey);
+            return Convert.ToBase64String(rsa.Encrypt(encryptString));
         }
 
         /// <summary>
@@ -114,11 +109,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicException">The cryptographic service provider (CSP) cannot be acquired. </exception>
         public static string RSADecrypt(this string mStrDecryptString, string privateKey)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(privateKey);
-            var plainTextBArray = Convert.FromBase64String(mStrDecryptString);
-            var dypherTextBArray = rsa.Decrypt(plainTextBArray, false);
-            return new UnicodeEncoding().GetString(dypherTextBArray);
+            var rsa = new RSA(privateKey);
+            return rsa.DecryptOrNull(mStrDecryptString);
         }
 
         /// <summary>
@@ -141,10 +133,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicException">The cryptographic service provider (CSP) cannot be acquired. </exception>
         public static string RSADecrypt(this byte[] decryptString, string privateKey)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(privateKey);
-            var dypherTextBArray = rsa.Decrypt(decryptString, false);
-            return new UnicodeEncoding().GetString(dypherTextBArray);
+            var rsa = new RSA(privateKey);
+            return new UnicodeEncoding().GetString(rsa.DecryptOrNull(decryptString));
         }
 
         /// <summary>
@@ -232,13 +222,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static byte[] SignatureBytes(this byte[] hashbyteSignature, string privateKey)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(privateKey);
-            var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
-            //设置签名的算法为MD5 
-            rsaFormatter.SetHashAlgorithm("MD5");
-            //执行签名 
-            return rsaFormatter.CreateSignature(hashbyteSignature);
+            var rsa = new RSA(privateKey);
+            return rsa.Sign("MD5", hashbyteSignature);
         }
 
         /// <summary>
@@ -251,14 +236,7 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static string SignatureString(this byte[] hashbyteSignature, string privateKey)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(privateKey);
-            var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
-            //设置签名的算法为MD5 
-            rsaFormatter.SetHashAlgorithm("MD5");
-            //执行签名 
-            var encryptedSignatureData = rsaFormatter.CreateSignature(hashbyteSignature);
-            return Convert.ToBase64String(encryptedSignatureData);
+            return Convert.ToBase64String(SignatureBytes(hashbyteSignature, privateKey));
         }
 
         /// <summary>
@@ -271,14 +249,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static byte[] SignatureBytes(this string mStrHashbyteSignature, string pStrKeyPrivate)
         {
-            byte[] hashbyteSignature = Convert.FromBase64String(mStrHashbyteSignature);
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(pStrKeyPrivate);
-            var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
-            //设置签名的算法为MD5 
-            rsaFormatter.SetHashAlgorithm("MD5");
-            //执行签名 
-            return rsaFormatter.CreateSignature(hashbyteSignature);
+            var rsa = new RSA(pStrKeyPrivate);
+            return Encoding.UTF32.GetBytes(rsa.Sign("MD5", mStrHashbyteSignature));
         }
 
         /// <summary>
@@ -291,15 +263,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static string SignatureString(this string mStrHashbyteSignature, string pStrKeyPrivate)
         {
-            var hashbyteSignature = Convert.FromBase64String(mStrHashbyteSignature);
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(pStrKeyPrivate);
-            var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
-            //设置签名的算法为MD5 
-            rsaFormatter.SetHashAlgorithm("MD5");
-            //执行签名 
-            var encryptedSignatureData = rsaFormatter.CreateSignature(hashbyteSignature);
-            return Convert.ToBase64String(encryptedSignatureData);
+            var rsa = new RSA(pStrKeyPrivate);
+            return rsa.Sign("MD5", mStrHashbyteSignature);
         }
 
         #endregion
@@ -317,13 +282,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static bool SignatureDeformatter(this byte[] deformatterData, string publicKey, byte[] hashbyteDeformatter)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(publicKey);
-            var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
-            //指定解密的时候HASH算法为MD5 
-            rsaDeformatter.SetHashAlgorithm("MD5");
-            if (rsaDeformatter.VerifySignature(hashbyteDeformatter, deformatterData)) return true;
-            return false;
+            var rsa = new RSA(publicKey);
+            return rsa.Verify("MD5", deformatterData, hashbyteDeformatter);
         }
 
         /// <summary>
@@ -337,14 +297,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static bool SignatureDeformatter(this byte[] deformatterData, string publicKey, string pStrHashbyteDeformatter)
         {
-            byte[] hashbyteDeformatter = Convert.FromBase64String(pStrHashbyteDeformatter);
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(publicKey);
-            var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
-            //指定解密的时候HASH算法为MD5 
-            rsaDeformatter.SetHashAlgorithm("MD5");
-            if (rsaDeformatter.VerifySignature(hashbyteDeformatter, deformatterData)) return true;
-            return false;
+            var rsa = new RSA(publicKey);
+            return rsa.Verify("MD5", deformatterData, Convert.FromBase64String(pStrHashbyteDeformatter));
         }
 
         /// <summary>
@@ -358,14 +312,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static bool SignatureDeformatter(this string pStrDeformatterData, string publicKey, byte[] hashbyteDeformatter)
         {
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(publicKey);
-            var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
-            //指定解密的时候HASH算法为MD5 
-            rsaDeformatter.SetHashAlgorithm("MD5");
-            var deformatterData = Convert.FromBase64String(pStrDeformatterData);
-            if (rsaDeformatter.VerifySignature(hashbyteDeformatter, deformatterData)) return true;
-            return false;
+            var rsa = new RSA(publicKey);
+            return rsa.Verify("MD5", Convert.FromBase64String(pStrDeformatterData), hashbyteDeformatter);
         }
 
         /// <summary>
@@ -379,15 +327,8 @@ namespace Masuit.Tools.Security
         /// <exception cref="CryptographicUnexpectedOperationException">The key is null.-or- The hash algorithm is null. </exception>
         public static bool SignatureDeformatter(this string pStrDeformatterData, string publicKey, string pStrHashbyteDeformatter)
         {
-            byte[] hashbyteDeformatter = Convert.FromBase64String(pStrHashbyteDeformatter);
-            using var rsa = new RSACryptoServiceProvider();
-            rsa.FromXmlString(publicKey);
-            var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
-            //指定解密的时候HASH算法为MD5 
-            rsaDeformatter.SetHashAlgorithm("MD5");
-            var deformatterData = Convert.FromBase64String(pStrDeformatterData);
-            if (rsaDeformatter.VerifySignature(hashbyteDeformatter, deformatterData)) return true;
-            return false;
+            var rsa = new RSA(publicKey);
+            return rsa.Verify("MD5", Convert.FromBase64String(pStrDeformatterData), Convert.FromBase64String(pStrHashbyteDeformatter));
         }
 
         #endregion

+ 724 - 0
Masuit.Tools/Security/RsaPem.cs

@@ -0,0 +1,724 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Masuit.Tools.Security
+{
+    /// <summary>
+    /// RSA PEM格式密钥对的解析和导出
+    /// </summary>
+    public class RsaPem
+    {
+        /// <summary>
+        /// modulus 模数n,公钥、私钥都有
+        /// </summary>
+        public byte[] KeyModulus;
+
+        /// <summary>
+        /// publicExponent 公钥指数e,公钥、私钥都有
+        /// </summary>
+        public byte[] KeyExponent;
+
+        /// <summary>
+        /// privateExponent 私钥指数d,只有私钥的时候才有
+        /// </summary>
+        public byte[] KeyD;
+
+        //以下参数只有私钥才有 https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsaparameters?redirectedfrom=MSDN&view=netframework-4.8
+        /// <summary>
+        /// prime1
+        /// </summary>
+        public byte[] ValP;
+
+        /// <summary>
+        /// prime2
+        /// </summary>
+        public byte[] ValQ;
+
+        /// <summary>
+        /// exponent1
+        /// </summary>
+        public byte[] ValDp;
+
+        /// <summary>
+        /// exponent2
+        /// </summary>
+        public byte[] ValDq;
+
+        /// <summary>
+        /// coefficient
+        /// </summary>
+        public byte[] ValInverseQ;
+
+        private RsaPem()
+        {
+        }
+
+        /// <summary>
+        /// 通过RSA中的公钥和私钥构造一个PEM,如果convertToPublic含私钥的RSA将只读取公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public RsaPem(RSACryptoServiceProvider rsa, bool convertToPublic = false)
+        {
+            var isPublic = convertToPublic || rsa.PublicOnly;
+            var param = rsa.ExportParameters(!isPublic);
+            KeyModulus = param.Modulus;
+            KeyExponent = param.Exponent;
+
+            if (!isPublic)
+            {
+                KeyD = param.D;
+                ValP = param.P;
+                ValQ = param.Q;
+                ValDp = param.DP;
+                ValDq = param.DQ;
+                ValInverseQ = param.InverseQ;
+            }
+        }
+
+        /// <summary>
+        /// 通过全量的PEM字段数据构造一个PEM,除了模数modulus和公钥指数exponent必须提供外,其他私钥指数信息要么全部提供,要么全部不提供(导出的PEM就只包含公钥)
+        /// 注意:所有参数首字节如果是0,必须先去掉
+        /// </summary>
+        public RsaPem(byte[] modulus, byte[] exponent, byte[] d, byte[] p, byte[] q, byte[] dp, byte[] dq, byte[] inverseQ)
+        {
+            KeyModulus = modulus;
+            KeyExponent = exponent;
+            KeyD = d;
+
+            ValP = p;
+            ValQ = q;
+            ValDp = dp;
+            ValDq = dq;
+            ValInverseQ = inverseQ;
+        }
+
+        /// <summary>
+        /// 通过公钥指数和私钥指数构造一个PEM,会反推计算出P、Q但和原始生成密钥的P、Q极小可能相同
+        /// 注意:所有参数首字节如果是0,必须先去掉
+        /// 出错将会抛出异常
+        /// </summary>
+        /// <param name="modulus">必须提供模数</param>
+        /// <param name="exponent">必须提供公钥指数</param>
+        /// <param name="dOrNull">私钥指数可以不提供,导出的PEM就只包含公钥</param>
+        public RsaPem(byte[] modulus, byte[] exponent, byte[] dOrNull)
+        {
+            KeyModulus = modulus; //modulus
+            KeyExponent = exponent; //publicExponent
+
+            if (dOrNull != null)
+            {
+                KeyD = dOrNull; //privateExponent
+
+                //反推P、Q
+                BigInteger n = BigX(modulus);
+                BigInteger e = BigX(exponent);
+                BigInteger d = BigX(dOrNull);
+                BigInteger p = FindFactor(e, d, n);
+                BigInteger q = n / p;
+                if (p.CompareTo(q) > 0)
+                {
+                    BigInteger t = p;
+                    p = q;
+                    q = t;
+                }
+
+                BigInteger exp1 = d % (p - BigInteger.One);
+                BigInteger exp2 = d % (q - BigInteger.One);
+                BigInteger coeff = BigInteger.ModPow(q, p - 2, p);
+
+                ValP = BigB(p); //prime1
+                ValQ = BigB(q); //prime2
+                ValDp = BigB(exp1); //exponent1
+                ValDq = BigB(exp2); //exponent2
+                ValInverseQ = BigB(coeff); //coefficient
+            }
+        }
+
+        /// <summary>
+        /// 密钥位数
+        /// </summary>
+        public int KeySize => KeyModulus.Length * 8;
+
+        /// <summary>
+        /// 是否包含私钥
+        /// </summary>
+        public bool HasPrivate => KeyD != null;
+
+        /// <summary>
+        /// 将PEM中的公钥私钥转成RSA对象,如果未提供私钥,RSA中就只包含公钥
+        /// </summary>
+        public RSACryptoServiceProvider GetRSA()
+        {
+            var rsaParams = new CspParameters();
+            rsaParams.Flags = CspProviderFlags.UseMachineKeyStore;
+            var rsa = new RSACryptoServiceProvider(rsaParams);
+
+            var param = new RSAParameters
+            {
+                Modulus = KeyModulus,
+                Exponent = KeyExponent
+            };
+            if (KeyD != null)
+            {
+                param.D = KeyD;
+                param.P = ValP;
+                param.Q = ValQ;
+                param.DP = ValDp;
+                param.DQ = ValDq;
+                param.InverseQ = ValInverseQ;
+            }
+
+            rsa.ImportParameters(param);
+            return rsa;
+        }
+
+        /// <summary>
+        /// 转成正整数,如果是负数,需要加前导0转成正整数
+        /// </summary>
+        public static BigInteger BigX(byte[] bigb)
+        {
+            if (bigb[0] > 0x7F)
+            {
+                byte[] c = new byte[bigb.Length + 1];
+                Array.Copy(bigb, 0, c, 1, bigb.Length);
+                bigb = c;
+            }
+
+            return new BigInteger(bigb.Reverse().ToArray()); //C#的二进制是反的
+        }
+
+        /// <summary>
+        /// BigInt导出byte整数首字节>0x7F的会加0前导,保证正整数,因此需要去掉0
+        /// </summary>
+        public static byte[] BigB(BigInteger bigx)
+        {
+            byte[] val = bigx.ToByteArray().Reverse().ToArray(); //C#的二进制是反的
+            if (val[0] == 0)
+            {
+                byte[] c = new byte[val.Length - 1];
+                Array.Copy(val, 1, c, 0, c.Length);
+                val = c;
+            }
+
+            return val;
+        }
+
+        /// <summary>
+        /// 由n e d 反推 P Q 
+        /// </summary>
+        private static BigInteger FindFactor(BigInteger e, BigInteger d, BigInteger n)
+        {
+            BigInteger edMinus1 = e * d - BigInteger.One;
+            int s = -1;
+            if (edMinus1 != BigInteger.Zero)
+            {
+                s = (int)(BigInteger.Log(edMinus1 & -edMinus1) / BigInteger.Log(2));
+            }
+
+            BigInteger t = edMinus1 >> s;
+
+            long now = DateTime.Now.Ticks;
+            for (int aInt = 2; ; aInt++)
+            {
+                if (aInt % 10 == 0 && DateTime.Now.Ticks - now > 3000 * 10000)
+                {
+                    throw new Exception("推算RSA.P超时"); //测试最多循环2次,1024位的速度很快 8ms
+                }
+
+                BigInteger aPow = BigInteger.ModPow(new BigInteger(aInt), t, n);
+                for (int i = 1; i <= s; i++)
+                {
+                    if (aPow == BigInteger.One)
+                    {
+                        break;
+                    }
+
+                    if (aPow == n - BigInteger.One)
+                    {
+                        break;
+                    }
+
+                    BigInteger aPowSquared = aPow * aPow % n;
+                    if (aPowSquared == BigInteger.One)
+                    {
+                        return BigInteger.GreatestCommonDivisor(aPow - BigInteger.One, n);
+                    }
+
+                    aPow = aPowSquared;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// 用PEM格式密钥对创建RSA,支持PKCS#1、PKCS#8格式的PEM
+        /// 出错将会抛出异常
+        /// </summary>
+        public static RsaPem FromPEM(string pem)
+        {
+            RsaPem param = new RsaPem();
+
+            var base64 = PemCode.Replace(pem, "");
+            byte[] data = null;
+            try
+            {
+                data = Convert.FromBase64String(base64);
+            }
+            catch
+            {
+            }
+
+            if (data == null)
+            {
+                throw new Exception("PEM内容无效");
+            }
+
+            var idx = 0;
+
+            //读取长度
+            Func<byte, int> readLen = (first) =>
+            {
+                if (data[idx] == first)
+                {
+                    idx++;
+                    if (data[idx] == 0x81)
+                    {
+                        idx++;
+                        return data[idx++];
+                    }
+
+                    if (data[idx] == 0x82)
+                    {
+                        idx++;
+                        return ((data[idx++]) << 8) + data[idx++];
+                    }
+
+                    if (data[idx] < 0x80)
+                    {
+                        return data[idx++];
+                    }
+                }
+
+                throw new Exception("PEM未能提取到数据");
+            };
+            //读取块数据
+            Func<byte[]> readBlock = () =>
+            {
+                var len = readLen(0x02);
+                if (data[idx] == 0x00)
+                {
+                    idx++;
+                    len--;
+                }
+
+                var val = new byte[len];
+                for (var i = 0; i < len; i++)
+                {
+                    val[i] = data[idx + i];
+                }
+
+                idx += len;
+                return val;
+            };
+            //比较data从idx位置开始是否是byts内容
+            Func<byte[], bool> eq = (byts) =>
+            {
+                for (var i = 0; i < byts.Length; i++, idx++)
+                {
+                    if (idx >= data.Length)
+                    {
+                        return false;
+                    }
+
+                    if (byts[i] != data[idx])
+                    {
+                        return false;
+                    }
+                }
+
+                return true;
+            };
+
+
+            if (pem.Contains("PUBLIC KEY"))
+            {
+                //使用公钥
+                //读取数据总长度
+                readLen(0x30);
+
+                //看看有没有oid
+                var idx2 = idx;
+                if (eq(SeqOid))
+                {
+                    //读取1长度
+                    readLen(0x03);
+                    idx++; //跳过0x00
+                    //读取2长度
+                    readLen(0x30);
+                }
+                else
+                {
+                    idx = idx2;
+                }
+
+                //Modulus
+                param.KeyModulus = readBlock();
+
+                //Exponent
+                param.KeyExponent = readBlock();
+            }
+            else if (pem.Contains("PRIVATE KEY"))
+            {
+                //使用私钥
+                //读取数据总长度
+                readLen(0x30);
+
+                //读取版本号
+                if (!eq(Ver))
+                {
+                    throw new Exception("PEM未知版本");
+                }
+
+                //检测PKCS8
+                var idx2 = idx;
+                if (eq(SeqOid))
+                {
+                    //读取1长度
+                    readLen(0x04);
+                    //读取2长度
+                    readLen(0x30);
+
+                    //读取版本号
+                    if (!eq(Ver))
+                    {
+                        throw new Exception("PEM版本无效");
+                    }
+                }
+                else
+                {
+                    idx = idx2;
+                }
+
+                //读取数据
+                param.KeyModulus = readBlock();
+                param.KeyExponent = readBlock();
+                param.KeyD = readBlock();
+                param.ValP = readBlock();
+                param.ValQ = readBlock();
+                param.ValDp = readBlock();
+                param.ValDq = readBlock();
+                param.ValInverseQ = readBlock();
+            }
+            else
+            {
+                throw new Exception("pem需要BEGIN END标头");
+            }
+
+            return param;
+        }
+
+        private static readonly Regex PemCode = new Regex(@"--+.+?--+|\s+");
+
+        private static readonly byte[] SeqOid = {
+            0x30,
+            0x0D,
+            0x06,
+            0x09,
+            0x2A,
+            0x86,
+            0x48,
+            0x86,
+            0xF7,
+            0x0D,
+            0x01,
+            0x01,
+            0x01,
+            0x05,
+            0x00
+        };
+
+        private static readonly byte[] Ver = {
+            0x02,
+            0x01,
+            0x00
+        };
+
+
+        /// <summary>
+        /// 将RSA中的密钥对转换成PEM格式,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public string ToPEM(bool convertToPublic, bool usePKCS8)
+        {
+            var ms = new MemoryStream();
+            //写入一个长度字节码
+            Action<int> writeLenByte = len =>
+            {
+                if (len < 0x80)
+                {
+                    ms.WriteByte((byte)len);
+                }
+                else if (len <= 0xff)
+                {
+                    ms.WriteByte(0x81);
+                    ms.WriteByte((byte)len);
+                }
+                else
+                {
+                    ms.WriteByte(0x82);
+                    ms.WriteByte((byte)(len >> 8 & 0xff));
+                    ms.WriteByte((byte)(len & 0xff));
+                }
+            };
+            //写入一块数据
+            Action<byte[]> writeBlock = byts =>
+            {
+                var addZero = (byts[0] >> 4) >= 0x8;
+                ms.WriteByte(0x02);
+                var len = byts.Length + (addZero ? 1 : 0);
+                writeLenByte(len);
+
+                if (addZero)
+                {
+                    ms.WriteByte(0x00);
+                }
+
+                ms.Write(byts, 0, byts.Length);
+            };
+            //根据后续内容长度写入长度数据
+            Func<int, byte[], byte[]> writeLen = (index, byts) =>
+            {
+                var len = byts.Length - index;
+
+                ms.SetLength(0);
+                ms.Write(byts, 0, index);
+                writeLenByte(len);
+                ms.Write(byts, index, len);
+
+                return ms.ToArray();
+            };
+            Action<MemoryStream, byte[]> writeAll = (stream, byts) =>
+            {
+                stream.Write(byts, 0, byts.Length);
+            };
+            Func<string, int, string> TextBreak = (text, line) =>
+            {
+                var idx = 0;
+                var len = text.Length;
+                var str = new StringBuilder();
+                while (idx < len)
+                {
+                    if (idx > 0)
+                    {
+                        str.Append('\n');
+                    }
+
+                    str.Append(idx + line >= len ? text.Substring(idx) : text.Substring(idx, line));
+                    idx += line;
+                }
+
+                return str.ToString();
+            };
+
+
+            if (KeyD == null || convertToPublic)
+            {
+                //生成公钥
+                //写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入
+                ms.WriteByte(0x30);
+                var index1 = (int)ms.Length;
+
+                //固定内容
+                writeAll(ms, SeqOid);
+
+                //从0x00开始的后续长度
+                ms.WriteByte(0x03);
+                var index2 = (int)ms.Length;
+                ms.WriteByte(0x00);
+
+                //后续内容长度
+                ms.WriteByte(0x30);
+                var index3 = (int)ms.Length;
+
+                //写入Modulus
+                writeBlock(KeyModulus);
+
+                //写入Exponent
+                writeBlock(KeyExponent);
+
+                //计算空缺的长度
+                var bytes = ms.ToArray();
+                bytes = writeLen(index3, bytes);
+                bytes = writeLen(index2, bytes);
+                bytes = writeLen(index1, bytes);
+                return "-----BEGIN PUBLIC KEY-----\n" + TextBreak(Convert.ToBase64String(bytes), 64) + "\n-----END PUBLIC KEY-----";
+            }
+            else
+            {
+                /****生成私钥****/
+
+                //写入总字节数,后续写入
+                ms.WriteByte(0x30);
+                int index1 = (int)ms.Length;
+
+                //写入版本号
+                writeAll(ms, Ver);
+
+                //PKCS8 多一段数据
+                int index2 = -1, index3 = -1;
+                if (usePKCS8)
+                {
+                    //固定内容
+                    writeAll(ms, SeqOid);
+
+                    //后续内容长度
+                    ms.WriteByte(0x04);
+                    index2 = (int)ms.Length;
+
+                    //后续内容长度
+                    ms.WriteByte(0x30);
+                    index3 = (int)ms.Length;
+
+                    //写入版本号
+                    writeAll(ms, Ver);
+                }
+
+                //写入数据
+                writeBlock(KeyModulus);
+                writeBlock(KeyExponent);
+                writeBlock(KeyD);
+                writeBlock(ValP);
+                writeBlock(ValQ);
+                writeBlock(ValDp);
+                writeBlock(ValDq);
+                writeBlock(ValInverseQ);
+
+
+                //计算空缺的长度
+                var byts = ms.ToArray();
+
+                if (index2 != -1)
+                {
+                    byts = writeLen(index3, byts);
+                    byts = writeLen(index2, byts);
+                }
+
+                byts = writeLen(index1, byts);
+
+
+                var flag = " PRIVATE KEY";
+                if (!usePKCS8)
+                {
+                    flag = " RSA" + flag;
+                }
+
+                return "-----BEGIN" + flag + "-----\n" + TextBreak(Convert.ToBase64String(byts), 64) + "\n-----END" + flag + "-----";
+            }
+        }
+
+
+        /// <summary>
+        /// 将XML格式密钥转成PEM,支持公钥xml、私钥xml
+        /// 出错将会抛出异常
+        /// </summary>
+        public static RsaPem FromXML(string xml)
+        {
+            var rtv = new RsaPem();
+            var xmlM = XmlExp.Match(xml);
+            if (!xmlM.Success)
+            {
+                throw new Exception("XML内容不符合要求");
+            }
+
+            var tagM = XmlTagExp.Match(xmlM.Groups[1].Value);
+            while (tagM.Success)
+            {
+                string tag = tagM.Groups[1].Value;
+                string b64 = tagM.Groups[2].Value;
+                byte[] val = Convert.FromBase64String(b64);
+                switch (tag)
+                {
+                    case "Modulus":
+                        rtv.KeyModulus = val;
+                        break;
+                    case "Exponent":
+                        rtv.KeyExponent = val;
+                        break;
+                    case "D":
+                        rtv.KeyD = val;
+                        break;
+
+                    case "P":
+                        rtv.ValP = val;
+                        break;
+                    case "Q":
+                        rtv.ValQ = val;
+                        break;
+                    case "DP":
+                        rtv.ValDp = val;
+                        break;
+                    case "DQ":
+                        rtv.ValDq = val;
+                        break;
+                    case "InverseQ":
+                        rtv.ValInverseQ = val;
+                        break;
+                }
+
+                tagM = tagM.NextMatch();
+            }
+
+            if (rtv.KeyModulus == null || rtv.KeyExponent == null)
+            {
+                throw new Exception("XML公钥丢失");
+            }
+
+            if (rtv.KeyD != null)
+            {
+                if (rtv.ValP == null || rtv.ValQ == null || rtv.ValDp == null || rtv.ValDq == null || rtv.ValInverseQ == null)
+                {
+                    return new RsaPem(rtv.KeyModulus, rtv.KeyExponent, rtv.KeyD);
+                }
+            }
+
+            return rtv;
+        }
+
+        private static readonly Regex XmlExp = new Regex("\\s*<RSAKeyValue>([<>\\/\\+=\\w\\s]+)</RSAKeyValue>\\s*");
+        private static readonly Regex XmlTagExp = new Regex("<(.+?)>\\s*([^<]+?)\\s*</");
+
+
+        /// <summary>
+        /// 将RSA中的密钥对转换成XML格式
+        /// ,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public string ToXML(bool convertToPublic)
+        {
+            StringBuilder str = new StringBuilder();
+            str.Append("<RSAKeyValue>");
+            str.Append("<Modulus>" + Convert.ToBase64String(KeyModulus) + "</Modulus>");
+            str.Append("<Exponent>" + Convert.ToBase64String(KeyExponent) + "</Exponent>");
+            if (KeyD == null || convertToPublic)
+            {
+                /****生成公钥****/
+                //NOOP
+            }
+            else
+            {
+                /****生成私钥****/
+                str.Append("<P>" + Convert.ToBase64String(ValP) + "</P>");
+                str.Append("<Q>" + Convert.ToBase64String(ValQ) + "</Q>");
+                str.Append("<DP>" + Convert.ToBase64String(ValDp) + "</DP>");
+                str.Append("<DQ>" + Convert.ToBase64String(ValDq) + "</DQ>");
+                str.Append("<InverseQ>" + Convert.ToBase64String(ValInverseQ) + "</InverseQ>");
+                str.Append("<D>" + Convert.ToBase64String(KeyD) + "</D>");
+            }
+
+            str.Append("</RSAKeyValue>");
+            return str.ToString();
+        }
+    }
+}

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -4,7 +4,7 @@
     <!--*-->
     <id>Masuit.Tools.Net</id>
     <!--*-->
-    <version>2.2.8.8</version>
+    <version>2.2.8.9</version>
     <title>Masuit.Tools</title>
     <!--*-->
     <authors>masuit.com</authors>

+ 1 - 0
Masuit.Tools/packages.config

@@ -10,4 +10,5 @@
   <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" />
   <package id="SharpCompress" version="0.25.0" targetFramework="net461" />
   <package id="StackExchange.Redis" version="1.2.6" targetFramework="net45" requireReinstallation="true" />
+  <package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
 </packages>

+ 23 - 6
Test/Program.cs

@@ -1,6 +1,7 @@
-using Masuit.Tools.Maths;
+using Masuit.Tools;
+using Masuit.Tools.Security;
+using Masuit.Tools.Systems;
 using System;
-using System.Collections.Generic;
 
 namespace Test
 {
@@ -8,10 +9,26 @@ namespace Test
     {
         static void Main(string[] args)
         {
-            var c1 = new RadarChart(new List<double>() { 2, 2, 2, 2 });
-            var c2 = new RadarChart(new List<double>() { 3, 3, 3, 3 });
-            var area = c1.ComputeIntersection(c2).ComputeArea();
-            Console.WriteLine(area);
+            var rsaKey = RsaCrypt.GenerateRsaKeys(512);
+            Console.WriteLine(rsaKey.PrivateKey);
+            var enc = new MyClass()
+            {
+                SdTime = DateTime.Now,
+                MyProperty = "asdf"
+            }.ToJsonString().RSAEncrypt();
+            Console.WriteLine(enc);
+            Console.WriteLine(HiPerfTimer.Execute(() =>
+            {
+                var dec = enc.RSADecrypt();
+                Console.WriteLine(dec);
+            }) * 1000);
         }
     }
+
+    public class MyClass
+    {
+        public string MyProperty { get; set; }
+        public DateTime SdTime { get; set; }
+
+    }
 }