12 Commits

Author SHA1 Message Date
Developer 02
66ed34b664 refactor(StringExtensions): Fehlermeldungen wurden ausgearbeitet und der falsche Variablenname „mode“ wurde in „divisor“ umbenannt. 2024-12-18 18:19:17 +01:00
Developer 02
d7b4c382cd fix(StringExtensions): Die Hauptformel der ToTag-Methode wurde von (x / y) in [(x - 1) / y + 1] geändert, um korrekt zu gruppieren.
- Aktualisierte Dokumentationskommentare
2024-12-18 18:10:52 +01:00
Developer 02
4f6ca3524a feat(AsymCryptParams): DateTagFormat als Subtext des Dateinamens für die periodische Aktualisierung von pem-Dateien hinzugefügt. 2024-12-18 17:51:02 +01:00
Developer 02
bd1ae4246d refactor(Extensoin): entfernt, um die Projektanzahl zu minimieren. 2024-12-18 14:09:53 +01:00
Developer 02
d92475c230 feat: Indexer hinzugefügt, um RSA-Dechiffrierer über den Index im AsymCryptService zuzugreifen
- Neuer Indexer eingeführt, um den Zugriff auf Dechiffrierer über den Index zu ermöglichen und so flexibleren Zugriff auf Elemente in der Decryptors-Sammlung zu bieten.
- Validierung der Indexgrenzen hinzugefügt, um eine ArgumentOutOfRangeException zu werfen, wenn ein ungültiger Index angegeben wird.
- Bestehende Funktionalität zum Zugriff auf Dechiffrierer über den Schlüssel beibehalten.
2024-12-18 14:05:31 +01:00
Developer 02
15705cccc4 feat(AsymCryptParams): Schlüsselgröße zum Parameter Dateiname hinzugefügt 2024-12-18 13:45:51 +01:00
Developer 02
a8403087f6 feat(DIExtensions): Die Methode AddCryptographerConverter wurde entfernt. 2024-12-18 13:17:18 +01:00
Developer 02
0235c83075 feat(RSAFactoryParams): Umbenennung von PbeHashAlgorithmName in PbeHashAlgorithm und Hinzufügen von String-Proportionen mit dem Namen von PbeHashAlgorithmName.
- PbeHashAlgorithmName.init Methoden hinzugefügt, um zu versuchen, PbeHashAlgorithm mit Reflection zu initialisieren. Wenn er null ist, wird er nach dem Namen generiert.
- PbeHashAlgorithmName.get hinzugefügt, um den Namen von PbeHashAlgorithm zu erhalten.
2024-12-18 13:04:15 +01:00
Developer 02
63aeba982f feat(RSACryptographer): Eigenschaft PaddingName hinzugefügt, um Padding mit dem Namen RSAEncryptionPadding zu initialisieren. 2024-12-18 11:36:45 +01:00
Developer 02
514495fc8d refactor: Aktualisierung der String-Verschlüsselungsmethoden in RSAEncryptor
- `Encrypt`-Methode überarbeitet, um `ToBytes` und `ToBase64String` für Konsistenz und bessere Lesbarkeit zu verwenden.
- Parametername in der Methode `Encrypt(string)` von `data` zu `strData` geändert, um die Verständlichkeit zu verbessern.
2024-12-17 20:49:34 +01:00
Developer 02
9752fb14ec fix: Unterdrückung der Nullable-Warnung für den Pem-Getter in RSADecryptor
- CS8603-Warnung für den `Pem`-Getter in `RSADecryptor` mit `#pragma warning disable/restore` unterdrückt.
- Konsistentes Verhalten sichergestellt, ohne die Laufzeitlogik zu ändern.
2024-12-17 15:29:28 +01:00
Developer 02
b3629661a1 fix(AsymCryptParams): Dateinamenerweiterung hinzugefügt. 2024-12-17 14:06:09 +01:00
14 changed files with 148 additions and 68 deletions

View File

@@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -1,13 +0,0 @@
namespace DigitalData.Core.Extensions
{
public static class StringExtensions
{
public static string ToBase64String(this byte[] bytes) => Convert.ToBase64String(bytes);
public static byte[] Base64ToByte(this string base64String) => Convert.FromBase64String(base64String);
public static byte[] ToBytes(this string str) => System.Text.Encoding.UTF8.GetBytes(str);
public static string BytesToString(this byte[] bytes) => System.Text.Encoding.UTF8.GetString(bytes);
}
}

View File

@@ -31,6 +31,13 @@ namespace DigitalData.Core.Security
}
}
public IRSADecryptor this[int index] => index < 0 || index >= Decryptors.Count()
? Decryptors.ElementAt(index)
: throw new ArgumentOutOfRangeException(
nameof(index),
index,
$"The index {index} is out of range. The valid indices for {GetType()} are between 0 and {Decryptors.Count() - 1} (inclusive). Please ensure the index is within this range.");
public AsymCryptService(IOptions<TAsymCryptParams> options, ILogger<AsymCryptService<TAsymCryptParams>>? logger = null) : base(options)
{
logger?.LogInformation("Core.Secrets version: {Version}, Created on: {CreationDate}.", Secrets.Version, Secrets.CreationDate.ToString("dd.MM.yyyy"));

View File

@@ -20,6 +20,8 @@ namespace DigitalData.Core.Security.Config
/// </example>
public string FileNameSeparator { get; init; } = "_-_";
public string FileExtension { get; init; } = "pem";
/// <summary>
/// Represents the separator used to concatenate the components of a key-related token string.
/// </summary>
@@ -34,13 +36,28 @@ namespace DigitalData.Core.Security.Config
/// </example>
public string KeyNameSeparator { get; init; } = ":";
/// <summary>
///This is the subtext of the pem file name. For the file to be automatically renewed, the name must be assigned to change periodically. For example, by default MM/2 will be refreshed every 2 months.
/// <br />
/// - <see cref="StringExtensions.ToTag(DateTime, string)" /> is used when converting to tag.
/// <br />
/// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
/// by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
/// <br />
/// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
/// rounds down the outgoing number (1), adds one to the number (resulting in 2).
/// <br />
/// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
/// <br />
/// This method provides a way to format the date based on typical or customized rules, including mathematical operations like division.
/// </summary>
public string DateTagFormat { get; init; } = "MM//2";
public IEnumerable<RSADecryptor> Decryptors { get; init; } = new List<RSADecryptor>();
public RSADecryptor? Vault { get; init; }
public AsymCryptParams()
{
AfterCreate += () =>
public AsymCryptParams() => AfterCreate += () =>
{
// Create root folder if it does not exist
if (!Directory.Exists(PemDirectory))
@@ -51,11 +68,13 @@ namespace DigitalData.Core.Security.Config
// set default path
if (decryptor.IsPemNull)
{
var file_name_params = new List<object> { decryptor.Issuer, decryptor.Audience };
var file_name_params = new List<object> { decryptor.Issuer, decryptor.Audience, KeySizeInBits, DateTime.Now.ToTag(DateTagFormat) };
if (decryptor.IsEncrypted)
file_name_params.Add(Secrets.Version);
var path = Path.Combine(PemDirectory, string.Join(FileNameSeparator, file_name_params));
var file_name = $"{string.Join(FileNameSeparator, file_name_params)}.{FileExtension}";
var path = Path.Combine(PemDirectory, file_name);
if (File.Exists(path))
decryptor.SetPem(File.ReadAllText(path));
@@ -65,7 +84,7 @@ namespace DigitalData.Core.Security.Config
? Instance.RSAFactory.CreateEncryptedPrivateKeyPem(pbeParameters: PbeParameters, keySizeInBits: KeySizeInBits, password: Secrets.PBE_PASSWORD)
: Instance.RSAFactory.CreatePrivateKeyPem(keySizeInBits: KeySizeInBits);
decryptor.SetPem(File.ReadAllText(pem));
decryptor.SetPem(pem);
// Save file in background
Task.Run(async () => await File.WriteAllTextAsync(path: path, pem));
@@ -73,6 +92,5 @@ namespace DigitalData.Core.Security.Config
}
}
};
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Security.Cryptography;
using System.Reflection;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
namespace DigitalData.Core.Security.Config
@@ -11,7 +12,16 @@ namespace DigitalData.Core.Security.Config
public PbeEncryptionAlgorithm PbeEncryptionAlgorithm { get; init; } = PbeEncryptionAlgorithm.Aes256Cbc;
public HashAlgorithmName PbeHashAlgorithmName { get; init; } = HashAlgorithmName.SHA256;
public HashAlgorithmName PbeHashAlgorithm { get; init; } = HashAlgorithmName.SHA256;
// TODO: add as json converter to IConfigurIConfiguration.Config
public string PbeHashAlgorithmName
{
get => PbeHashAlgorithm.ToString();
init => PbeHashAlgorithm = (typeof(HashAlgorithmName).GetProperty(value, BindingFlags.Public | BindingFlags.Static)?.GetValue(null) is HashAlgorithmName hashAlgorithmName)
? hashAlgorithmName
: new(value);
}
public int PbeIterationCount { get; init; } = 100_000;
@@ -37,7 +47,7 @@ namespace DigitalData.Core.Security.Config
return true;
});
AfterCreate += () => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount);
AfterCreate += () => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithm, PbeIterationCount);
}
protected event Action AfterCreate;

View File

@@ -0,0 +1,83 @@
namespace DigitalData.Core.Security.Config
{
internal static class StringExtensions
{
public static string ToBase64String(this byte[] bytes) => Convert.ToBase64String(bytes);
public static byte[] Base64ToByte(this string base64String) => Convert.FromBase64String(base64String);
public static byte[] ToBytes(this string str) => System.Text.Encoding.UTF8.GetBytes(str);
public static string BytesToString(this byte[] bytes) => System.Text.Encoding.UTF8.GetString(bytes);
/// <summary>
/// Converts a <see cref="DateTime"/> to a formatted string based on the specified format string.
/// <br />
/// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
/// by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
/// <br />
/// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
/// rounds down the outgoing number (1), adds one to the number (resulting in 2).
/// <br />
/// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
/// <br />
/// </summary>
/// <param name="date">The <see cref="DateTime"/> value to be formatted.</param>
/// <param name="format">The format string that dictates the formatting of the date. If the format includes the "//" symbol,
/// it splits the string at "//" and divides the left-side value by the right-side value. The format string can include standard
/// <see cref="DateTime.ToString"/> format patterns.</param>
/// <returns>A string representation of the formatted date, or the result of the division operation if "//" is present in the format.</returns>
/// <exception cref="ArgumentException">Thrown if the format string is invalid, such as having an incorrect number of parts after "//".</exception>
/// <exception cref="DivideByZeroException">Thrown if the right side of the "//" contains a zero, resulting in division by zero.</exception>
/// <exception cref="FormatException">Thrown if either the left-side or right-side value of "//" cannot be parsed as an integer.</exception>
public static string ToTag(this DateTime date, string format)
{
if (format is not null && format.Contains("//"))
{
var subStrings = format.Split("//");
if (subStrings.Length != 2)
throw new ArgumentException($"Date tag format {format} is invalid. It must contain exactly one '//' separator.", nameof(format));
var formattedLeft = date.ToString(subStrings[0]);
if (!int.TryParse(formattedLeft, out var dateValue))
throw new FormatException($"The left-side value ({formattedLeft}) of the format could not be parsed to an integer.");
if (!int.TryParse(subStrings[1], out var divisor))
throw new FormatException($"The right-side value ({divisor}) of the format could not be parsed to an integer.");
if (divisor == 0)
throw new DivideByZeroException($"Date tag format {format} includes division by zero, which is not allowed.");
var result = (dateValue - 1) / divisor + 1;
return result.ToString();
}
return date.ToString(format);
}
/// <summary>
/// Converts a <see cref="DateTime"/> to a formatted string based on the specified format string.
/// <br />
/// - If the format contains the symbol “//”, the method divides the numeric value obtained from the left side of the format
/// by one minus the numeric value obtained from the right side of the format string and adds one. For instance:
/// <br />
/// - If the date is 02.03.2024 and the format is "MM//2", it extracts the month (02), subtracts one (3), divides it by 2,
/// rounds down the outgoing number (1), adds one to the number (resulting in 2).
/// <br />
/// - If the format does not contain "//", the method uses the default <see cref="DateTime.ToString"/> format.
/// <br />
/// This method provides a way to format the date based on typical or customized rules, including mathematical operations like division.
/// </summary>
/// <param name="date">The <see cref="DateOnly"/> value to be formatted. It will convert to DateTime to use the method shared with DateTime.</param>
/// <param name="format">The format string that dictates the formatting of the date. If the format includes the "//" symbol,
/// it splits the string at "//" and divides the left-side value by the right-side value. The format string can include standard
/// <see cref="DateTime.ToString"/> format patterns.</param>
/// <returns>A string representation of the formatted date, or the result of the division operation if "//" is present in the format.</returns>
/// <exception cref="ArgumentException">Thrown if the format string is invalid, such as having an incorrect number of parts after "//".</exception>
/// <exception cref="DivideByZeroException">Thrown if the right side of the "//" contains a zero, resulting in division by zero.</exception>
/// <exception cref="FormatException">Thrown if either the left-side or right-side value of "//" cannot be parsed as an integer.</exception>
public static string ToTag(this DateOnly date, string format) => date.ToDateTime(new()).ToTag(format);
}
}

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Abstractions.Security;
using System.Reflection;
using System.Security.Cryptography;
namespace DigitalData.Core.Security.Cryptographer
@@ -10,6 +11,13 @@ namespace DigitalData.Core.Security.Cryptographer
public RSAEncryptionPadding Padding { get; init; } = RSAEncryptionPadding.OaepSHA256;
// TODO: add as json converter to IConfigurIConfiguration.Config
public string PaddingName
{
get => Padding.ToString();
init => Padding = typeof(RSAEncryptionPadding).GetProperty(value, BindingFlags.Public | BindingFlags.Static)?.GetValue(null) as RSAEncryptionPadding ?? throw new ArgumentException($"Padding '{value}' not found.");
}
protected virtual RSA RSA { get; } = RSA.Create();
public string Issuer { get; init; } = string.Empty;

View File

@@ -1,5 +1,5 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Extensions;
using DigitalData.Core.Security.Config;
using System.Security.Cryptography;
namespace DigitalData.Core.Security.Cryptographer
@@ -10,7 +10,9 @@ namespace DigitalData.Core.Security.Cryptographer
public override string Pem
{
get => _pem ?? throw PemIsNullException;
#pragma warning disable CS8603 // Possible null reference return.
get => _pem;
#pragma warning restore CS8603 // Possible null reference return.
init
{
_pem = value;

View File

@@ -1,5 +1,5 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Extensions;
using DigitalData.Core.Security.Config;
namespace DigitalData.Core.Security.Cryptographer
{
@@ -17,7 +17,7 @@ namespace DigitalData.Core.Security.Cryptographer
public byte[] Encrypt(byte[] data) => RSA.Encrypt(data, Padding);
public string Encrypt(string data) => RSA.Encrypt(data.Base64ToByte(), Padding).BytesToString();
public string Encrypt(string data) => RSA.Encrypt(data.ToBytes(), Padding).ToBase64String();
public bool Verify(string data, string signature) => Encrypt(data) == signature;
}

View File

@@ -31,7 +31,7 @@ namespace DigitalData.Core.Security.Cryptographer
var pbeParameters = pbeEncryptionAlgorithm is null && hashAlgorithmName is null && iterationCount is null
? new PbeParameters(
pbeEncryptionAlgorithm ?? _params.PbeEncryptionAlgorithm,
hashAlgorithmName ?? _params.PbeHashAlgorithmName,
hashAlgorithmName ?? _params.PbeHashAlgorithm,
iterationCount ?? _params.PbeIterationCount)
: _params.PbeParameters;

View File

@@ -4,23 +4,11 @@ using DigitalData.Core.Security.Cryptographer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DigitalData.Core.Security
{
public static class DIExtensions
{
public static JsonSerializerOptions AddCryptographerConverter(this JsonSerializerOptions options)
{
if (!options.Converters.OfType<HashAlgorithmNameConverter>().Any())
options.Converters.Add(new HashAlgorithmNameConverter());
if (!options.Converters.OfType<JsonStringEnumConverter>().Any())
options.Converters.Add(new JsonStringEnumConverter());
return options;
}
private static IServiceCollection AddParamsConfigureOptions<TParams>(this IServiceCollection services) where TParams : RSAFactoryParams
=> services.AddSingleton<IConfigureOptions<TParams>, ParamsConfigureOptions<TParams>>();

View File

@@ -12,7 +12,6 @@
<ItemGroup>
<ProjectReference Include="..\DigitalData.Core.Abstractions\DigitalData.Core.Abstractions.csproj" />
<ProjectReference Include="..\DigitalData.Core.Extensions\DigitalData.Core.Extensions.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,13 +0,0 @@
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using System.Text.Json;
namespace DigitalData.Core.Security
{
public class HashAlgorithmNameConverter : JsonConverter<HashAlgorithmName>
{
public override HashAlgorithmName Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => new(reader.GetString() ?? string.Empty);
public override void Write(Utf8JsonWriter writer, HashAlgorithmName value, JsonSerializerOptions options) => writer.WriteStringValue(value.Name);
}
}

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.Extensions", "DigitalData.Core.Extensions\DigitalData.Core.Extensions.csproj", "{FC6AD1C4-5D7C-4B50-9330-B7A0E52B24B8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Tests.API", "DigitalData.Core.Tests.API\DigitalData.Core.Tests.API.csproj", "{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -76,10 +76,10 @@ Global
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.Build.0 = Release|Any CPU
{FC6AD1C4-5D7C-4B50-9330-B7A0E52B24B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC6AD1C4-5D7C-4B50-9330-B7A0E52B24B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC6AD1C4-5D7C-4B50-9330-B7A0E52B24B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC6AD1C4-5D7C-4B50-9330-B7A0E52B24B8}.Release|Any CPU.Build.0 = Release|Any CPU
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BC2DEC5-E89D-48CC-9A51-4D94496EE4A6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE