Compare commits
10 Commits
211064d44e
...
feat/secur
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad8d15314f | ||
|
|
f1efbae6a4 | ||
|
|
051567aa0a | ||
|
|
287871ddc6 | ||
|
|
a0ad8d732d | ||
|
|
3ad08e2a86 | ||
|
|
b90a52412c | ||
|
|
39091ff5cf | ||
|
|
22040cf1e7 | ||
|
|
af4b7d5438 |
@@ -9,7 +9,7 @@
|
|||||||
<Authors>Digital Data GmbH</Authors>
|
<Authors>Digital Data GmbH</Authors>
|
||||||
<Company>Digital Data GmbH</Company>
|
<Company>Digital Data GmbH</Company>
|
||||||
<Product>DigitalData.Core.Abstractions</Product>
|
<Product>DigitalData.Core.Abstractions</Product>
|
||||||
<Description>This package contains abstractions for the DigitalData.Core.Abstractions library, developed according to the principles of Clean Architecture. It promotes separation of concerns and enables independent core logic.</Description>
|
<Description>This package contains abstractions for the DigitalData.Core library, developed according to the principles of Clean Architecture. It promotes separation of concerns and enables independent core logic.</Description>
|
||||||
<PackageTags>digital data core abstractions clean architecture</PackageTags>
|
<PackageTags>digital data core abstractions clean architecture</PackageTags>
|
||||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
<Copyright>Copyright 2024</Copyright>
|
<Copyright>Copyright 2024</Copyright>
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
<PackAsTool>False</PackAsTool>
|
<PackAsTool>False</PackAsTool>
|
||||||
<PackageIcon>core_icon.png</PackageIcon>
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
<Version>3.0.0</Version>
|
<Version>3.1.0</Version>
|
||||||
<AssemblyVersion>3.0.0</AssemblyVersion>
|
<AssemblyVersion>3.1.0</AssemblyVersion>
|
||||||
<FileVersion>3.0.0</FileVersion>
|
<FileVersion>3.1.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
public interface IAsymmetricKey
|
public interface IAsymmetricKey
|
||||||
{
|
{
|
||||||
string Id { get; }
|
string? Id { get; }
|
||||||
|
|
||||||
string Content { get; }
|
string Content { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ namespace DigitalData.Core.Abstractions.Security
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAsymmetricTokenDescriptor : IAsymmetricPrivateKey, IUniqueSecurityContext
|
public interface IAsymmetricTokenDescriptor : IAsymmetricPrivateKey, IUniqueSecurityContext
|
||||||
{
|
{
|
||||||
string? ApiRoute { get; }
|
IAsymmetricTokenValidator Validator { get; }
|
||||||
|
|
||||||
|
TimeSpan Lifetime { get; init; }
|
||||||
|
|
||||||
#region SecurityTokenDescriptor Map
|
#region SecurityTokenDescriptor Map
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace DigitalData.Core.Abstractions.Security
|
||||||
|
{
|
||||||
|
public interface IAsymmetricTokenValidator : IAsymmetricPublicKey
|
||||||
|
{
|
||||||
|
SecurityKey SecurityKey { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,6 @@ namespace DigitalData.Core.Abstractions.Security
|
|||||||
|
|
||||||
SecurityToken CreateToken(TPrincipal subject, string issuer, string audience);
|
SecurityToken CreateToken(TPrincipal subject, string issuer, string audience);
|
||||||
|
|
||||||
SecurityToken CreateToken(TPrincipal subject, string apiRoute);
|
|
||||||
|
|
||||||
string WriteToken(SecurityToken token);
|
string WriteToken(SecurityToken token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,6 @@ namespace DigitalData.Core.Abstractions.Security
|
|||||||
|
|
||||||
public static string WriteToken<TPrincipal>(this IJwtSignatureHandler<TPrincipal> handler, TPrincipal subject, string issuer, string audience)
|
public static string WriteToken<TPrincipal>(this IJwtSignatureHandler<TPrincipal> handler, TPrincipal subject, string issuer, string audience)
|
||||||
=> handler.WriteToken(handler.CreateToken(subject: subject, issuer: issuer, audience: audience));
|
=> handler.WriteToken(handler.CreateToken(subject: subject, issuer: issuer, audience: audience));
|
||||||
|
|
||||||
public static string WriteToken<TPrincipal>(this IJwtSignatureHandler<TPrincipal> handler, TPrincipal subject, string apiRoute)
|
|
||||||
=> handler.WriteToken(handler.CreateToken(subject: subject, apiRoute: apiRoute));
|
|
||||||
#endregion Jwt Signature Handler
|
#endregion Jwt Signature Handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,13 +47,6 @@ namespace DigitalData.Core.Security.Config
|
|||||||
|
|
||||||
public CryptoFactoryParams()
|
public CryptoFactoryParams()
|
||||||
{
|
{
|
||||||
// set defaults
|
|
||||||
if (VaultDecryptor is not null)
|
|
||||||
VaultDecryptor.Id = "vault";
|
|
||||||
|
|
||||||
foreach (var descriptor in TokenDescriptors)
|
|
||||||
descriptor.IdSeparator = FileNameSeparator;
|
|
||||||
|
|
||||||
// init decryptors
|
// init decryptors
|
||||||
AfterCreate += () =>
|
AfterCreate += () =>
|
||||||
{
|
{
|
||||||
@@ -72,7 +65,17 @@ namespace DigitalData.Core.Security.Config
|
|||||||
// set default path
|
// set default path
|
||||||
if (privateKey.IsPemNull)
|
if (privateKey.IsPemNull)
|
||||||
{
|
{
|
||||||
var file_name_params = new List<object> { privateKey.Id, KeySizeInBits, DateTime.Now.ToTag(DateTagFormat) };
|
// file name
|
||||||
|
var file_name_params = new List<object>();
|
||||||
|
|
||||||
|
if (privateKey.Id is not null)
|
||||||
|
file_name_params.Add(privateKey.Id);
|
||||||
|
else if (privateKey is RSATokenDescriptor descriptor)
|
||||||
|
file_name_params.Add(descriptor.Issuer);
|
||||||
|
|
||||||
|
file_name_params.Add(KeySizeInBits);
|
||||||
|
file_name_params.Add(DateTime.Now.ToTag(DateTagFormat));
|
||||||
|
|
||||||
if (privateKey.IsEncrypted)
|
if (privateKey.IsEncrypted)
|
||||||
file_name_params.Add(Secrets.Version);
|
file_name_params.Add(Secrets.Version);
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,28 @@
|
|||||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<PackageId>DigitalData.Core.Security</PackageId>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<Company>Digital Data GmbH</Company>
|
||||||
|
<Product>Digital Data GmbH</Product>
|
||||||
|
<Description>This package provides RSA-based security functionalities as an implementation of the DigitalData.Core.Abstractions.Security library. It supports robust encryption and decryption operations, as well as JWT signing and validation for secure authentication and data integrity.</Description>
|
||||||
|
<Authors>Digital Data GmbH</Authors>
|
||||||
|
<Copyright>Copyright 2025</Copyright>
|
||||||
|
<PackageProjectUrl></PackageProjectUrl>
|
||||||
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
|
<PackageTags>digital data core security</PackageTags>
|
||||||
|
<AssemblyVersion>1.0.0</AssemblyVersion>
|
||||||
|
<FileVersion>1.0.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\..\nuget-package-icons\core_icon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||||
|
|||||||
@@ -36,13 +36,5 @@ namespace DigitalData.Core.Security
|
|||||||
?? throw new InvalidOperationException($"No or multiple token description found for issuer '{issuer}' and audience '{audience}'.");
|
?? throw new InvalidOperationException($"No or multiple token description found for issuer '{issuer}' and audience '{audience}'.");
|
||||||
return CreateToken(subject: subject, descriptor: descriptor);
|
return CreateToken(subject: subject, descriptor: descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecurityToken CreateToken(TPrincipal subject, string apiRoute)
|
|
||||||
{
|
|
||||||
var desc = _cryptoFactory.TokenDescriptors.SingleOrDefault(desc => desc.ApiRoute == apiRoute)
|
|
||||||
?? throw new InvalidOperationException($"No or multiple token description found for api route '{apiRoute}'.");
|
|
||||||
|
|
||||||
return CreateToken(subject: subject, descriptor: desc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,10 +7,10 @@ namespace DigitalData.Core.Security.RSAKey
|
|||||||
{
|
{
|
||||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
public virtual string Content { get; init; }
|
public virtual string Content { get; init; }
|
||||||
|
|
||||||
public virtual string Id { get; internal set; }
|
|
||||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
|
|
||||||
|
public string? Id { get; init; }
|
||||||
|
|
||||||
protected virtual RSA RSA { get; } = RSA.Create();
|
protected virtual RSA RSA { get; } = RSA.Create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
using DigitalData.Core.Abstractions.Security;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace DigitalData.Core.Security.RSAKey
|
namespace DigitalData.Core.Security.RSAKey
|
||||||
@@ -24,16 +23,16 @@ namespace DigitalData.Core.Security.RSAKey
|
|||||||
|
|
||||||
public bool IsEncrypted { get; init; }
|
public bool IsEncrypted { get; init; }
|
||||||
|
|
||||||
private readonly Lazy<IAsymmetricPublicKey> _lazyPublicKey;
|
protected TPublicKey CreatePublicKey<TPublicKey>() where TPublicKey : RSAPublicKey, new()
|
||||||
|
=> new() { Content = RSA.ExportRSAPublicKeyPem() };
|
||||||
|
|
||||||
|
private readonly Lazy<RSAPublicKey> _lazyPublicKey;
|
||||||
|
|
||||||
public IAsymmetricPublicKey PublicKey => _lazyPublicKey.Value;
|
public IAsymmetricPublicKey PublicKey => _lazyPublicKey.Value;
|
||||||
|
|
||||||
public RSAPrivateKey()
|
public RSAPrivateKey()
|
||||||
{
|
{
|
||||||
_lazyPublicKey = new(() => new RSAPublicKey()
|
_lazyPublicKey = new(CreatePublicKey<RSAPublicKey>);
|
||||||
{
|
|
||||||
Content = RSA.ExportRSAPublicKeyPem()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetPem(string pem)
|
internal void SetPem(string pem)
|
||||||
|
|||||||
@@ -8,13 +8,11 @@ namespace DigitalData.Core.Security.RSAKey
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class RSATokenDescriptor : RSAPrivateKey, IAsymmetricTokenDescriptor
|
public class RSATokenDescriptor : RSAPrivateKey, IAsymmetricTokenDescriptor
|
||||||
{
|
{
|
||||||
internal string IdSeparator { get; set; } = "_-_";
|
private readonly Lazy<RSATokenValidator> _lazyTokenValidator;
|
||||||
|
|
||||||
private string? _id;
|
public IAsymmetricTokenValidator Validator => _lazyTokenValidator.Value;
|
||||||
|
|
||||||
public override string Id { get => _id ?? $"{Issuer}{IdSeparator}{Audience}"; internal set => _id = value; }
|
public required TimeSpan Lifetime { get; init; }
|
||||||
|
|
||||||
public string? ApiRoute { get; init; }
|
|
||||||
|
|
||||||
#region SecurityTokenDescriptor Map
|
#region SecurityTokenDescriptor Map
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -34,8 +32,9 @@ namespace DigitalData.Core.Security.RSAKey
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value of the 'expiration' claim. This value should be in UTC.
|
/// Gets or sets the value of the 'expiration' claim. This value should be in UTC.
|
||||||
|
/// The expiration time is the sum of DateTime.Now and LifeTime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime? Expires { get; set; }
|
public DateTime? Expires => DateTime.Now.AddTicks(Lifetime.Ticks);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the issuer of this <see cref="SecurityTokenDescriptor"/>.
|
/// Gets or sets the issuer of this <see cref="SecurityTokenDescriptor"/>.
|
||||||
@@ -108,6 +107,8 @@ namespace DigitalData.Core.Security.RSAKey
|
|||||||
public RSATokenDescriptor()
|
public RSATokenDescriptor()
|
||||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
{
|
{
|
||||||
|
_lazyTokenValidator = new(CreatePublicKey<RSATokenValidator>);
|
||||||
|
|
||||||
_lazyRsaSecurityKey = new(() => new RsaSecurityKey(RSA));
|
_lazyRsaSecurityKey = new(() => new RsaSecurityKey(RSA));
|
||||||
|
|
||||||
_lazySigningCredentials = new(() => SigningDigest is null
|
_lazySigningCredentials = new(() => SigningDigest is null
|
||||||
|
|||||||
17
DigitalData.Core.Security/RSAKey/RSATokenValidator.cs
Normal file
17
DigitalData.Core.Security/RSAKey/RSATokenValidator.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using DigitalData.Core.Abstractions.Security;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace DigitalData.Core.Security.RSAKey
|
||||||
|
{
|
||||||
|
public class RSATokenValidator : RSAPublicKey, IAsymmetricTokenValidator
|
||||||
|
{
|
||||||
|
private readonly Lazy<RsaSecurityKey> _lazyRsaSecurityKey;
|
||||||
|
|
||||||
|
public SecurityKey SecurityKey => _lazyRsaSecurityKey.Value;
|
||||||
|
|
||||||
|
public RSATokenValidator()
|
||||||
|
{
|
||||||
|
_lazyRsaSecurityKey = new(() => new RsaSecurityKey(RSA));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Terminal", "DigitalData.Core.Terminal\DigitalData.Core.Terminal.csproj", "{0FA93730-8084-4907-B172-87D610323796}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Terminal", "DigitalData.Core.Terminal\DigitalData.Core.Terminal.csproj", "{0FA93730-8084-4907-B172-87D610323796}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Tests.API", "DigitalData.Core.Tests.API\DigitalData.Core.Tests.API.csproj", "{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Tests.API", "DigitalData.Core.Tests.API\DigitalData.Core.Tests.API.csproj", "{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -68,8 +68,8 @@ Global
|
|||||||
{E009A053-A9F4-48F2-984F-EF5C376A9B14}.Debug|Any CPU.Build.0 = Release|Any CPU
|
{E009A053-A9F4-48F2-984F-EF5C376A9B14}.Debug|Any CPU.Build.0 = Release|Any CPU
|
||||||
{E009A053-A9F4-48F2-984F-EF5C376A9B14}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E009A053-A9F4-48F2-984F-EF5C376A9B14}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E009A053-A9F4-48F2-984F-EF5C376A9B14}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E009A053-A9F4-48F2-984F-EF5C376A9B14}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Debug|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Debug|Any CPU.Build.0 = Release|Any CPU
|
||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
{47D80C65-74A2-4EB8-96A5-D571A9108FB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
|||||||
Reference in New Issue
Block a user