using System.Security.Cryptography; using IdentityShroud.Core.Contracts; using IdentityShroud.Core.Security.Keys; namespace IdentityShroud.Core.Model; public sealed class DecryptedSigningKey : IDisposable { private readonly byte[] _keyData; private readonly int _keyLength; private bool _disposed; public RealmSigningKeyId Id { get; } public KeyType KeyType { get; } public ReadOnlySpan KeyData => _disposed ? throw new ObjectDisposedException(nameof(DecryptedSigningKey)) : _keyData.AsSpan(0, _keyLength); public DecryptedSigningKey(RealmSigningKey realmSigningKey, IDekEncryptionService encryptionService) { Id = realmSigningKey.Id; KeyType = realmSigningKey.KeyType; int keySize = encryptionService.GetDecryptedSize(realmSigningKey.Key); _keyData = GC.AllocateArray(keySize, pinned: true); _keyLength = keySize; encryptionService.Decrypt(realmSigningKey.Key, _keyData); } public DecryptedSigningKey() { Id = RealmSigningKeyId.NewId(); KeyType = KeyType.RSA; const int keySize = 2048; using var rsa = RSA.Create(); rsa.KeySize = keySize; int estimatedSize = EstimatePkcs8ExportSize(keySize); Span temp = stackalloc byte[estimatedSize * 2]; try { if (!rsa.TryExportPkcs8PrivateKey(temp, out int bytesWritten)) throw new CryptographicException("Unable to export RSA private key."); _keyData = GC.AllocateArray(bytesWritten, pinned: true); _keyLength = bytesWritten; temp[..bytesWritten].CopyTo(_keyData); } finally { CryptographicOperations.ZeroMemory(temp); } } public void Dispose() { if (_disposed) return; _disposed = true; CryptographicOperations.ZeroMemory(_keyData); } // Note actual accurate coefficients would be *0.566 and +57.4 public static int EstimatePkcs8ExportSize(int keySizeBits) => ((keySizeBits * 6) / 10) + 150; }