10 Commits

14 changed files with 82 additions and 44 deletions

View File

@@ -9,7 +9,7 @@
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<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>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Copyright>Copyright 2024</Copyright>
@@ -17,9 +17,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackAsTool>False</PackAsTool>
<PackageIcon>core_icon.png</PackageIcon>
<Version>3.0.0</Version>
<AssemblyVersion>3.0.0</AssemblyVersion>
<FileVersion>3.0.0</FileVersion>
<Version>3.1.0</Version>
<AssemblyVersion>3.1.0</AssemblyVersion>
<FileVersion>3.1.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -2,7 +2,7 @@
{
public interface IAsymmetricKey
{
string Id { get; }
string? Id { get; }
string Content { get; }
}

View File

@@ -7,7 +7,9 @@ namespace DigitalData.Core.Abstractions.Security
/// </summary>
public interface IAsymmetricTokenDescriptor : IAsymmetricPrivateKey, IUniqueSecurityContext
{
string? ApiRoute { get; }
IAsymmetricTokenValidator Validator { get; }
TimeSpan Lifetime { get; init; }
#region SecurityTokenDescriptor Map
/// <summary>

View File

@@ -0,0 +1,9 @@
using Microsoft.IdentityModel.Tokens;
namespace DigitalData.Core.Abstractions.Security
{
public interface IAsymmetricTokenValidator : IAsymmetricPublicKey
{
SecurityKey SecurityKey { get; }
}
}

View File

@@ -10,8 +10,6 @@ namespace DigitalData.Core.Abstractions.Security
SecurityToken CreateToken(TPrincipal subject, string issuer, string audience);
SecurityToken CreateToken(TPrincipal subject, string apiRoute);
string WriteToken(SecurityToken token);
}
}

View File

@@ -57,9 +57,6 @@ namespace DigitalData.Core.Abstractions.Security
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));
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
}
}

View File

@@ -47,13 +47,6 @@ namespace DigitalData.Core.Security.Config
public CryptoFactoryParams()
{
// set defaults
if (VaultDecryptor is not null)
VaultDecryptor.Id = "vault";
foreach (var descriptor in TokenDescriptors)
descriptor.IdSeparator = FileNameSeparator;
// init decryptors
AfterCreate += () =>
{
@@ -72,7 +65,17 @@ namespace DigitalData.Core.Security.Config
// set default path
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)
file_name_params.Add(Secrets.Version);

View File

@@ -4,8 +4,28 @@
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<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>
<ItemGroup>
<None Include="..\..\nuget-package-icons\core_icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />

View File

@@ -36,13 +36,5 @@ namespace DigitalData.Core.Security
?? throw new InvalidOperationException($"No or multiple token description found for issuer '{issuer}' and audience '{audience}'.");
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);
}
}
}

View File

@@ -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.
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.
public string? Id { get; init; }
protected virtual RSA RSA { get; } = RSA.Create();
}
}

View File

@@ -1,5 +1,4 @@
using DigitalData.Core.Abstractions.Security;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
namespace DigitalData.Core.Security.RSAKey
@@ -24,16 +23,16 @@ namespace DigitalData.Core.Security.RSAKey
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 RSAPrivateKey()
{
_lazyPublicKey = new(() => new RSAPublicKey()
{
Content = RSA.ExportRSAPublicKeyPem()
});
_lazyPublicKey = new(CreatePublicKey<RSAPublicKey>);
}
internal void SetPem(string pem)

View File

@@ -8,13 +8,11 @@ namespace DigitalData.Core.Security.RSAKey
/// </summary>
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 string? ApiRoute { get; init; }
public required TimeSpan Lifetime { get; init; }
#region SecurityTokenDescriptor Map
/// <summary>
@@ -34,8 +32,9 @@ namespace DigitalData.Core.Security.RSAKey
/// <summary>
/// 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>
public DateTime? Expires { get; set; }
public DateTime? Expires => DateTime.Now.AddTicks(Lifetime.Ticks);
/// <summary>
/// Gets or sets the issuer of this <see cref="SecurityTokenDescriptor"/>.
@@ -108,6 +107,8 @@ namespace DigitalData.Core.Security.RSAKey
public RSATokenDescriptor()
#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));
_lazySigningCredentials = new(() => SigningDigest is null

View 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));
}
}
}

View File

@@ -25,7 +25,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Terminal", "DigitalData.Core.Terminal\DigitalData.Core.Terminal.csproj", "{0FA93730-8084-4907-B172-87D610323796}"
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
Global
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}.Release|Any CPU.ActiveCfg = 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.Build.0 = 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 = 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
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU