Compare commits
17 Commits
8787c04917
...
feat/clien
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
988d1e2b16 | ||
|
|
4e0e907313 | ||
|
|
0bfec426d4 | ||
|
|
08ffe821ff | ||
|
|
fa5d0f1b26 | ||
|
|
38bd23d012 | ||
|
|
50e2581727 | ||
|
|
5c09d7775b | ||
|
|
dbfee49dee | ||
|
|
0c6c84852d | ||
|
|
3f61b5064c | ||
|
|
f79d2e2352 | ||
|
|
201da81aa5 | ||
|
|
bea57a25e8 | ||
|
|
0ff89b4906 | ||
|
|
600d17ef40 | ||
|
|
16565eca4d |
@@ -8,8 +8,14 @@ namespace DigitalData.Core.Abstractions.Security
|
||||
|
||||
public RSAEncryptionPadding Padding { get; init; }
|
||||
|
||||
public string? Issuer { get; init; }
|
||||
public string? Directory { get; set; }
|
||||
|
||||
public string? Audience { get; init; }
|
||||
public string? FileName { get; set; }
|
||||
|
||||
public string Issuer { get; init; }
|
||||
|
||||
public string Audience { get; init; }
|
||||
|
||||
public void Init();
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,7 @@
|
||||
{
|
||||
public interface IRSADecryptor : IRSACryptographer
|
||||
{
|
||||
(string Value, Version Version)? VersionedPassword { init; }
|
||||
|
||||
Version? PasswordVersion { get; }
|
||||
|
||||
bool HasEncryptedPem { get; }
|
||||
public bool Encrypt { get; init; }
|
||||
|
||||
IRSAEncryptor Encryptor { get; }
|
||||
|
||||
|
||||
@@ -12,7 +12,5 @@ namespace DigitalData.Core.Abstractions.Security
|
||||
PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null,
|
||||
HashAlgorithmName? hashAlgorithmName = null,
|
||||
int? iterationCount = null);
|
||||
|
||||
Task<IRSADecryptor> ReadRSADecryptorAsync(string path, Version? version = null, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,56 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,6 @@ namespace DigitalData.Core.Security.Config
|
||||
[JsonIgnore]
|
||||
public PbeParameters PbeParameters => _pbeParameters!;
|
||||
|
||||
public void OnDeserialized() => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount);
|
||||
public virtual void OnDeserialized() => _pbeParameters = new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount);
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,49 @@ namespace DigitalData.Core.Security.Cryptographer
|
||||
{
|
||||
public class RSACryptographer : IRSACryptographer
|
||||
{
|
||||
public required virtual string Pem { get; init; }
|
||||
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; }
|
||||
public string Issuer { get; init; } = string.Empty;
|
||||
|
||||
public string? Audience { get; init; }
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using DigitalData.Core.Abstractions.Security;
|
||||
using DigitalData.Core.Security.Config;
|
||||
using DigitalData.Core.Security.Extensions;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
@@ -6,31 +7,12 @@ namespace DigitalData.Core.Security.Cryptographer
|
||||
{
|
||||
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;
|
||||
public bool Encrypt { get; init; }
|
||||
|
||||
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()
|
||||
@@ -38,21 +20,36 @@ namespace DigitalData.Core.Security.Cryptographer
|
||||
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();
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +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 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;
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,26 +40,5 @@ namespace DigitalData.Core.Security.Cryptographer
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user