using DigitalData.Core.Abstractions.Security; using Microsoft.Extensions.Options; using System.Security.Cryptography; namespace DigitalData.Core.Security { public class RSAFactory : IRSAFactory where TRSAFactoryParams : RSAFactoryParams { private static readonly Lazy> LazyInstance = new(() => new(Options.Create(new()))); public static RSAFactory Static => LazyInstance.Value; protected readonly TRSAFactoryParams _params; public RSAFactory(IOptions options) => _params = options.Value; public string CreateRSAPrivateKeyPem(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) { password ??= _params.PbePassword; 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); return new string(pemChars); } public async Task 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 }; } } }