Skip to content

Commit

Permalink
ECDsa算法独立到Security库,避免NewLife.Core依赖Cng导致下游项目引用一大堆包
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Oct 31, 2020
1 parent d55f640 commit 2ed2468
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 38 deletions.
9 changes: 1 addition & 8 deletions NewLife.Core/NewLife.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
<Compile Remove="Reflection\EmitHelper.cs" />
<Compile Remove="Reflection\EmitReflect.cs" />
<Compile Remove="Reflection\IIndexAccessor.cs" />
<Compile Remove="Security\ECDsaHelper.cs" />
<Compile Remove="Serialization\BinaryCodec.cs" />
<Compile Remove="Serialization\BinaryCodec2.cs" />
<Compile Remove="Serialization\JsonCodec.cs" />
Expand All @@ -128,12 +129,4 @@
<EmbeddedResource Include="Windows\MySpeech.cs" />
<EmbeddedResource Include="X组件.txt" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Security.Cryptography.Algorithms">
<Version>4.3.1</Version>
</PackageReference>
<PackageReference Include="System.Security.Cryptography.Cng">
<Version>4.7.0</Version>
</PackageReference>
</ItemGroup>
</Project>
117 changes: 88 additions & 29 deletions NewLife.Core/Web/JwtBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@

namespace NewLife.Web
{
/// <summary>Jwt编码委托</summary>
/// <param name="data"></param>
/// <param name="secrect"></param>
/// <returns></returns>
public delegate Byte[] JwtEncodeDelegate(Byte[] data, String secrect);

/// <summary>Jwt解码委托</summary>
/// <param name="data"></param>
/// <param name="secrect"></param>
/// <param name="signature"></param>
/// <returns></returns>
public delegate Boolean JwtDecodeDelegate(Byte[] data, String secrect, Byte[] signature);

/// <summary>JSON Web Token</summary>
/// <remarks>
/// 主要问题:
Expand Down Expand Up @@ -59,6 +72,19 @@ public class JwtBuilder : IExtend3
public Object this[String key] { get => Items?[key]; set => Items[key] = value; }
#endregion

#region 构造
static JwtBuilder()
{
RegisterAlgorithm("HS256", (d, s) => d.SHA256(s.GetBytes()), null);
RegisterAlgorithm("HS384", (d, s) => d.SHA384(s.GetBytes()), null);
RegisterAlgorithm("HS512", (d, s) => d.SHA512(s.GetBytes()), null);

RegisterAlgorithm("RS256", RSAHelper.SignSha256, RSAHelper.VerifySha256);
RegisterAlgorithm("RS384", RSAHelper.SignSha384, RSAHelper.VerifySha384);
RegisterAlgorithm("RS512", RSAHelper.SignSha512, RSAHelper.VerifySha512);
}
#endregion

#region JWT方法
/// <summary>编码目标对象,生成令牌</summary>
/// <param name="payload"></param>
Expand Down Expand Up @@ -92,20 +118,28 @@ public String Encode(Object payload)

// 签名
var data = $"{header}.{body}".GetBytes();
var sign = alg switch
//var sign = alg switch
//{
// "HS256" => data.SHA256(Secret.GetBytes()),
// "HS384" => data.SHA384(Secret.GetBytes()),
// "HS512" => data.SHA512(Secret.GetBytes()),
// "RS256" => RSAHelper.SignSha256(data, Secret),
// "RS384" => RSAHelper.SignSha384(data, Secret),
// "RS512" => RSAHelper.SignSha512(data, Secret),
// "ES256" => ECDsaHelper.SignSha256(data, Secret),
// "ES384" => ECDsaHelper.SignSha384(data, Secret),
// "ES512" => ECDsaHelper.SignSha512(data, Secret),
// _ => throw new InvalidOperationException($"不支持的算法[{alg}]"),
//};

if (_encodes.TryGetValue(alg, out var enc) && enc != null)
{
"HS256" => data.SHA256(Secret.GetBytes()),
"HS384" => data.SHA384(Secret.GetBytes()),
"HS512" => data.SHA512(Secret.GetBytes()),
"RS256" => RSAHelper.SignSha256(data, Secret),
"RS384" => RSAHelper.SignSha384(data, Secret),
"RS512" => RSAHelper.SignSha512(data, Secret),
"ES256" => ECDsaHelper.SignSha256(data, Secret),
"ES384" => ECDsaHelper.SignSha384(data, Secret),
"ES512" => ECDsaHelper.SignSha512(data, Secret),
_ => throw new InvalidOperationException($"不支持的算法[{alg}]"),
};
return $"{header}.{body}.{sign.ToUrlBase64()}";
var sign = enc(data, Secret);

return $"{header}.{body}.{sign.ToUrlBase64()}";
}

throw new InvalidOperationException($"不支持的算法[{alg}]");
}

/// <summary>解码令牌,得到目标对象</summary>
Expand Down Expand Up @@ -157,25 +191,50 @@ public Boolean TryDecode(String token, out String message)

// 验证签名
var data = $"{ts[0]}.{ts[1]}".GetBytes();
switch (Algorithm)
//switch (Algorithm)
//{
// case "RS256": return RSAHelper.VerifySha256(data, Secret, ts[2].ToBase64());
// case "RS384": return RSAHelper.VerifySha384(data, Secret, ts[2].ToBase64());
// case "RS512": return RSAHelper.VerifySha512(data, Secret, ts[2].ToBase64());
// case "ES256": return ECDsaHelper.VerifySha256(data, Secret, ts[2].ToBase64());
// case "ES384": return ECDsaHelper.VerifySha384(data, Secret, ts[2].ToBase64());
// case "ES512": return ECDsaHelper.VerifySha512(data, Secret, ts[2].ToBase64());
//}

//var sec = Secret.GetBytes();
//var sign = Algorithm switch
//{
// "HS256" => data.SHA256(sec),
// "HS384" => data.SHA384(sec),
// "HS512" => data.SHA512(sec),
// _ => throw new InvalidOperationException($"不支持的算法[{alg}]"),
//};
//return sign.ToUrlBase64() == ts[2];

if (_decodes.TryGetValue(Algorithm, out var dec))
{
case "RS256": return RSAHelper.VerifySha256(data, Secret, ts[2].ToBase64());
case "RS384": return RSAHelper.VerifySha384(data, Secret, ts[2].ToBase64());
case "RS512": return RSAHelper.VerifySha512(data, Secret, ts[2].ToBase64());
case "ES256": return ECDsaHelper.VerifySha256(data, Secret, ts[2].ToBase64());
case "ES384": return ECDsaHelper.VerifySha384(data, Secret, ts[2].ToBase64());
case "ES512": return ECDsaHelper.VerifySha512(data, Secret, ts[2].ToBase64());
if (dec != null) return dec(data, Secret, ts[2].ToBase64());

// 没有验证算法,对比签名
if (_encodes.TryGetValue(Algorithm, out var enc) && enc != null) return enc(data, Secret).ToUrlBase64() == ts[2];
}

var sec = Secret.GetBytes();
var sign = Algorithm switch
{
"HS256" => data.SHA256(sec),
"HS384" => data.SHA384(sec),
"HS512" => data.SHA512(sec),
_ => throw new InvalidOperationException($"不支持的算法[{alg}]"),
};
return sign.ToUrlBase64() == ts[2];
throw new InvalidOperationException($"不支持的算法[{Algorithm}]");
}
#endregion

#region 算法管理
private static IDictionary<String, JwtEncodeDelegate> _encodes = new Dictionary<String, JwtEncodeDelegate>(StringComparer.OrdinalIgnoreCase);
private static IDictionary<String, JwtDecodeDelegate> _decodes = new Dictionary<String, JwtDecodeDelegate>(StringComparer.OrdinalIgnoreCase);

/// <summary>注册算法的编解码实现</summary>
/// <param name="algorithm"></param>
/// <param name="encode"></param>
/// <param name="decode"></param>
public static void RegisterAlgorithm(String algorithm, JwtEncodeDelegate encode, JwtDecodeDelegate decode)
{
_encodes[algorithm] = encode;
_decodes[algorithm] = decode;
}
#endregion
}
Expand Down
78 changes: 78 additions & 0 deletions NewLife.Security/NewLife.Security.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net40;net45;netstandard2.0</TargetFrameworks>
<AssemblyTitle>加密库</AssemblyTitle>
<Description>扩展加密算法</Description>
<Company>新生命开发团队</Company>
<Copyright>©2002-2020 NewLife</Copyright>
<Version>8.10.2020.1029-rc1</Version>
<FileVersion>8.10.2020.1029</FileVersion>
<AssemblyVersion>8.10.*</AssemblyVersion>
<Deterministic>false</Deterministic>
<OutputPath>..\..\Bin</OutputPath>
<DocumentationFile>$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
<LangVersion>latest</LangVersion>
<!--<Nullable>enable</Nullable>-->
</PropertyGroup>
<PropertyGroup>
<PackageId>$(AssemblyName)</PackageId>
<Authors>$(Company)</Authors>
<ProjectUrl>https://github.com/NewLifeX</ProjectUrl>
<PackageIcon>leaf.png</PackageIcon>
<RepositoryUrl>https://github.com/NewLifeX/X</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>新生命团队;X组件;NewLife;$(AssemblyName)</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSource>true</IncludeSource>
<!--<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>-->
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)'=='net40'">
<DefineConstants>$(DefineConstants);__WIN__;NET4</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net45'">
<DefineConstants>$(DefineConstants);__WIN__;NET45</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net46'">
<DefineConstants>$(DefineConstants);__WIN__;NET46</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<DefineConstants>$(DefineConstants);__CORE__;STD20</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
<DefineConstants>$(DefineConstants);__CORE__;STD21</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);DEBUG</DefineConstants>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\NewLife.Core\Security\ECDsaHelper.cs" Link="ECDsaHelper.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Security.Cryptography.Cng">
<Version>4.7.0</Version>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NewLife.Core\NewLife.Core.csproj" />
</ItemGroup>

<ItemGroup>
<Content Include="..\Doc\leaf.png" Link="leaf.png" PackagePath="\" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions XUnitTest.Core/Web/JwtBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
using System;
using NewLife.Security;
using NewLife.Web;
using Xunit;

namespace XUnitTest.Web
{
public class JwtBuilderTests
{
static JwtBuilderTests()
{
JwtBuilder.RegisterAlgorithm("ES256", ECDsaHelper.SignSha256, ECDsaHelper.VerifySha256);
JwtBuilder.RegisterAlgorithm("ES384", ECDsaHelper.SignSha384, ECDsaHelper.VerifySha384);
JwtBuilder.RegisterAlgorithm("ES512", ECDsaHelper.SignSha512, ECDsaHelper.VerifySha512);
}

[Fact]
public void HS256_Encode()
{
Expand Down
1 change: 1 addition & 0 deletions XUnitTest.Core/XUnitTest.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NewLife.Core\NewLife.Core.csproj" />
<ProjectReference Include="..\NewLife.Security\NewLife.Security.csproj" />
</ItemGroup>
</Project>
16 changes: 15 additions & 1 deletion X组件.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileApp", "Samples\Mobile
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewLife.WinCore", "NewLife.WinCore\NewLife.WinCore.csproj", "{3D30240B-E87F-4DFC-844A-8D74C6E6D454}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XCodeTool", "XCodeTool\XCodeTool.csproj", "{ADAD9BA3-5AB4-478A-BB51-3E144D410F59}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XCodeTool", "XCodeTool\XCodeTool.csproj", "{ADAD9BA3-5AB4-478A-BB51-3E144D410F59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NewLife.Security", "NewLife.Security\NewLife.Security.csproj", "{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -175,6 +177,18 @@ Global
{ADAD9BA3-5AB4-478A-BB51-3E144D410F59}.Release|iPhone.Build.0 = Release|Any CPU
{ADAD9BA3-5AB4-478A-BB51-3E144D410F59}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{ADAD9BA3-5AB4-478A-BB51-3E144D410F59}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Debug|iPhone.Build.0 = Debug|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Release|Any CPU.Build.0 = Release|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Release|iPhone.ActiveCfg = Release|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Release|iPhone.Build.0 = Release|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{13D57F4A-2A7C-46FE-8472-DD3BA67F939D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down

0 comments on commit 2ed2468

Please sign in to comment.