// NEW/tests/JdeScoping.Infrastructure.Tests/Security/RsaKeyServiceTests.cs using JdeScoping.Core.Interfaces; using JdeScoping.Infrastructure.Security; using Shouldly; namespace JdeScoping.Infrastructure.Tests.Security; public class RsaKeyServiceTests : IDisposable { private readonly string _testKeyPath; public RsaKeyServiceTests() { _testKeyPath = Path.Combine(Path.GetTempPath(), $"test-rsa-key-{Guid.NewGuid()}.bin"); } public void Dispose() { if (File.Exists(_testKeyPath)) File.Delete(_testKeyPath); } [Fact] public void GetPublicKeyPem_ReturnsValidPemFormat() { // Arrange var service = new RsaKeyService(_testKeyPath); // Act var pem = service.GetPublicKeyPem(); // Assert pem.ShouldStartWith("-----BEGIN PUBLIC KEY-----"); pem.ShouldEndWith("-----END PUBLIC KEY-----"); } [Fact] public void Constructor_WhenKeyFileMissing_GeneratesAndPersistsKey() { // Arrange - ensure file doesn't exist File.Exists(_testKeyPath).ShouldBeFalse(); // Act _ = new RsaKeyService(_testKeyPath); // Assert File.Exists(_testKeyPath).ShouldBeTrue(); new FileInfo(_testKeyPath).Length.ShouldBeGreaterThan(0); } [Fact] public void Constructor_WhenKeyFileExists_LoadsSameKey() { // Arrange - create service to generate key var service1 = new RsaKeyService(_testKeyPath); var publicKey1 = service1.GetPublicKeyPem(); // Act - create new service instance var service2 = new RsaKeyService(_testKeyPath); var publicKey2 = service2.GetPublicKeyPem(); // Assert - same key loaded publicKey2.ShouldBe(publicKey1); } [Fact] public void Decrypt_WithValidCiphertext_ReturnsPlaintext() { // Arrange var service = new RsaKeyService(_testKeyPath); var plaintext = "Hello, World!"u8.ToArray(); // Encrypt using public key (simulating what client does) using var rsa = System.Security.Cryptography.RSA.Create(); rsa.ImportFromPem(service.GetPublicKeyPem()); var ciphertext = rsa.Encrypt(plaintext, System.Security.Cryptography.RSAEncryptionPadding.OaepSHA256); // Act var decrypted = service.Decrypt(ciphertext); // Assert decrypted.ShouldBe(plaintext); } [Fact] public void Decrypt_WithInvalidCiphertext_ThrowsCryptographicException() { // Arrange var service = new RsaKeyService(_testKeyPath); var invalidCiphertext = new byte[] { 1, 2, 3, 4, 5 }; // Act & Assert Should.Throw( () => service.Decrypt(invalidCiphertext)); } }