10 Commits

Author SHA1 Message Date
Developer 02
6a92466490 feat: hinzugefügte Index-Eigenschaft zur IAsymCryptService-Schnittstelle
- Neue Index-Eigenschaft `this[string key]` zur `IAsymCryptService`-Schnittstelle hinzugefügt.
- Ermöglicht das Abrufen spezifischer `IRSADecryptor`-Instanzen anhand eines Schlüsselstrings.
- Schnittstellendefinition aktualisiert, um die Funktionalität für implementierende Klassen zu erweitern.
2024-12-13 16:59:02 +01:00
Developer 02
5d9d756b91 feat: hinzugefügte Index-Eigenschaft zur Abfrage eines spezifischen IRSADecryptor anhand eines Schlüssels
- Neue Index-Eigenschaft `this[string key]` in `AsymCryptService` eingeführt, um spezifische `IRSADecryptor`-Instanzen basierend auf Issuer- und Audience-Schlüsseln abzurufen.
- Validierung des Schlüsselformats und Fehlerbehandlung für Fälle hinzugefügt, in denen kein passender Decryptor gefunden wird.
- Implementierung aktualisiert, um die Kompatibilität mit der bestehenden Decryptor-Enumerationslogik sicherzustellen.
2024-12-13 16:57:30 +01:00
Developer 02
f14aaa75e1 refactor(AsymCryptParams): Umbenennung von Separator in FileNameSeparator.
- KeyNameSeparator hinzugefügt.
2024-12-13 16:40:00 +01:00
Developer 02
249f5a0ae5 feat(AsymCryptService): Encryptors.get hinzugefügt, um die Verschlüsseler der Entschlüsseler zu numerieren. 2024-12-13 16:34:53 +01:00
Developer 02
30177cf0c7 feat(AsymCryptService): Implementiert IEnumerable<IRSADecryptor> 2024-12-13 16:25:21 +01:00
Developer 02
68ef0a7537 refactor(RSACryptographer): Entfernen von _pem, IsPemNull, SetPem, Init und Methoden zur Vereinfachung von RSAEncryptor 2024-12-13 16:17:35 +01:00
Developer 02
fe2ee78d14 refactor(RSADecryptor): Umbenennung der Eigenschaft Encrypt in IsEncrypted 2024-12-13 16:02:25 +01:00
Developer 02
53e6f37a09 refactor(AsymCryptParams): Umbenennung von crypt in der Schleife in decryptor 2024-12-13 15:59:28 +01:00
Developer 02
7ec85b4e30 refactor(AsymCryptParams): Unnötige Methoden entfernt 2024-12-13 15:57:17 +01:00
Developer 02
a9ebc406f3 refactor(RSAFactory): Methode CreateEncryptedPrivateKeyPem hinzugefügt, um mit direkt benutzerdefinierten pbeParametern zu erstellen.
- Umbenennung der Methode CreateRSAPrivateKeyPem in CreatePrivateKeyPem
2024-12-13 15:45:09 +01:00
10 changed files with 133 additions and 64 deletions

View File

@@ -3,6 +3,8 @@
public interface IAsymCryptService : IRSAFactory
{
public IEnumerable<IRSADecryptor> Decryptors { get; }
public IRSADecryptor this[string key] { get; }
}
public interface IAsymCryptService<TParams> : IAsymCryptService, IRSAFactory<TParams> { }

View File

@@ -11,7 +11,5 @@ namespace DigitalData.Core.Abstractions.Security
public string Issuer { get; init; }
public string Audience { get; init; }
public void Init();
}
}

View File

@@ -2,7 +2,7 @@
{
public interface IRSADecryptor : IRSACryptographer
{
public bool Encrypt { get; init; }
public bool IsEncrypted { get; init; }
IRSAEncryptor Encryptor { get; }

View File

@@ -4,14 +4,19 @@ namespace DigitalData.Core.Abstractions.Security
{
public interface IRSAFactory
{
string CreateRSAPrivateKeyPem(int? keySizeInBits = null);
string CreatePrivateKeyPem(int? keySizeInBits = null);
string CreateEncryptedPrivateKeyPem(
int? keySizeInBits = null,
string? password = null,
public string CreateEncryptedPrivateKeyPem(
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
HashAlgorithmName? hashAlgorithmName = null,
int? iterationCount = null);
int? iterationCount = null,
int? keySizeInBits = null,
string? password = null);
public string CreateEncryptedPrivateKeyPem(
PbeParameters pbeParameters,
int? keySizeInBits = null,
string? password = null);
}
public interface IRSAFactory<TParams> : IRSAFactory { }

View File

@@ -3,16 +3,47 @@ using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Cryptographer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections;
namespace DigitalData.Core.Security
{
public class AsymCryptService<TAsymCryptParams> : RSAFactory<TAsymCryptParams>, IAsymCryptService<TAsymCryptParams>, IRSAFactory<TAsymCryptParams> where TAsymCryptParams : AsymCryptParams
public class AsymCryptService<TAsymCryptParams> : RSAFactory<TAsymCryptParams>, IAsymCryptService<TAsymCryptParams>, IRSAFactory<TAsymCryptParams>, IEnumerable<IRSADecryptor>
where TAsymCryptParams : AsymCryptParams
{
public IEnumerable<IRSADecryptor> Decryptors => _params.Decryptors;
public IRSADecryptor this[string key]
{
get
{
var key_params = key.Split(_params.KeyNameSeparator);
if (key_params.Length != 2)
throw new ArgumentException($"Invalid key format. Expected two segments separated by '{_params.KeyNameSeparator}', but received: '{key}'.", nameof(key));
return _params.Decryptors.FirstOrDefault(d => d.Issuer == key_params[0] && d.Audience == key_params[1])
?? throw new KeyNotFoundException($"No decryptor found matching the issuer '{key_params[0]}' and audience '{key_params[1]}'.");
}
}
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"));
}
public IEnumerator<IRSADecryptor> GetEnumerator() => Decryptors.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Decryptors.GetEnumerator();
public IEnumerable<IRSAEncryptor> Encryptors
{
get
{
foreach (var decryptor in Decryptors)
{
yield return decryptor.Encryptor;
}
}
}
}
}

View File

@@ -6,19 +6,35 @@ namespace DigitalData.Core.Security.Config
{
public string PemDirectory { get; init; } = string.Empty;
public string Separator { get; init; } = "_-_";
public IEnumerable<RSADecryptor> Decryptors { get; init; } = new List<RSADecryptor>();
/// <summary>
/// Represents the separator used to concatenate the components of a file-related token string.
/// </summary>
/// <remarks>
/// The resulting file-related token string is constructed as follows:
/// <c>string.Join(FileNameSeparator, Issuer, Audience, Secret_version)</c>.
/// If <c>Secret_version</c> is not null, it will be included in the concatenation.
/// </remarks>
/// <example>
/// For example, if <c>FileNameSeparator = "_-_"</c>, the output might look like:
/// <c>"Issuer_-_Audience_-_Secret_version"</c>.
/// </example>
public string FileNameSeparator { get; init; } = "_-_";
/// <summary>
/// 0: Issuer - 1: Audience - 2: Secret version (if is encrypted)
/// Represents the separator used to concatenate the components of a key-related token string.
/// </summary>
private string CreateFileName(params object[] objs) => string.Join(Separator, objs);
/// <remarks>
/// The resulting key-related token string is constructed as follows:
/// <c>string.Join(KeyNameSeparator, Issuer, Audience, Secret_version)</c>.
/// If <c>Secret_version</c> is not null, it will be included in the concatenation.
/// </remarks>
/// <example>
/// For example, if <c>KeyNameSeparator = ":"</c>, the output might look like:
/// <c>"Issuer:Audience:Secret_version"</c>.
/// </example>
public string KeyNameSeparator { get; init; } = ":";
private string CreatePem(bool isEncrypted) => isEncrypted
? Instance.RSAFactory.CreateEncryptedPrivateKeyPem(keySizeInBits: KeySizeInBits, password: Secrets.PBE_PASSWORD,
pbeEncryptionAlgorithm: PbeEncryptionAlgorithm, hashAlgorithmName: PbeHashAlgorithmName, iterationCount: PbeIterationCount)
: Instance.RSAFactory.CreateRSAPrivateKeyPem(keySizeInBits: KeySizeInBits);
public IEnumerable<RSADecryptor> Decryptors { get; init; } = new List<RSADecryptor>();
public override void OnDeserialized()
{
@@ -28,29 +44,33 @@ namespace DigitalData.Core.Security.Config
if (!Directory.Exists(PemDirectory))
Directory.CreateDirectory(PemDirectory);
foreach (var crypt in Decryptors)
foreach (var decryptor in Decryptors)
{
// set default path
if (crypt.IsPemNull)
if (decryptor.IsPemNull)
{
var file_name_params = new List<object> { crypt.Issuer, crypt.Audience };
if (crypt.Encrypt)
var file_name_params = new List<object> { decryptor.Issuer, decryptor.Audience };
if (decryptor.IsEncrypted)
file_name_params.Add(Secrets.Version);
var file_name = CreateFileName(file_name_params);
var path = Path.Combine(PemDirectory, file_name);
var path = Path.Combine(PemDirectory, string.Join(FileNameSeparator, file_name_params));
if (File.Exists(path))
crypt.SetPem(File.ReadAllText(path));
decryptor.SetPem(File.ReadAllText(path));
else
{
var pem = CreatePem(crypt.Encrypt);
crypt.SetPem(File.ReadAllText(pem));
var pem = decryptor.IsEncrypted
? Instance.RSAFactory.CreateEncryptedPrivateKeyPem(pbeParameters: PbeParameters, keySizeInBits: KeySizeInBits, password: Secrets.PBE_PASSWORD)
: Instance.RSAFactory.CreatePrivateKeyPem(keySizeInBits: KeySizeInBits);
decryptor.SetPem(File.ReadAllText(pem));
// Save file in background
Task.Run(async () => await File.WriteAllTextAsync(path: path, pem));
}
}
crypt.Init();
decryptor.Init();
}
}
}

View File

@@ -5,19 +5,8 @@ namespace DigitalData.Core.Security.Cryptographer
{
public class RSACryptographer : IRSACryptographer
{
protected string? _pem;
public string Pem
{
get => _pem
?? throw PemIsNullException;
init => _pem = value;
}
internal bool IsPemNull => _pem is null;
private InvalidOperationException PemIsNullException => new($"Pem is not initialized. Please ensure that the PEM is set or properly loaded from the file. Issuer: {Issuer}, Audience: {Audience}.");
public virtual string Pem { get; init; }
public RSAEncryptionPadding Padding { get; init; } = RSAEncryptionPadding.OaepSHA256;
protected virtual RSA RSA { get; } = RSA.Create();
@@ -26,14 +15,8 @@ namespace DigitalData.Core.Security.Cryptographer
public string Audience { get; init; } = string.Empty;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
internal RSACryptographer() { }
internal void SetPem(string pem) => _pem = pem;
public virtual void Init()
{
if (_pem is null)
throw PemIsNullException;
}
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}
}

View File

@@ -6,7 +6,13 @@ namespace DigitalData.Core.Security.Cryptographer
{
public class RSADecryptor : RSACryptographer, IRSADecryptor, IRSACryptographer
{
public bool Encrypt { get; init; }
private string? _pem;
public override string Pem { get => _pem ?? throw PemIsNullException; init => _pem = value; }
public bool IsPemNull => _pem is null;
public bool IsEncrypted { get; init; }
private readonly Lazy<IRSAEncryptor> _lazyEncryptor;
@@ -25,13 +31,19 @@ namespace DigitalData.Core.Security.Cryptographer
public string Decrypt(string data) => RSA.Decrypt(data.Base64ToByte(), Padding).BytesToString();
public override void Init()
internal void SetPem(string pem) => _pem = pem;
public void Init()
{
base.Init();
if (Encrypt)
if (_pem is null)
throw PemIsNullException;
if (IsEncrypted)
RSA.ImportFromEncryptedPem(Pem, Secrets.PBE_PASSWORD.AsSpan());
else
RSA.ImportFromPem(Pem);
}
private InvalidOperationException PemIsNullException => new($"Pem is not initialized. Please ensure that the PEM is set or properly loaded from the file. Issuer: {Issuer}, Audience: {Audience}.");
}
}

View File

@@ -4,17 +4,21 @@ using DigitalData.Core.Security.Extensions;
namespace DigitalData.Core.Security.Cryptographer
{
public class RSAEncryptor : RSACryptographer, IRSAEncryptor, IRSACryptographer
{
{
public override string Pem
{
get => base.Pem;
init
{
base.Pem = value;
RSA.ImportFromPem(value);
}
}
public byte[] Encrypt(byte[] data) => RSA.Encrypt(data, Padding);
public string Encrypt(string data) => RSA.Encrypt(data.Base64ToByte(), Padding).BytesToString();
public bool Verify(string data, string signature) => Encrypt(data) == signature;
public override void Init()
{
base.Init();
RSA.ImportFromPem(base.Pem);
}
}
}

View File

@@ -11,15 +11,15 @@ namespace DigitalData.Core.Security.Cryptographer
public RSAFactory(IOptions<TRSAFactoryParams> options) => _params = options.Value;
public string CreateRSAPrivateKeyPem(int? keySizeInBits = null)
public string CreatePrivateKeyPem(int? keySizeInBits = null)
=> RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportRSAPrivateKeyPem();
public string CreateEncryptedPrivateKeyPem(
int? keySizeInBits = null,
string? password = null,
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
HashAlgorithmName? hashAlgorithmName = null,
int? iterationCount = null)
int? iterationCount = null,
int? keySizeInBits = null,
string? password = null)
{
password ??= _params.PbePassword;
@@ -36,5 +36,19 @@ namespace DigitalData.Core.Security.Cryptographer
return new string(pemChars);
}
public string CreateEncryptedPrivateKeyPem(
PbeParameters pbeParameters,
int? keySizeInBits = null,
string? password = null)
{
password ??= _params.PbePassword;
var encryptedPrivateKey = RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportEncryptedPkcs8PrivateKey(password.AsSpan(), pbeParameters);
var pemChars = PemEncoding.Write(_params.EncryptedPrivateKeyPemLabel, encryptedPrivateKey);
return new string(pemChars);
}
}
}