Compare commits
26 Commits
3ffdd49a47
...
8787c04917
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8787c04917 | ||
|
|
b3568216a0 | ||
|
|
6f520732dd | ||
|
|
8003cffb9b | ||
|
|
b02f93b38d | ||
|
|
2f0c6a905a | ||
|
|
baf1f5e045 | ||
|
|
b8a4a1f2b5 | ||
|
|
a69f610ef4 | ||
|
|
016d8bdcf2 | ||
|
|
738005f5dc | ||
|
|
c96af25e23 | ||
|
|
35e2fef046 | ||
|
|
b8fb45d4a3 | ||
|
|
fa60147507 | ||
|
|
e9d408a717 | ||
|
|
5fd3fa2fc6 | ||
|
|
0d5bcedc01 | ||
|
|
2e68a37944 | ||
|
|
8076efb934 | ||
|
|
c38f7dcf72 | ||
|
|
6e4942c885 | ||
|
|
d0dfd834b0 | ||
|
|
aa9951f242 | ||
|
|
506685a0b5 | ||
|
|
c9548238bb |
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace DigitalData.Core.Abstractions.Security
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -7,5 +7,9 @@ namespace DigitalData.Core.Abstractions.Security
|
||||
public string Pem { get; init; }
|
||||
|
||||
public RSAEncryptionPadding Padding { get; init; }
|
||||
|
||||
public string? Issuer { get; init; }
|
||||
|
||||
public string? Audience { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace DigitalData.Core.Security
|
||||
{
|
||||
public class AsymCryptParams : RSAFactoryParams
|
||||
{
|
||||
public IEnumerable<ReadOrCreateDirectory> ReadOrCreateDirs { get; init; } = new List<ReadOrCreateDirectory>();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
using DigitalData.Core.Security.Config;
|
||||
using DigitalData.Core.Security.Cryptographer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
_decryptors = new();
|
||||
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);
|
||||
}
|
||||
}
|
||||
11
DigitalData.Core.Security/Config/AsymCryptParams.cs
Normal file
11
DigitalData.Core.Security/Config/AsymCryptParams.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
|
||||
namespace DigitalData.Core.Security.Config
|
||||
{
|
||||
public class AsymCryptParams : RSAFactoryParams
|
||||
{
|
||||
public IEnumerable<IRSADecryptor> Decryptors { get; init; } = new List<IRSADecryptor>();
|
||||
|
||||
public IEnumerable<IRSAEncryptor> Encryptors { get; init; } = new List<IRSAEncryptor>();
|
||||
}
|
||||
}
|
||||
27
DigitalData.Core.Security/Config/RSAFactoryParams.cs
Normal file
27
DigitalData.Core.Security/Config/RSAFactoryParams.cs
Normal 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 void OnDeserialized() => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount);
|
||||
}
|
||||
}
|
||||
20
DigitalData.Core.Security/Cryptographer/RSACryptographer.cs
Normal file
20
DigitalData.Core.Security/Cryptographer/RSACryptographer.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace DigitalData.Core.Security.Cryptographer
|
||||
{
|
||||
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; }
|
||||
|
||||
internal RSACryptographer() { }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using DigitalData.Core.Security.Extensions;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace DigitalData.Core.Security
|
||||
namespace DigitalData.Core.Security.Cryptographer
|
||||
{
|
||||
public class RSADecryptor : RSACryptographer, IRSADecryptor, IRSACryptographer
|
||||
{
|
||||
@@ -31,7 +31,7 @@ namespace DigitalData.Core.Security
|
||||
|
||||
protected override RSA RSA => lazyRSA.Value;
|
||||
|
||||
public RSADecryptor()
|
||||
public RSADecryptor()
|
||||
{
|
||||
_lazyEncryptor = new(() => new RSAEncryptor()
|
||||
{
|
||||
@@ -50,7 +50,7 @@ namespace DigitalData.Core.Security
|
||||
return rsa;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public byte[] Decrypt(byte[] data) => RSA.Decrypt(data, Padding);
|
||||
|
||||
public string Decrypt(string data) => RSA.Decrypt(data.Base64ToByte(), Padding).BytesToString();
|
||||
@@ -1,13 +1,13 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
using DigitalData.Core.Security.Extensions;
|
||||
|
||||
namespace DigitalData.Core.Security
|
||||
namespace DigitalData.Core.Security.Cryptographer
|
||||
{
|
||||
public class RSAEncryptor : RSACryptographer, IRSAEncryptor, IRSACryptographer
|
||||
{
|
||||
public override required string Pem
|
||||
{
|
||||
get => base.Pem;
|
||||
{
|
||||
get => base.Pem;
|
||||
init
|
||||
{
|
||||
RSA.ImportFromPem(base.Pem);
|
||||
@@ -1,8 +1,9 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
using DigitalData.Core.Security.Config;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace DigitalData.Core.Security
|
||||
namespace DigitalData.Core.Security.Cryptographer
|
||||
{
|
||||
public class RSAFactory<TRSAFactoryParams> : IRSAFactory<TRSAFactoryParams> where TRSAFactoryParams : RSAFactoryParams
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace DigitalData.Core.Security
|
||||
public static RSAFactory<RSAFactoryParams> Static => LazyInstance.Value;
|
||||
|
||||
protected readonly TRSAFactoryParams _params;
|
||||
|
||||
|
||||
public RSAFactory(IOptions<TRSAFactoryParams> options) => _params = options.Value;
|
||||
|
||||
public string CreateRSAPrivateKeyPem(int? keySizeInBits = null)
|
||||
@@ -26,13 +27,13 @@ namespace DigitalData.Core.Security
|
||||
{
|
||||
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(
|
||||
pbeEncryptionAlgorithm ?? _params.PbeEncryptionAlgorithm,
|
||||
hashAlgorithmName ?? _params.PbeHashAlgorithmName,
|
||||
iterationCount ?? _params.PbeIterationCount)
|
||||
: _params.PbeParameters;
|
||||
|
||||
|
||||
var encryptedPrivateKey = RSA.Create(keySizeInBits ?? _params.KeySizeInBits).ExportEncryptedPkcs8PrivateKey(password.AsSpan(), pbeParameters);
|
||||
|
||||
var pemChars = PemEncoding.Write(_params.EncryptedPrivateKeyPemLabel, encryptedPrivateKey);
|
||||
@@ -46,7 +47,7 @@ namespace DigitalData.Core.Security
|
||||
|
||||
(string Value, Version Version)? versionedPassword = null;
|
||||
|
||||
if(version is not 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}.");
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace DigitalData.Core.Security
|
||||
{
|
||||
namespace DigitalData.Core.Security
|
||||
{
|
||||
public enum CryptographicKeyType
|
||||
{
|
||||
PrivateKey,
|
||||
EncryptedPrivateKey,
|
||||
PublicKey
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,27 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
using DigitalData.Core.Security.Config;
|
||||
using DigitalData.Core.Security.Cryptographer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
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 AddAsymCryptService<TAsymCryptParams>(this IServiceCollection services)
|
||||
where TAsymCryptParams : AsymCryptParams
|
||||
{
|
||||
|
||||
13
DigitalData.Core.Security/HashAlgorithmNameConverter.cs
Normal file
13
DigitalData.Core.Security/HashAlgorithmNameConverter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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() { }
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user