using DigitalData.Core.Abstractions.Security; using Microsoft.Extensions.Logging; using System.Text; namespace DigitalData.Core.Security { public class CryptFactory : RSAFactory, ICryptFactory { private readonly IDictionary _decryptors; public IRSADecryptor this[string key] { get => _decryptors[key]; set => _decryptors[key] = value; } public Func RSADecryptorKeyFormatter { get; } public CryptFactory(ILogger logger, IDictionary decryptors, Func rsaDecryptorKeyFormatter) : base() { _decryptors = decryptors ?? new Dictionary(); RSADecryptorKeyFormatter = rsaDecryptorKeyFormatter; 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); public static readonly string DefaultEncryptedPrivateKeyFileTag = "enc-private"; public static readonly string DefaultPrivateKeyFileTag = "private"; public static readonly string DefaultPublicKeyFileTag = "public"; public static readonly IEnumerable KeyFileTags = new string[] { DefaultEncryptedPrivateKeyFileTag, DefaultPrivateKeyFileTag, DefaultPublicKeyFileTag }; private static readonly Lazy> LazyLowerFileTags = new(() => KeyFileTags.Select(tag => tag.ToLower())); //TODO: make the validation using regex public static string DefaultRSADecryptorKeyFormatter(IRSADecryptor decryptor, string issuer, string audience, string separator = "-_-") { void ValidateForbidden(string value, string paramName) { if (Path.GetInvalidFileNameChars().Any(value.Contains) || LazyLowerFileTags.Value.Any(tag => value.ToLower().Contains(tag))) throw new ArgumentException($"RSA decryptor key name creation is forbidden. The {paramName} contains forbidden characters that are not allowed in file naming.", paramName); } static void ValidateSeparator(string value, string paramName, string separator) { if (value.Contains(separator)) throw new ArgumentException($"RSA decryptor key name creation is forbidden. The {paramName} contains separator characters ({separator}) that are not allowed in file naming.", paramName); } ValidateForbidden(issuer, nameof(issuer)); ValidateForbidden(audience, nameof(audience)); ValidateForbidden(separator, nameof(separator)); ValidateSeparator(issuer, nameof(issuer), separator); ValidateSeparator(audience, nameof(audience), separator); var sb = new StringBuilder(issuer.Length + audience.Length + separator.Length * 2 + 20); sb.Append(issuer).Append(separator).Append(audience).Append(separator); if (decryptor.HasEncryptedPem) sb.Append("e-private").Append(separator).Append(decryptor.PasswordVersion); else sb.Append("private"); var rsaKey = sb.Append(".pem").ToString(); return rsaKey; } } }