using DigitalData.Core.Abstractions.Security; using System.Security.Cryptography; namespace DigitalData.Core.Security { public class CryptFactory : ICryptFactory { private static readonly Lazy LazyInstance = new (() => new ()); public static CryptFactory Instance => LazyInstance.Value; public int KeySizeInBits { get; init; } = 2048; public string PbePassword { private 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; private readonly Lazy _lazyPbeParameters; public PbeParameters PbeParameters => _lazyPbeParameters.Value; public string EncryptedPrivateKeyPemLabel { get; init; } = "ENCRYPTED PRIVATE KEY"; public CryptFactory() { _lazyPbeParameters = new(() => new PbeParameters(PbeEncryptionAlgorithm, PbeHashAlgorithmName, PbeIterationCount)); } public string CreateRSAPrivateKeyPem(int? keySizeInBits = null) => RSA.Create(keySizeInBits ?? KeySizeInBits).ExportRSAPrivateKeyPem(); public string CreateEncryptedPrivateKeyPem( int? keySizeInBits = null, string? password = null, PbeEncryptionAlgorithm? pbeEncryptionAlgorithm = null, HashAlgorithmName? hashAlgorithmName = null, int? iterationCount = null) { password ??= PbePassword; var pbeParameters = (pbeEncryptionAlgorithm is null && hashAlgorithmName is null && iterationCount is null) ? new PbeParameters( pbeEncryptionAlgorithm ?? PbeEncryptionAlgorithm, hashAlgorithmName ?? PbeHashAlgorithmName, iterationCount ?? PbeIterationCount) : PbeParameters; var encryptedPrivateKey = RSA.Create(keySizeInBits ?? KeySizeInBits).ExportEncryptedPkcs8PrivateKey(password.AsSpan(), pbeParameters); var pemChars = PemEncoding.Write(EncryptedPrivateKeyPemLabel, encryptedPrivateKey); return new string(pemChars); } } }