43 Commits

Author SHA1 Message Date
Developer 02
988d1e2b16 feat(RSADecryptor): FileNotFoundEvent-Methode aktualisiert, um Datei zu erstellen, wenn nicht gefunden 2024-12-07 03:26:00 +01:00
Developer 02
4e0e907313 feat(RSAEncryptor): FileNotFoundEvent-Methode aktualisiert, um Datei zu erstellen, wenn nicht gefunden 2024-12-07 03:24:29 +01:00
Developer 02
0bfec426d4 refactor: Mergen von Encryptors und Decryptors in eine einzelne Sammlung
- Kombiniert `Encryptors` und `Decryptors` in `cryptographers` für eine vereinfachte Initialisierung in `OnDeserialized`.
2024-12-07 03:10:29 +01:00
Developer 02
08ffe821ff fix: Nullprüfungen und Initialisierung nach der Deserialisierung hinzufügen
- Nullprüfungen in `OnDeserialized` implementiert, um `Directory` und `FileName` für Decryptoren festzulegen.
- `FileName` mit `FileNameFormat` dynamisch erstellt.
- `TypeTagOf` verfeinert, um den richtigen Tag zu bestimmen, und Fehlerbehandlung für nicht unterstützte Kryptografietypen hinzugefügt.
2024-12-07 03:06:57 +01:00
Developer 02
fa5d0f1b26 refactor(IRSACryptographer): Init-Methode, Verzeichnis- und Dateinamen-Getter-Setter hinzugefügt 2024-12-07 02:09:32 +01:00
Developer 02
38bd23d012 refactor(RSAFactory): Entfernen der Methode ReadRSADecryptorAsync. 2024-12-07 02:01:06 +01:00
Developer 02
50e2581727 feat(RSACryptographer): Virtuelle UnableToInitPemEvent-Methode für den Fall hinzugefügt, dass sowohl pem als auch pem-Pfad null sein können 2024-12-07 01:33:56 +01:00
Developer 02
5c09d7775b feat(RSACryptographer): Virtuelle FileNotFoundEvent-Methode für nicht gefundene Pem-Datei hinzugefügt 2024-12-07 01:26:00 +01:00
Developer 02
dbfee49dee refactor(RSADecryptor): RSADecryptor, Version und Passwort entfernen und hinzufügen 2024-12-07 01:14:13 +01:00
Developer 02
0c6c84852d refactor: Validierung für Pem-Eigenschaft hinzugefügt, um Ausnahme bei Nicht-Initialisierung auszulösen
- Die Pem-Eigenschaft wurde aktualisiert, um eine Validierung hinzuzufügen, die eine InvalidOperationException auslöst, falls sie vor der Initialisierung aufgerufen wird.
 - Nicht verwendeten Import System.Text.Json.Serialization entfernt.
 - Fehlermeldungen wurden erweitert, um Issuer und Audience für eine bessere Debugging-Kontextbereitschaft einzuschließen.
2024-12-07 00:57:10 +01:00
Developer 02
3f61b5064c refactor(RSACryptographer): Verzeichnis- und Dateinamen-Intter in Setter umwandeln 2024-12-06 17:27:03 +01:00
Developer 02
f79d2e2352 refactor(IRSACryptographer): IJsonOnDeserialized-Implementierung entfernt 2024-12-06 17:22:42 +01:00
Developer 02
201da81aa5 refactor(RSACryptographer): anstatt PemPath.init zu verwenden, wurden getrennte Verzeichnis- und Dateinameneigenschaften hinzugefügt 2024-12-06 17:17:53 +01:00
Developer 02
bea57a25e8 feat(RSACryptographer) Init-Methode zur Verwaltung des pem-Importprozesses hinzugefügt 2024-12-06 15:12:21 +01:00
Developer 02
0ff89b4906 Reapply "refactor(RSACryptographer): Entfernte nullbare Eigenschaft von Issuer und Audience."
This reverts commit 600d17ef40.
2024-12-05 23:18:19 +01:00
Developer 02
600d17ef40 Revert "refactor(RSACryptographer): Entfernte nullbare Eigenschaft von Issuer und Audience."
This reverts commit 16565eca4d.
2024-12-05 23:08:13 +01:00
Developer 02
16565eca4d refactor(RSACryptographer): Entfernte nullbare Eigenschaft von Issuer und Audience.
- Schnittstelle aktualisiert
 - standardmäßig als leerer String zugewiesen.
2024-12-05 20:07:17 +01:00
Developer 02
8787c04917 refactor(AsymCryptParams): unnötige Eigenschaften entfernt 2024-12-05 15:50:53 +01:00
Developer 02
b3568216a0 refactor(IAsymCryptService): Indexer entfernt und Decryptors und Encryptors getter Methoden hinzugefügt. 2024-12-05 15:47:46 +01:00
Developer 02
6f520732dd refactor(AsymCryptService): Entschlüsselungswörterbuch entfernt 2024-12-05 15:22:23 +01:00
Developer 02
8003cffb9b refactor(CryptographerExtensions): In die Abstraktionsschicht verschieben 2024-12-05 15:20:56 +01:00
Developer 02
b02f93b38d refactor(RSACryptographerList): entfernt 2024-12-05 15:19:44 +01:00
Developer 02
2f0c6a905a chore: Hinzugefügtes ToDo 2024-12-05 15:03:28 +01:00
Developer 02
baf1f5e045 refactor(CryptographerExtensions): Aktualisiert, um IRSACryptographer anstelle von RSACryptographer zu verwenden, um die Abstraktion zu erhöhen. 2024-12-05 14:58:44 +01:00
Developer 02
b8a4a1f2b5 refactor(IRSACryptographer): Issuer und Audience Identifier String-Eigenschaften hinzugefügt 2024-12-05 14:50:05 +01:00
Developer 02
a69f610ef4 feat(CryptographerExtensions): Abfrage in SingleOrDefault verschieben 2024-12-05 14:38:32 +01:00
Developer 02
016d8bdcf2 feat(RSACryptographerList): Hinzufügen der Methode try get mit dem Wort out-key 2024-12-05 14:36:28 +01:00
Developer 02
738005f5dc feat(RSACryptographerList): Die Ausgabe der Indexer-Methode ist nicht null und wirft eine Ausnahme, wenn sie nicht gefunden wird. 2024-12-05 14:33:24 +01:00
Developer 02
c96af25e23 feat(CryptographerExtensions): Erstellt Erweiterungen zum Suchen und Erstellen von RSACryptographerList. 2024-12-05 14:26:20 +01:00
Developer 02
35e2fef046 feat(RSACryptographerList): Erstellt, um die Cryptographer-Liste sowohl als Wörterbuch als auch als IEnumerable zu verwenden 2024-12-05 13:37:34 +01:00
Developer 02
b8fb45d4a3 feat(AsymCryptService): Decryptors und Encryptors Getter hinzugefügt. 2024-12-05 13:17:23 +01:00
Developer 02
fa60147507 refactor(RSAFactoryParams): Implementierung von IJsonOnDeserialized anstelle von Lazy Initialization. 2024-12-05 12:12:56 +01:00
Developer 02
e9d408a717 feat(AsymCryptParams): EncryptedPrivateKeyFileTag, PrivateKeyFileTag, PublicKeyFileTag und RSAKeyNameSeparator aus RSAFactoryParams verschoben. 2024-12-05 11:34:35 +01:00
Developer 02
5fd3fa2fc6 feat(AsymCryptParams): IRSADecryptor-Liste und IRSAEncryptor-Liste hinzugefügt. 2024-12-05 11:31:00 +01:00
Developer 02
0d5bcedc01 refactor(DIExtensions): Umbenennung von TryAddCryptographerConverter in AddCryptographerConverter 2024-12-05 11:21:34 +01:00
Developer 02
2e68a37944 feat(HashAlgorithmNameConverter): Erstellt für benutzerfreundlichere json de/serilization.
- DI-Erweiterungsmethoden hinzugefügt
2024-12-05 11:06:11 +01:00
Developer 02
8076efb934 refactor(ReadOrCreateDirectory): Entfernt 2024-12-05 10:28:15 +01:00
Developer 02
c38f7dcf72 rektor(RSA): Umbenennung von dir in cryptographer und Verschiebung der zugehörigen Klassen 2024-12-05 10:03:39 +01:00
Developer 02
6e4942c885 feat(Config): Verzeichnis erstellt 2024-12-05 09:58:42 +01:00
Developer 02
d0dfd834b0 feat(Config): Verzeichnis erstellt und Params verschoben 2024-12-05 09:57:12 +01:00
Developer 02
aa9951f242 refactor: KeyType entfernt 2024-12-05 09:30:19 +01:00
Developer 02
506685a0b5 refactor(RSACryptographer): Verfallsdatum und Version entfernt 2024-12-05 09:17:44 +01:00
Developer 02
c9548238bb Revert "feat: CryptographerType-Enum hinzugefügt, um Schlüsseltypen darzustellen"
This reverts commit 3ffdd49a47.
2024-12-05 09:13:54 +01:00
21 changed files with 302 additions and 234 deletions

View File

@@ -0,0 +1,21 @@
namespace DigitalData.Core.Abstractions.Security
{
public static class CryptographerExtensions
{
public static IEnumerable<TRSACryptographer> GetByIssuer<TRSACryptographer>(this IEnumerable<TRSACryptographer> cryptographers, string issuer) where TRSACryptographer: IRSACryptographer
=> cryptographers.Where(c => c.Issuer == issuer);
public static IEnumerable<TRSACryptographer> GetByAudience<TRSACryptographer>(this IEnumerable<TRSACryptographer> cryptographers, string audience) where TRSACryptographer : IRSACryptographer
=> cryptographers.Where(c => c.Audience == audience);
public static TRSACryptographer Get<TRSACryptographer>(this IEnumerable<TRSACryptographer> cryptographers, string issuer, string audience) where TRSACryptographer : IRSACryptographer
=> cryptographers.Where(c => c.Issuer == issuer && c.Audience == audience).SingleOrDefault()
?? throw new InvalidOperationException($"No {typeof(TRSACryptographer).GetType().Name.TrimStart('I')} found with Issuer: {issuer} and Audience: {audience}.");
public static bool TryGet<TRSACryptographer>(this IEnumerable<TRSACryptographer> cryptographers, string issuer, string audience, out TRSACryptographer? cryptographer) where TRSACryptographer : IRSACryptographer
{
cryptographer = cryptographers.SingleOrDefault(c => c.Issuer == issuer && c.Audience == audience);
return cryptographer is not null;
}
}
}

View File

@@ -1,9 +1,9 @@
namespace DigitalData.Core.Abstractions.Security namespace DigitalData.Core.Abstractions.Security
{ {
public interface IAsymCryptService<TParams> : IRSAFactory<TParams> public interface IAsymCryptService<TParams> : IRSAFactory<TParams>
{ {
IRSADecryptor this[string key] { get; } public IEnumerable<IRSADecryptor> Decryptors { get; }
bool TryGetRSADecryptor(string key, out IRSADecryptor? decryptor); public IEnumerable<IRSAEncryptor> Encryptors { get; }
} }
} }

View File

@@ -7,5 +7,15 @@ namespace DigitalData.Core.Abstractions.Security
public string Pem { get; init; } public string Pem { get; init; }
public RSAEncryptionPadding Padding { get; init; } public RSAEncryptionPadding Padding { get; init; }
public string? Directory { get; set; }
public string? FileName { get; set; }
public string Issuer { get; init; }
public string Audience { get; init; }
public void Init();
} }
} }

View File

@@ -2,11 +2,7 @@
{ {
public interface IRSADecryptor : IRSACryptographer public interface IRSADecryptor : IRSACryptographer
{ {
(string Value, Version Version)? VersionedPassword { init; } public bool Encrypt { get; init; }
Version? PasswordVersion { get; }
bool HasEncryptedPem { get; }
IRSAEncryptor Encryptor { get; } IRSAEncryptor Encryptor { get; }

View File

@@ -12,7 +12,5 @@ namespace DigitalData.Core.Abstractions.Security
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null, PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
HashAlgorithmName? hashAlgorithmName = null, HashAlgorithmName? hashAlgorithmName = null,
int? iterationCount = null); int? iterationCount = null);
Task<IRSADecryptor> ReadRSADecryptorAsync(string path, Version? version = null, CancellationToken cancellationToken = default);
} }
} }

View File

@@ -1,7 +0,0 @@
namespace DigitalData.Core.Security
{
public class AsymCryptParams : RSAFactoryParams
{
public IEnumerable<ReadOrCreateDirectory> ReadOrCreateDirs { get; init; } = new List<ReadOrCreateDirectory>();
}
}

View File

@@ -1,4 +1,6 @@
using DigitalData.Core.Abstractions.Security; using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Cryptographer;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -6,16 +8,13 @@ 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> where TAsymCryptParams : AsymCryptParams
{ {
private readonly Dictionary<string, IRSADecryptor> _decryptors; public IEnumerable<IRSADecryptor> Decryptors => _params.Decryptors;
public IRSADecryptor this[string key] { get => _decryptors[key]; set => _decryptors[key] = value; } public IEnumerable<IRSAEncryptor> Encryptors => _params.Encryptors;
public AsymCryptService(IOptions<TAsymCryptParams> options, ILogger<AsymCryptService<TAsymCryptParams>>? logger = null) : base(options) public AsymCryptService(IOptions<TAsymCryptParams> options, ILogger<AsymCryptService<TAsymCryptParams>>? logger = null) : base(options)
{ {
_decryptors = new();
logger?.LogInformation("Core.Secrets version: {Version}, Created on: {CreationDate}.", Secrets.Version, Secrets.CreationDate.ToString("dd.MM.yyyy")); logger?.LogInformation("Core.Secrets version: {Version}, Created on: {CreationDate}.", Secrets.Version, Secrets.CreationDate.ToString("dd.MM.yyyy"));
} }
public bool TryGetRSADecryptor(string key, out IRSADecryptor? decryptor) => _decryptors.TryGetValue(key, out decryptor);
} }
} }

View File

@@ -0,0 +1,59 @@
using DigitalData.Core.Abstractions.Security;
namespace DigitalData.Core.Security.Config
{
public class AsymCryptParams : RSAFactoryParams
{
public string Directory { get; init; } = string.Empty;
/// <summary>
/// 0: Issuer - 1: Audience - 2: Type tag - 3: Version
/// </summary>
public string FileNameFormat { get; init; } = "{0}_-_{1}_-_{2}_-_{3}.pem";
public string EncryptorTag { get; init; } = "public";
public string DecryptorTag { get; init; } = "private";
public string EncryptedDecryptorTag { get; init; } = "enc-private";
public IEnumerable<IRSADecryptor> Decryptors { get; init; } = new List<IRSADecryptor>();
public IEnumerable<IRSAEncryptor> Encryptors { get; init; } = new List<IRSAEncryptor>();
private string TypeTagOf(IRSACryptographer crypt)
{
if (crypt is IRSAEncryptor)
return EncryptorTag;
else if (crypt is IRSADecryptor decryptor)
return decryptor.Encrypt ? EncryptedDecryptorTag : DecryptorTag;
else
throw new InvalidOperationException(
"Unknown cryptographer type. The crypt parameter must be either IRSAEncryptor or IRSADecryptor.");
}
public override void OnDeserialized()
{
base.OnDeserialized();
var cryptographers = Encryptors.Cast<IRSACryptographer>().Concat(Decryptors.Cast<IRSACryptographer>());
foreach (var crypt in cryptographers)
{
// set default path
if (crypt.Pem is null)
{
crypt.Directory ??= Directory;
crypt.FileName ??= string.Format(
FileNameFormat,
crypt.Issuer,
crypt.Audience,
TypeTagOf(crypt),
Secrets.Version);
}
crypt.Init();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using System.Security.Cryptography;
using System.Text.Json.Serialization;
namespace DigitalData.Core.Security.Config
{
public class RSAFactoryParams : IJsonOnDeserialized
{
public int KeySizeInBits { get; init; } = 2048;
public string PbePassword { internal get; init; } = Secrets.PBE_PASSWORD;
public PbeEncryptionAlgorithm PbeEncryptionAlgorithm { get; init; } = PbeEncryptionAlgorithm.Aes256Cbc;
public HashAlgorithmName PbeHashAlgorithmName { get; init; } = HashAlgorithmName.SHA256;
public int PbeIterationCount { get; init; } = 100_000;
public string EncryptedPrivateKeyPemLabel { get; init; } = "ENCRYPTED PRIVATE KEY";
private PbeParameters? _pbeParameters;
[JsonIgnore]
public PbeParameters PbeParameters => _pbeParameters!;
public virtual void OnDeserialized() => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount);
}
}

View File

@@ -0,0 +1,53 @@
using DigitalData.Core.Abstractions.Security;
using System.Security.Cryptography;
namespace DigitalData.Core.Security.Cryptographer
{
public class RSACryptographer : IRSACryptographer
{
protected string? _pem;
public string Pem
{
get => _pem
?? throw new InvalidOperationException($"Pem is not initialized. Please ensure that the PEM is set or properly loaded from the file. Issuer: {Issuer}, Audience: {Audience}.");
init => _pem = value;
}
public string? PemPath => FileName is null ? null : Path.Combine(Directory ?? string.Empty, FileName);
public string? Directory { get; set; }
public string? FileName { get; set; }
public RSAEncryptionPadding Padding { get; init; } = RSAEncryptionPadding.OaepSHA256;
protected virtual RSA RSA { get; } = RSA.Create();
public string Issuer { get; init; } = string.Empty;
public string Audience { get; init; } = string.Empty;
internal RSACryptographer() { }
public virtual void UnableToInitPemEvent() => throw new InvalidOperationException(
$"Pem is not initialized and pem file is null. Issuer is {Issuer} and audience {Audience}.");
public virtual void FileNotFoundEvent() => throw new FileNotFoundException(
$"Pem is not initialized and pem file is not found in {PemPath}. Issuer is {Issuer} and audience {Audience}.");
// TODO: make file read asynchronous, consider multiple routing
public virtual void Init()
{
if(_pem is null)
{
if(PemPath is null)
UnableToInitPemEvent();
if (File.Exists(PemPath))
_pem = File.ReadAllText(PemPath);
else
FileNotFoundEvent();
}
}
}
}

View File

@@ -0,0 +1,55 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Extensions;
using System.Security.Cryptography;
namespace DigitalData.Core.Security.Cryptographer
{
public class RSADecryptor : RSACryptographer, IRSADecryptor, IRSACryptographer
{
public bool Encrypt { get; init; }
private readonly Lazy<IRSAEncryptor> _lazyEncryptor;
public IRSAEncryptor Encryptor => _lazyEncryptor.Value;
public RSADecryptor()
{
_lazyEncryptor = new(() => new RSAEncryptor()
{
Pem = RSA.ExportRSAPublicKeyPem(),
Padding = Padding
});
}
public byte[] Decrypt(byte[] data) => RSA.Decrypt(data, Padding);
public string Decrypt(string data) => RSA.Decrypt(data.Base64ToByte(), Padding).BytesToString();
public override void Init()
{
base.Init();
if (Encrypt)
RSA.ImportFromEncryptedPem(Pem, Secrets.PBE_PASSWORD.AsSpan());
else
RSA.ImportFromPem(Pem);
}
public override void FileNotFoundEvent()
{
var new_decryptor = new RSADecryptor()
{
Pem = RSAFactory<RSAFactoryParams>.Static.CreateRSAPrivateKeyPem(),
Encrypt = Encrypt
};
_pem = new_decryptor.Pem;
if (PemPath is not null)
Task.Run(async () =>
{
await File.WriteAllTextAsync(_pem, PemPath);
});
}
}
}

View File

@@ -0,0 +1,37 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Extensions;
namespace DigitalData.Core.Security.Cryptographer
{
public class RSAEncryptor : RSACryptographer, IRSAEncryptor, IRSACryptographer
{
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);
}
public override void FileNotFoundEvent()
{
var new_decryptor = new RSADecryptor()
{
Pem = RSAFactory<RSAFactoryParams>.Static.CreateRSAPrivateKeyPem()
};
_pem = new_decryptor.Encryptor.Pem;
if (PemPath is not null)
Task.Run(async () =>
{
await File.WriteAllTextAsync(_pem, PemPath);
});
}
}
}

View File

@@ -1,8 +1,9 @@
using DigitalData.Core.Abstractions.Security; using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Config;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace DigitalData.Core.Security namespace DigitalData.Core.Security.Cryptographer
{ {
public class RSAFactory<TRSAFactoryParams> : IRSAFactory<TRSAFactoryParams> where TRSAFactoryParams : RSAFactoryParams public class RSAFactory<TRSAFactoryParams> : IRSAFactory<TRSAFactoryParams> where TRSAFactoryParams : RSAFactoryParams
{ {
@@ -11,7 +12,7 @@ namespace DigitalData.Core.Security
public static RSAFactory<RSAFactoryParams> Static => LazyInstance.Value; public static RSAFactory<RSAFactoryParams> Static => LazyInstance.Value;
protected readonly TRSAFactoryParams _params; protected readonly TRSAFactoryParams _params;
public RSAFactory(IOptions<TRSAFactoryParams> options) => _params = options.Value; public RSAFactory(IOptions<TRSAFactoryParams> options) => _params = options.Value;
public string CreateRSAPrivateKeyPem(int? keySizeInBits = null) public string CreateRSAPrivateKeyPem(int? keySizeInBits = null)
@@ -26,39 +27,18 @@ namespace DigitalData.Core.Security
{ {
password ??= _params.PbePassword; password ??= _params.PbePassword;
var pbeParameters = (pbeEncryptionAlgorithm is null && hashAlgorithmName is null && iterationCount is null) var pbeParameters = pbeEncryptionAlgorithm is null && hashAlgorithmName is null && iterationCount is null
? new PbeParameters( ? new PbeParameters(
pbeEncryptionAlgorithm ?? _params.PbeEncryptionAlgorithm, pbeEncryptionAlgorithm ?? _params.PbeEncryptionAlgorithm,
hashAlgorithmName ?? _params.PbeHashAlgorithmName, hashAlgorithmName ?? _params.PbeHashAlgorithmName,
iterationCount ?? _params.PbeIterationCount) iterationCount ?? _params.PbeIterationCount)
: _params.PbeParameters; : _params.PbeParameters;
var encryptedPrivateKey = RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportEncryptedPkcs8PrivateKey(password.AsSpan(), pbeParameters); var encryptedPrivateKey = RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportEncryptedPkcs8PrivateKey(password.AsSpan(), pbeParameters);
var pemChars = PemEncoding.Write(_params.EncryptedPrivateKeyPemLabel, encryptedPrivateKey); var pemChars = PemEncoding.Write(_params.EncryptedPrivateKeyPemLabel, encryptedPrivateKey);
return new string(pemChars); return new string(pemChars);
} }
public async Task<IRSADecryptor> ReadRSADecryptorAsync(string path, Version? version = null, CancellationToken cancellationToken = default)
{
var pem = await File.ReadAllTextAsync(path, cancellationToken);
(string Value, Version Version)? versionedPassword = null;
if(version is not null)
{
if (version != Secrets.Version)
throw new InvalidOperationException($"The provided version {version} does not match the expected version {Secrets.Version}.");
versionedPassword = (Secrets.PBE_PASSWORD, Secrets.Version);
}
return new RSADecryptor()
{
Pem = pem,
VersionedPassword = versionedPassword
};
}
} }
} }

View File

@@ -1,12 +0,0 @@
namespace DigitalData.Core.Security
{
namespace DigitalData.Core.Security
{
public enum CryptographicKeyType
{
PrivateKey,
EncryptedPrivateKey,
PublicKey
}
}
}

View File

@@ -1,13 +1,27 @@
using DigitalData.Core.Abstractions.Security; using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Config;
using DigitalData.Core.Security.Cryptographer;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DigitalData.Core.Security namespace DigitalData.Core.Security
{ {
public static class DIExtensions 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 AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services) private static IServiceCollection AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services)
where TAsymCryptParams : AsymCryptParams where TAsymCryptParams : AsymCryptParams
{ {

View File

@@ -0,0 +1,13 @@
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

@@ -1,50 +0,0 @@
using DigitalData.Core.Abstractions.Security;
using System.Security.Cryptography;
using System.Text.Json;
namespace DigitalData.Core.Security
{
public class RSACryptographer : IRSACryptographer
{
public required virtual string Pem { get; init; }
public RSAEncryptionPadding Padding { get; init; } = RSAEncryptionPadding.OaepSHA256;
protected virtual RSA RSA { get; } = RSA.Create();
public string? Issuer { get; init; }
public string? Audience { get; init; }
private DateOnly? _expiration;
public DateOnly? Expiration
{
get => _expiration;
init
{
if (value <= DateOnly.FromDateTime(DateTime.Now))
throw new InvalidOperationException($"Cryptographer expiration date has already passed. Cryptographer: {JsonSerializer.Serialize(this)}");
_expiration = value;
}
}
private Version? _version;
public Version? Version
{
get => _version;
init
{
if (value != Secrets.Version)
throw new InvalidOperationException($"Cryptographer version ({value}) does not match the expected version ({Secrets.Version}). Cryptographer: {JsonSerializer.Serialize(this)}");
_version = value;
}
}
internal RSACryptographer() { }
}
}

View File

@@ -1,58 +0,0 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Extensions;
using System.Security.Cryptography;
namespace DigitalData.Core.Security
{
public class RSADecryptor : RSACryptographer, IRSADecryptor, IRSACryptographer
{
public (string Value, Version Version)? VersionedPassword
{
init
{
_password = value?.Value;
PasswordVersion = value?.Version;
}
}
private string? _password;
public Version? PasswordVersion { get; private init; } = null;
public bool HasEncryptedPem => _password is not null;
public bool IsEncrypted => _password is not null;
private readonly Lazy<IRSAEncryptor> _lazyEncryptor;
public IRSAEncryptor Encryptor => _lazyEncryptor.Value;
private readonly Lazy<RSA> lazyRSA;
protected override RSA RSA => lazyRSA.Value;
public RSADecryptor()
{
_lazyEncryptor = new(() => new RSAEncryptor()
{
Pem = RSA.ExportRSAPublicKeyPem(),
Padding = Padding
});
lazyRSA = new(() =>
{
var rsa = RSA.Create();
if (_password is null)
RSA.ImportFromPem(Pem);
else
RSA.ImportFromEncryptedPem(Pem, _password.AsSpan());
return rsa;
});
}
public byte[] Decrypt(byte[] data) => RSA.Decrypt(data, Padding);
public string Decrypt(string data) => RSA.Decrypt(data.Base64ToByte(), Padding).BytesToString();
}
}

View File

@@ -1,24 +0,0 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.Extensions;
namespace DigitalData.Core.Security
{
public class RSAEncryptor : RSACryptographer, IRSAEncryptor, IRSACryptographer
{
public override required string Pem
{
get => base.Pem;
init
{
RSA.ImportFromPem(base.Pem);
base.Pem = 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;
}
}

View File

@@ -1,34 +0,0 @@
using System.Security.Cryptography;
namespace DigitalData.Core.Security
{
public class RSAFactoryParams
{
public string EncryptedPrivateKeyFileTag { get; init; } = "enc-private";
public string PrivateKeyFileTag { get; init; } = "private";
public string PublicKeyFileTag { get; init; } = "public";
public string RSAKeyNameSeparator { get; init; } = "-_-";
public int KeySizeInBits { get; init; } = 2048;
public string PbePassword { internal get; init; } = Secrets.PBE_PASSWORD;
public PbeEncryptionAlgorithm PbeEncryptionAlgorithm { get; init; } = PbeEncryptionAlgorithm.Aes256Cbc;
public HashAlgorithmName PbeHashAlgorithmName { get; init; } = HashAlgorithmName.SHA256;
public int PbeIterationCount { get; init; } = 100_000;
public string EncryptedPrivateKeyPemLabel { get; init; } = "ENCRYPTED PRIVATE KEY";
private readonly Lazy<PbeParameters> _lazyPbeParameters;
public PbeParameters PbeParameters => _lazyPbeParameters.Value;
public RSAFactoryParams()
=> _lazyPbeParameters = new(() => new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount));
}
}

View File

@@ -1,9 +0,0 @@
namespace DigitalData.Core.Security
{
public class ReadOrCreateDirectory
{
public required string Dir { get; init; }
public IEnumerable<string> ReadOrCreateFiles { get; init; } = new List<string>();
}
}