diff --git a/AdvancedSystems.Security.Tests/AdvancedSystems.Security.Tests.csproj b/AdvancedSystems.Security.Tests/AdvancedSystems.Security.Tests.csproj
index 06e6fc1..e4a25e3 100644
--- a/AdvancedSystems.Security.Tests/AdvancedSystems.Security.Tests.csproj
+++ b/AdvancedSystems.Security.Tests/AdvancedSystems.Security.Tests.csproj
@@ -13,7 +13,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/AdvancedSystems.Security.Tests/Cryptography/CryptoRandomProviderTests.cs b/AdvancedSystems.Security.Tests/Cryptography/CryptoRandomProviderTests.cs
index 72aaef3..d3fe3ec 100644
--- a/AdvancedSystems.Security.Tests/Cryptography/CryptoRandomProviderTests.cs
+++ b/AdvancedSystems.Security.Tests/Cryptography/CryptoRandomProviderTests.cs
@@ -7,10 +7,17 @@
namespace AdvancedSystems.Security.Tests.Cryptography;
-public class CryptoRandomProviderTests
+///
+/// Tests the public methods in .
+///
+public sealed class CryptoRandomProviderTests
{
#region Tests
+ ///
+ /// Tests that returns an
+ /// array of non-zero bytes with the correct size.
+ ///
[Fact]
public void TestGetBytes()
{
@@ -25,6 +32,9 @@ public void TestGetBytes()
Assert.All(buffer.ToArray(), b => Assert.InRange(b, byte.MinValue, byte.MaxValue));
}
+ ///
+ /// Tests heuristically that returns an integer.
+ ///
[Fact]
public void TestGetInt32()
{
@@ -42,6 +52,9 @@ public void TestGetInt32()
Assert.All(randomNumbers, x => Assert.InRange(x, int.MinValue, int.MaxValue));
}
+ ///
+ /// Tests heuristically that returns an integer.
+ ///
[Fact]
public void TestGetInt32_MinMax()
{
@@ -61,6 +74,10 @@ public void TestGetInt32_MinMax()
Assert.All(randomNumbers, x => Assert.InRange(x, min, max - 1));
}
+ ///
+ /// Tests that changes the order
+ /// of elements in an array.
+ ///
[Fact]
public void TestShuffle()
{
@@ -75,6 +92,10 @@ public void TestShuffle()
Assert.NotEqual(array1, array2);
}
+ ///
+ /// Tests that elements returned by
+ /// are elements of the original collection.
+ ///
[Fact]
public void TestChoice()
{
diff --git a/AdvancedSystems.Security.Tests/Cryptography/HashTests.cs b/AdvancedSystems.Security.Tests/Cryptography/HashTests.cs
index 90f2f4b..1fc11c6 100644
--- a/AdvancedSystems.Security.Tests/Cryptography/HashTests.cs
+++ b/AdvancedSystems.Security.Tests/Cryptography/HashTests.cs
@@ -8,10 +8,25 @@
namespace AdvancedSystems.Security.Tests.Cryptography;
-public class HashTests
+///
+/// Tests the public methods in .
+///
+public sealed class HashTests
{
#region Tests
+ ///
+ /// Tests that the computed hash returns a well-formatted string.
+ ///
+ ///
+ /// The input to compute the hash code for.
+ ///
+ ///
+ /// The expected result.
+ ///
+ ///
+ /// The formatting to use.
+ ///
[Theory]
[InlineData("Hello, World!", "65a8e27d8879283831b664bd8b7f0ad4", Format.Hex)]
[InlineData("Hello, World!", "ZajifYh5KDgxtmS9i38K1A==", Format.Base64)]
@@ -31,6 +46,18 @@ public void TestMd5Hash(string input, string expected, Format format)
Assert.Equal(expected, md5);
}
+ ///
+ /// Tests that the computed hash returns a well-formatted string.
+ ///
+ ///
+ /// The input to compute the hash code for.
+ ///
+ ///
+ /// The expected result.
+ ///
+ ///
+ /// The formatting to use.
+ ///
[Theory]
[InlineData("Hello, World!", "0a0a9f2a6772942557ab5355d76af442f8f65e01", Format.Hex)]
[InlineData("Hello, World!", "CgqfKmdylCVXq1NV12r0Qvj2XgE=", Format.Base64)]
@@ -50,6 +77,18 @@ public void TestSHA1Hash(string input, string expected, Format format)
Assert.Equal(expected, sha1);
}
+ ///
+ /// Tests that the computed hash returns a well-formatted string.
+ ///
+ ///
+ /// The input to compute the hash code for.
+ ///
+ ///
+ /// The expected result.
+ ///
+ ///
+ /// The formatting to use.
+ ///
[Theory]
[InlineData("Hello, World!", "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f", Format.Hex)]
[InlineData("Hello, World!", "3/1gIbsr1bCvZ2KQgJ7DpTGR3YHH9wpLKGiKNiGCmG8=", Format.Base64)]
@@ -69,6 +108,18 @@ public void TestSHA256Hash(string input, string expected, Format format)
Assert.Equal(expected, sha256);
}
+ ///
+ /// Tests that the computed hash returns a well-formatted string.
+ ///
+ ///
+ /// The input to compute the hash code for.
+ ///
+ ///
+ /// The expected result.
+ ///
+ ///
+ /// The formatting to use.
+ ///
[Theory]
[InlineData("Hello, World!", "5485cc9b3365b4305dfb4e8337e0a598a574f8242bf17289e0dd6c20a3cd44a089de16ab4ab308f63e44b1170eb5f515", Format.Hex)]
[InlineData("Hello, World!", "VIXMmzNltDBd+06DN+ClmKV0+CQr8XKJ4N1sIKPNRKCJ3harSrMI9j5EsRcOtfUV", Format.Base64)]
@@ -88,6 +139,18 @@ public void TestSHA384Hash(string input, string expected, Format format)
Assert.Equal(expected, sha384);
}
+ ///
+ /// Tests that the computed hash returns a well-formatted string.
+ ///
+ ///
+ /// The input to compute the hash code for.
+ ///
+ ///
+ /// The expected result.
+ ///
+ ///
+ /// The formatting to use.
+ ///
[Theory]
[InlineData("Hello, World!", "374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6cc69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387", Format.Hex)]
[InlineData("Hello, World!", "N015SpXNz9izWZMYX++bo2jxYNja9DLQi6nx7R5avmzGkpHg+i/gAGpSVw7xjBne9OYXwzzlLvCm5fvjGMsDhw==", Format.Base64)]
diff --git a/AdvancedSystems.Security.Tests/DependencyInjection/ServiceCollectionExtensionsTests.cs b/AdvancedSystems.Security.Tests/DependencyInjection/ServiceCollectionExtensionsTests.cs
new file mode 100644
index 0000000..f20fcfa
--- /dev/null
+++ b/AdvancedSystems.Security.Tests/DependencyInjection/ServiceCollectionExtensionsTests.cs
@@ -0,0 +1,250 @@
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+
+using AdvancedSystems.Security.Abstractions;
+using AdvancedSystems.Security.DependencyInjection;
+using AdvancedSystems.Security.Options;
+
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+using Xunit;
+
+namespace AdvancedSystems.Security.Tests.DependencyInjection;
+
+///
+/// Tests the public methods in .
+///
+public sealed class ServiceCollectionExtensionsTests
+{
+ #region AddCertificateService Tests
+
+ ///
+ /// Tests that can be initialized through dependency injection from configuration options.
+ ///
+ [Fact]
+ public async Task TestAddCertificateService_FromOptions()
+ {
+ // Arrange
+ using var hostBuilder = await new HostBuilder()
+ .ConfigureWebHost(builder => builder
+ .UseTestServer()
+ .ConfigureServices(services =>
+ {
+ services.AddCertificateService(options =>
+ {
+ options.Thumbprint = "123456789";
+ options.Store = new CertificateStoreOptions
+ {
+ Location = StoreLocation.CurrentUser,
+ Name = StoreName.My,
+ };
+ });
+ })
+ .Configure(app =>
+ {
+
+ }))
+ .StartAsync();
+
+ // Act
+ var certificateService = hostBuilder.Services.GetService();
+ var certificate = certificateService?.GetConfiguredCertificate();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.NotNull(certificateService);
+ Assert.Null(certificate);
+ });
+
+ await hostBuilder.StopAsync();
+ }
+
+ ///
+ /// Tests that can be initialized through dependency injection from configuration sections.
+ ///
+ [Fact]
+ public async Task TestAddCertificateService_FromAppSettings()
+ {
+ // Arrange
+ using var hostBuilder = await new HostBuilder()
+ .ConfigureWebHost(builder => builder
+ .UseTestServer()
+ .ConfigureAppConfiguration(config =>
+ {
+ config.AddJsonFile("appsettings.json", optional: false);
+ })
+ .ConfigureServices((context, services) =>
+ {
+ services.AddCertificateService(context.Configuration.GetSection(Sections.CERTIFICATE));
+ })
+ .Configure(app =>
+ {
+
+ }))
+ .StartAsync();
+
+ // Act
+ var certificateService = hostBuilder.Services.GetService();
+ var certificate = certificateService?.GetConfiguredCertificate();
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.NotNull(certificateService);
+ Assert.Null(certificate);
+ });
+
+ await hostBuilder.StopAsync();
+ }
+
+ #endregion
+
+ #region AddCertificateStore Tests
+
+ ///
+ /// Tests that can be initialized through dependency injection from configuration options.
+ ///
+ [Fact]
+ public async Task TestAddCertificateStore_FromOptions()
+ {
+ // Arrange
+ using var hostBuilder = await new HostBuilder()
+ .ConfigureWebHost(builder => builder
+ .UseTestServer()
+ .ConfigureServices(services =>
+ {
+ services.AddCertificateStore(options =>
+ {
+ options.Name = StoreName.My;
+ options.Location = StoreLocation.CurrentUser;
+ });
+ })
+ .Configure(app =>
+ {
+
+ }))
+ .StartAsync();
+
+ // Act
+ var certificateStore = hostBuilder.Services.GetService();
+
+ // Assert
+ Assert.NotNull(certificateStore);
+ await hostBuilder.StopAsync();
+ }
+
+ ///
+ /// Tests that can be initialized through dependency injection from configuration sections.
+ ///
+ [Fact]
+ public async Task TestAddCertificateStore_FromAppSettings()
+ {
+ // Arrange
+ using var hostBuilder = await new HostBuilder()
+ .ConfigureWebHost(builder => builder
+ .UseTestServer()
+ .ConfigureAppConfiguration(config =>
+ {
+ config.AddJsonFile("appsettings.json", optional: false);
+ })
+ .ConfigureServices((context, services) =>
+ {
+ services.AddCertificateStore(context.Configuration.GetSection($"{Sections.CERTIFICATE}:{Sections.STORE}"));
+ })
+ .Configure(app =>
+ {
+
+ }))
+ .StartAsync();
+
+ // Act
+ var certificateStore = hostBuilder.Services.GetService();
+
+ // Assert
+ Assert.NotNull(certificateStore);
+ await hostBuilder.StopAsync();
+ }
+
+ #endregion
+
+ #region AddCryptoRandomService Tests
+
+ ///
+ /// Tests that can be initialized through dependency injection.
+ ///
+ [Fact]
+ public async Task TestAddCryptoRandomService()
+ {
+ // Arrange
+ using var hostBuilder = await new HostBuilder()
+ .ConfigureWebHost(builder =>
+ {
+ builder.UseTestServer();
+ builder.ConfigureServices(services =>
+ {
+ services.AddCryptoRandomService();
+ });
+ builder.Configure(app =>
+ {
+
+ });
+ })
+ .StartAsync();
+
+ // Act
+ var cryptoRandomService = hostBuilder.Services.GetService();
+
+ // Assert
+ Assert.NotNull(cryptoRandomService);
+ await hostBuilder.StopAsync();
+ }
+
+ #endregion
+
+ #region AddHashService Tests
+
+ ///
+ /// Tests that can be initialized through dependency injection.
+ ///
+ [Fact]
+ public async Task TestAddHashService()
+ {
+ // Arrange
+ using var hostBuilder = await new HostBuilder()
+ .ConfigureWebHost(builder => builder
+ .UseTestServer()
+ .ConfigureServices(services =>
+ {
+ services.AddHashService();
+ })
+ .Configure(app =>
+ {
+
+ }))
+ .StartAsync();
+
+ string input = "The quick brown fox jumps over the lazy dog";
+ byte[] buffer = Encoding.UTF8.GetBytes(input);
+
+ // Act
+ var hashService = hostBuilder.Services.GetService();
+ string? sha256 = hashService?.GetSHA256Hash(buffer);
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.NotNull(hashService);
+ Assert.Equal("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", sha256);
+ });
+
+ await hostBuilder.StopAsync();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/AdvancedSystems.Security.Tests/Extensions/CertificateExtensionsTests.cs b/AdvancedSystems.Security.Tests/Extensions/CertificateExtensionsTests.cs
new file mode 100644
index 0000000..65eacdb
--- /dev/null
+++ b/AdvancedSystems.Security.Tests/Extensions/CertificateExtensionsTests.cs
@@ -0,0 +1,67 @@
+using AdvancedSystems.Security.Cryptography;
+using AdvancedSystems.Security.Extensions;
+
+using Xunit;
+
+namespace AdvancedSystems.Security.Tests.Extensions;
+
+///
+/// Tests the public methods in .
+///
+public sealed class CertificateExtensionsTests
+{
+ #region Tests
+
+ ///
+ /// Tests that
+ /// extracts the RDNs from the DN correctly from the string.
+ ///
+ [Fact]
+ public void TestTryParseDistinguishedName()
+ {
+ // Arrange
+ string commonName = "Advanced Systems Root";
+ string organizationalUnit = "R&D Department";
+ string organization = "Advanced Systems Inc.";
+ string locality = "Berlin";
+ string state = "Berlin";
+ string country = "DE";
+ string distinguiedName = $"CN={commonName}, OU={organizationalUnit}, O={organization}, L={locality}, S={state}, C={country}";
+
+ // Act
+ bool isDn = CertificateExtensions.TryParseDistinguishedName(distinguiedName, out DistinguishedName? dn);
+
+ // Assert
+ Assert.Multiple(() =>
+ {
+ Assert.True(isDn);
+ Assert.NotNull(dn);
+ Assert.Equal(dn.CommonName, commonName);
+ Assert.Equal(dn.OrganizationalUnit, organizationalUnit);
+ Assert.Equal(dn.Organization, organization);
+ Assert.Equal(dn.Locality, locality);
+ Assert.Equal(dn.State, state);
+ Assert.Equal(dn.Country, country);
+ });
+ }
+
+ ///
+ /// Tests that
+ /// when the DN is malformed.
+ ///
+ [Fact]
+ public void TestTryParseDistinguishedName_ReturnsNull()
+ {
+ // Arrange
+ string distinguishedName = string.Empty;
+
+ // Act
+ bool isDn = CertificateExtensions.TryParseDistinguishedName(distinguishedName, out DistinguishedName? dn);
+
+ // Assert
+ Assert.False(isDn);
+ Assert.Null(dn);
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/AdvancedSystems.Security.Tests/Services/CertificateServiceTests.cs b/AdvancedSystems.Security.Tests/Services/CertificateServiceTests.cs
index f5fd591..3a4d706 100644
--- a/AdvancedSystems.Security.Tests/Services/CertificateServiceTests.cs
+++ b/AdvancedSystems.Security.Tests/Services/CertificateServiceTests.cs
@@ -1,25 +1,20 @@
using System.Linq;
using System.Security.Cryptography.X509Certificates;
-using System.Threading.Tasks;
using AdvancedSystems.Security.Abstractions;
-using AdvancedSystems.Security.DependencyInjection;
using AdvancedSystems.Security.Options;
using AdvancedSystems.Security.Tests.Fixtures;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
using Moq;
using Xunit;
namespace AdvancedSystems.Security.Tests.Services;
-public class CertificateServiceTests : IClassFixture
+///
+/// Tests the public methods in .
+///
+public sealed class CertificateServiceTests : IClassFixture
{
private readonly CertificateFixture _sut;
@@ -30,6 +25,10 @@ public CertificateServiceTests(CertificateFixture fixture)
#region Tests
+ ///
+ /// Tests that
+ /// returns a mocked certificate from the certificate store.
+ ///
[Fact]
public void TestGetStoreCertificate()
{
@@ -43,11 +42,19 @@ public void TestGetStoreCertificate()
var certificate = this._sut.CertificateService.GetStoreCertificate(thumbprint, StoreName.My, StoreLocation.CurrentUser);
// Assert
- Assert.NotNull(certificate);
- Assert.Equal(thumbprint, certificate.Thumbprint);
+ Assert.Multiple(() =>
+ {
+ Assert.NotNull(certificate);
+ Assert.Equal(thumbprint, certificate.Thumbprint);
+ });
+
this._sut.Store.Verify(service => service.Open(It.Is(flags => flags == OpenFlags.ReadOnly)));
}
+ ///
+ /// Tests that
+ /// returns if a certificate could not be found in the certificate store.
+ ///
[Fact]
public void TestGetStoreCertificate_NotFound()
{
@@ -65,6 +72,10 @@ public void TestGetStoreCertificate_NotFound()
Assert.Null(certificate);
}
+ ///
+ /// Tests that
+ /// returns a mocked certificate from the certificate store.
+ ///
[Fact]
public void GetConfiguredCertificate()
{
@@ -90,11 +101,19 @@ public void GetConfiguredCertificate()
var certificate = this._sut.CertificateService.GetConfiguredCertificate();
// Assert
- Assert.NotNull(certificate);
- Assert.Equal(certificateOptions.Thumbprint, certificate.Thumbprint);
+ Assert.Multiple(() =>
+ {
+ Assert.NotNull(certificate);
+ Assert.Equal(certificateOptions.Thumbprint, certificate.Thumbprint);
+ });
+
this._sut.Store.Verify(service => service.Open(It.Is(flags => flags == OpenFlags.ReadOnly)));
}
+ ///
+ /// Tests that
+ /// returns if a certificate could not be found in the certificate store.
+ ///
[Fact]
public void GetConfiguredCertificate_NotFound()
{
@@ -122,71 +141,5 @@ public void GetConfiguredCertificate_NotFound()
Assert.Null(certificate);
}
- [Fact]
- public async Task TestAddCertificateService_FromOptions()
- {
- // Arrange
- using var hostBuilder = await new HostBuilder()
- .ConfigureWebHost(builder => builder
- .UseTestServer()
- .ConfigureServices(services =>
- {
- services.AddCertificateService(options =>
- {
- options.Thumbprint = "123456789";
- options.Store = new CertificateStoreOptions
- {
- Location = StoreLocation.CurrentUser,
- Name = StoreName.My,
- };
- });
- })
- .Configure(app =>
- {
-
- }))
- .StartAsync();
-
- // Act
- var certificateService = hostBuilder.Services.GetService();
- var certificate = certificateService?.GetConfiguredCertificate();
-
- // Assert
- Assert.NotNull(certificateService);
- Assert.Null(certificate);
- await hostBuilder.StopAsync();
- }
-
- [Fact]
- public async Task TestAddCertificateService_FromAppSettings()
- {
- // Arrange
- using var hostBuilder = await new HostBuilder()
- .ConfigureWebHost(builder => builder
- .UseTestServer()
- .ConfigureAppConfiguration(config =>
- {
- config.AddJsonFile("appsettings.json", optional: false);
- })
- .ConfigureServices((context, services) =>
- {
- services.AddCertificateService(context.Configuration);
- })
- .Configure(app =>
- {
-
- }))
- .StartAsync();
-
- // Act
- var certificateService = hostBuilder.Services.GetService();
- var certificate = certificateService?.GetConfiguredCertificate();
-
- // Assert
- Assert.NotNull(certificateService);
- Assert.Null(certificate);
- await hostBuilder.StopAsync();
- }
-
#endregion
}
\ No newline at end of file
diff --git a/AdvancedSystems.Security.Tests/Services/CertificateStoreTests.cs b/AdvancedSystems.Security.Tests/Services/CertificateStoreTests.cs
index 79e4742..e586213 100644
--- a/AdvancedSystems.Security.Tests/Services/CertificateStoreTests.cs
+++ b/AdvancedSystems.Security.Tests/Services/CertificateStoreTests.cs
@@ -1,21 +1,14 @@
-using System.Security.Cryptography.X509Certificates;
-using System.Threading.Tasks;
-
-using AdvancedSystems.Security.Abstractions;
-using AdvancedSystems.Security.DependencyInjection;
+using AdvancedSystems.Security.Abstractions;
using AdvancedSystems.Security.Tests.Fixtures;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
using Xunit;
namespace AdvancedSystems.Security.Tests.Services;
-public class CertificateStoreTests : IClassFixture
+///
+/// Tests the public methods in .
+///
+public sealed class CertificateStoreTests : IClassFixture
{
private readonly CertificateStoreFixture _sut;
@@ -26,64 +19,7 @@ public CertificateStoreTests(CertificateStoreFixture fixture)
#region Tests
- [Fact]
- public async Task TestAddCertificateStore_FromOptions()
- {
- // Arrange
- using var hostBuilder = await new HostBuilder()
- .ConfigureWebHost(builder => builder
- .UseTestServer()
- .ConfigureServices(services =>
- {
- services.AddCertificateStore(options =>
- {
- options.Name = StoreName.My;
- options.Location = StoreLocation.CurrentUser;
- });
- })
- .Configure(app =>
- {
-
- }))
- .StartAsync();
-
- // Act
- var certificateStore = hostBuilder.Services.GetService();
-
- // Assert
- Assert.NotNull(certificateStore);
- await hostBuilder.StopAsync();
- }
-
- [Fact]
- public async Task TestAddCertificateStore_FromAppSettings()
- {
- // Arrange
- using var hostBuilder = await new HostBuilder()
- .ConfigureWebHost(builder => builder
- .UseTestServer()
- .ConfigureAppConfiguration(config =>
- {
- config.AddJsonFile("appsettings.json", optional: false);
- })
- .ConfigureServices((context, services) =>
- {
-
- services.AddCertificateStore(context.Configuration);
- })
- .Configure(app =>
- {
-
- }))
- .StartAsync();
-
- // Act
- var certificateStore = hostBuilder.Services.GetService();
-
- // Assert
- Assert.NotNull(certificateStore);
- await hostBuilder.StopAsync();
- }
+ // TODO
#endregion
}
\ No newline at end of file
diff --git a/AdvancedSystems.Security.Tests/Services/CryptoRandomServiceTests.cs b/AdvancedSystems.Security.Tests/Services/CryptoRandomServiceTests.cs
index 0bb3f2c..df2f796 100644
--- a/AdvancedSystems.Security.Tests/Services/CryptoRandomServiceTests.cs
+++ b/AdvancedSystems.Security.Tests/Services/CryptoRandomServiceTests.cs
@@ -1,21 +1,20 @@
using System;
using System.Linq;
-using System.Threading.Tasks;
using AdvancedSystems.Security.Abstractions;
-using AdvancedSystems.Security.DependencyInjection;
using AdvancedSystems.Security.Tests.Fixtures;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-
using Xunit;
namespace AdvancedSystems.Security.Tests.Services;
-public class CryptoRandomServiceTests : IClassFixture
+///
+/// Tests the public methods in .
+///
+///
+/// These methods are more exhaustively tested by the underlying provider class.
+///
+public sealed class CryptoRandomServiceTests : IClassFixture
{
private readonly CryptoRandomFixture _sut;
@@ -26,6 +25,10 @@ public CryptoRandomServiceTests(CryptoRandomFixture fixture)
#region Tests
+ ///
+ /// Tests that returns
+ /// an array of elements of the correct size.
+ ///
[Fact]
public void TestGetBytes()
{
@@ -91,35 +94,11 @@ public void TestChoice()
int randomNumber = this._sut.CryptoRandomService.Choice(array);
// Assert
- Assert.Contains(randomNumber, array);
- Assert.InRange(randomNumber, min, max - 1);
- }
-
- [Fact]
- public async Task TestAddCryptoRandomService()
- {
- // Arrange
- using var hostBuilder = await new HostBuilder()
- .ConfigureWebHost(builder =>
- {
- builder.UseTestServer();
- builder.ConfigureServices(services =>
- {
- services.AddCryptoRandomService();
- });
- builder.Configure(app =>
- {
-
- });
- })
- .StartAsync();
-
- // Act
- var cryptoRandomService = hostBuilder.Services.GetService();
-
- // Assert
- Assert.NotNull(cryptoRandomService);
- await hostBuilder.StopAsync();
+ Assert.Multiple(() =>
+ {
+ Assert.Contains(randomNumber, array);
+ Assert.InRange(randomNumber, min, max - 1);
+ });
}
#endregion
diff --git a/AdvancedSystems.Security.Tests/Services/HashServiceTests.cs b/AdvancedSystems.Security.Tests/Services/HashServiceTests.cs
index 3bea864..243a4c5 100644
--- a/AdvancedSystems.Security.Tests/Services/HashServiceTests.cs
+++ b/AdvancedSystems.Security.Tests/Services/HashServiceTests.cs
@@ -1,15 +1,9 @@
using System;
using System.Text;
-using System.Threading.Tasks;
using AdvancedSystems.Security.Abstractions;
-using AdvancedSystems.Security.DependencyInjection;
using AdvancedSystems.Security.Tests.Fixtures;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Moq;
@@ -18,7 +12,13 @@
namespace AdvancedSystems.Security.Tests.Services;
-public class HashServiceTests : IClassFixture
+///
+/// Tests the public methods in .
+///
+///
+/// These methods are more exhaustively tested by the underlying provider class.
+///
+public sealed class HashServiceTests : IClassFixture
{
private readonly HashServiceFixture _sut;
@@ -29,6 +29,10 @@ public HashServiceTests(HashServiceFixture fixture)
#region Tests
+ ///
+ /// Tests that returns the expected hash,
+ /// and that the log warning message is called.
+ ///
[Fact]
public void TestMD5Hash()
{
@@ -52,6 +56,10 @@ public void TestMD5Hash()
);
}
+ ///
+ /// Tests that returns the expected hash,
+ /// and that the log warning message is called.
+ ///
[Fact]
public void TestSHA1Hash()
{
@@ -75,6 +83,9 @@ public void TestSHA1Hash()
);
}
+ ///
+ /// Tests that returns the expected hash.
+ ///
[Fact]
public void TestSHA256Hash()
{
@@ -89,6 +100,9 @@ public void TestSHA256Hash()
Assert.Equal("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", sha256);
}
+ ///
+ /// Tests that returns the expected hash.
+ ///
[Fact]
public void TestSHA384Hash()
{
@@ -103,6 +117,9 @@ public void TestSHA384Hash()
Assert.Equal("ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1", sha384);
}
+ ///
+ /// Tests that returns the expected hash.
+ ///
[Fact]
public void TestSHA512Hash()
{
@@ -117,35 +134,5 @@ public void TestSHA512Hash()
Assert.Equal("07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", sha512);
}
- [Fact]
- public async Task TestAddHashService()
- {
- // Arrange
- using var hostBuilder = await new HostBuilder()
- .ConfigureWebHost(builder => builder
- .UseTestServer()
- .ConfigureServices(services =>
- {
- services.AddHashService();
- })
- .Configure(app =>
- {
-
- }))
- .StartAsync();
-
- string input = "The quick brown fox jumps over the lazy dog";
- byte[] buffer = Encoding.UTF8.GetBytes(input);
-
- // Act
- var hashService = hostBuilder.Services.GetService();
- string? sha256 = hashService?.GetSHA256Hash(buffer);
-
- // Assert
- Assert.NotNull(hashService);
- Assert.Equal("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", sha256);
- await hostBuilder.StopAsync();
- }
-
#endregion
}
\ No newline at end of file
diff --git a/AdvancedSystems.Security/AdvancedSystems.Security.csproj b/AdvancedSystems.Security/AdvancedSystems.Security.csproj
index 2326e78..11b168d 100644
--- a/AdvancedSystems.Security/AdvancedSystems.Security.csproj
+++ b/AdvancedSystems.Security/AdvancedSystems.Security.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/AdvancedSystems.Security/Cryptography/DistinguishedName.cs b/AdvancedSystems.Security/Cryptography/DistinguishedName.cs
new file mode 100644
index 0000000..5d07ce1
--- /dev/null
+++ b/AdvancedSystems.Security/Cryptography/DistinguishedName.cs
@@ -0,0 +1,61 @@
+using System.Security.Cryptography.X509Certificates;
+
+namespace AdvancedSystems.Security.Cryptography;
+
+///
+/// Defines the Distinguished Name (DN) of an entity that owns or issued the certificate.
+/// This entity is typically a Certificate Authority (CA) or an intermediate CA in a chain of trust.
+///
+///
+/// DN is a term that describes the identifying information in a certificate and is
+/// part of the itself. A certificate contains DN
+/// information for both the owner or requestor of the certificate (called the Subject DN)
+/// and the CA that issues the certificate (called the Issuer DN). Depending on the
+/// identification policy of the CA that issues a certificate, the DN can include a variety
+/// of information.
+///
+///
+public sealed record DistinguishedName
+{
+ ///
+ /// Gets or sets the .
+ ///
+ ///
+ ///
+ ///
+ public string? Country { get; init; }
+
+ ///
+ /// Gets or sets the .
+ ///
+ ///
+ ///
+ ///
+ public string? CommonName { get; init; }
+
+ ///
+ /// Gets or sets the .
+ ///
+ ///
+ ///
+ ///
+ public string? Locality { get; init; }
+
+ ///
+ /// Gets or sets the .
+ ///
+ public string? Organization { get; init; }
+
+ ///
+ /// Gets or sets the .
+ ///
+ public string? OrganizationalUnit { get; init; }
+
+ ///
+ /// Gets or sets the ..
+ ///
+ ///
+ ///
+ ///
+ public string? State { get; init; }
+}
\ No newline at end of file
diff --git a/AdvancedSystems.Security/Cryptography/RDN.cs b/AdvancedSystems.Security/Cryptography/RDN.cs
new file mode 100644
index 0000000..4dc8de1
--- /dev/null
+++ b/AdvancedSystems.Security/Cryptography/RDN.cs
@@ -0,0 +1,55 @@
+namespace AdvancedSystems.Security.Cryptography;
+
+///
+/// The Relative Distinguished Names (RDNs) are attribute-value pairs that
+/// uniquely identify an entity.
+///
+///
+/// This class defines the attributes that annotate one or more values.
+///
+///
+///
+public static class RDN
+{
+ ///
+ /// Country (C)
+ ///
+ ///
+ /// This field could also be used to store the region.
+ ///
+ public const string C = "C";
+
+ ///
+ /// Common Name (CN)
+ ///
+ ///
+ /// This field denotes the certificate owner's common name.
+ ///
+ public const string CN = "CN";
+
+ ///
+ /// Locality (L)
+ ///
+ ///
+ /// This field could also be used to store the city.
+ ///
+ public const string L = "L";
+
+ ///
+ /// Organization (O)
+ ///
+ public const string O = "O";
+
+ ///
+ /// Organizational Unit (OU)
+ ///
+ public const string OU = "OU";
+
+ ///
+ /// State (S)
+ ///
+ ///
+ /// This field could also be used to store the province.
+ ///
+ public const string S = "S";
+}
\ No newline at end of file
diff --git a/AdvancedSystems.Security/DependencyInjection/Sections.cs b/AdvancedSystems.Security/DependencyInjection/Sections.cs
new file mode 100644
index 0000000..21a9db9
--- /dev/null
+++ b/AdvancedSystems.Security/DependencyInjection/Sections.cs
@@ -0,0 +1,24 @@
+using AdvancedSystems.Security.Options;
+
+namespace AdvancedSystems.Security.DependencyInjection;
+
+///
+/// Defines section keys for appsettings.json.
+///
+public static partial class Sections
+{
+ ///
+ /// Key used to bind the configuration section.
+ ///
+ public const string CERTIFICATE = "Certificate";
+
+ ///
+ /// Key used to bind the configuration section.
+ ///
+ public const string RSA = "RSA";
+
+ ///
+ /// Key used to bind the configuration section.
+ ///
+ public const string STORE = "Store";
+}
\ No newline at end of file
diff --git a/AdvancedSystems.Security/DependencyInjection/ServiceCollectionExtensions.cs b/AdvancedSystems.Security/DependencyInjection/ServiceCollectionExtensions.cs
index 51165e7..5dac396 100644
--- a/AdvancedSystems.Security/DependencyInjection/ServiceCollectionExtensions.cs
+++ b/AdvancedSystems.Security/DependencyInjection/ServiceCollectionExtensions.cs
@@ -1,5 +1,6 @@
using System;
+using AdvancedSystems.Core.DependencyInjection;
using AdvancedSystems.Security.Abstractions;
using AdvancedSystems.Security.Options;
using AdvancedSystems.Security.Services;
@@ -11,10 +12,19 @@
namespace AdvancedSystems.Security.DependencyInjection;
-public static class ServiceCollectionExtensions
+public static partial class ServiceCollectionExtensions
{
#region CryptoRandom
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// The value of .
+ ///
public static IServiceCollection AddCryptoRandomService(this IServiceCollection services)
{
services.TryAdd(ServiceDescriptor.Scoped());
@@ -25,6 +35,15 @@ public static IServiceCollection AddCryptoRandomService(this IServiceCollection
#region HashService
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// The value of .
+ ///
public static IServiceCollection AddHashService(this IServiceCollection services)
{
services.TryAdd(ServiceDescriptor.Scoped());
@@ -35,7 +54,7 @@ public static IServiceCollection AddHashService(this IServiceCollection services
#region CertificateStore
- internal static void AddCertificateStore(this IServiceCollection services) where TOptions : class
+ private static void AddCertificateStore(this IServiceCollection services) where TOptions : class
{
services.TryAdd(ServiceDescriptor.Singleton(serviceProvider =>
{
@@ -50,6 +69,18 @@ internal static void AddCertificateStore(this IServiceCollection servi
}));
}
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// An action used to configure .
+ ///
+ ///
+ /// The value of .
+ ///
public static IServiceCollection AddCertificateStore(this IServiceCollection services, Action setupAction)
{
services.AddOptions()
@@ -59,13 +90,21 @@ public static IServiceCollection AddCertificateStore(this IServiceCollection ser
return services;
}
- public static IServiceCollection AddCertificateStore(this IServiceCollection services, IConfiguration configuration)
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// A configuration section targeting .
+ ///
+ ///
+ /// The value of .
+ ///
+ public static IServiceCollection AddCertificateStore(this IServiceCollection services, IConfigurationSection configurationSection)
{
- services.AddOptions()
- .Bind(configuration.GetRequiredSection(Sections.CERTIFICATE_STORE))
- .ValidateDataAnnotations()
- .ValidateOnStart();
-
+ services.TryAddOptions(configurationSection);
services.AddCertificateStore();
return services;
}
@@ -74,11 +113,23 @@ public static IServiceCollection AddCertificateStore(this IServiceCollection ser
#region CertificateService
- internal static void AddCertificateService(this IServiceCollection services)
+ private static void AddCertificateService(this IServiceCollection services)
{
services.TryAdd(ServiceDescriptor.Scoped());
}
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// An action used to configure .
+ ///
+ ///
+ /// The value of .
+ ///
public static IServiceCollection AddCertificateService(this IServiceCollection services, Action setupAction)
{
services.AddOptions()
@@ -90,14 +141,23 @@ public static IServiceCollection AddCertificateService(this IServiceCollection s
return services;
}
- public static IServiceCollection AddCertificateService(this IServiceCollection services, IConfiguration configuration)
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// A configuration section targeting . NOTE: This configuration requires a nested
+ /// section within the previous section.
+ ///
+ ///
+ /// The value of .
+ ///
+ public static IServiceCollection AddCertificateService(this IServiceCollection services, IConfigurationSection configurationSection)
{
- services.AddOptions()
- .Bind(configuration.GetRequiredSection(Sections.CERTIFICATE))
- .ValidateDataAnnotations()
- .ValidateOnStart();
-
- services.AddCertificateStore(configuration);
+ services.TryAddOptions(configurationSection);
+ services.AddCertificateStore(configurationSection.GetRequiredSection(Sections.STORE));
services.AddCertificateService();
return services;
@@ -107,16 +167,41 @@ public static IServiceCollection AddCertificateService(this IServiceCollection s
#region RSACryptoService
- internal static IServiceCollection AddRSACryptoService(this IServiceCollection services)
+ private static IServiceCollection AddRSACryptoService(this IServiceCollection services)
{
throw new NotImplementedException();
}
+
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// An action used to configure .
+ ///
+ ///
+ /// The value of .
+ ///
public static IServiceCollection AddRSACryptoService(this IServiceCollection services, Action setupAction)
{
throw new NotImplementedException();
}
- public static IServiceCollection AddRSACryptoService(this IServiceCollection services, IConfiguration configuration)
+ ///
+ /// Adds the default implementation of to .
+ ///
+ ///
+ /// The service collection containing the service.
+ ///
+ ///
+ /// A configuration section targeting .
+ ///
+ ///
+ /// The value of .
+ ///
+ public static IServiceCollection AddRSACryptoService(this IServiceCollection services, IConfigurationSection configurationSection)
{
throw new NotImplementedException();
}
diff --git a/AdvancedSystems.Security/Extensions/CertificateExtensions.cs b/AdvancedSystems.Security/Extensions/CertificateExtensions.cs
new file mode 100644
index 0000000..27086b2
--- /dev/null
+++ b/AdvancedSystems.Security/Extensions/CertificateExtensions.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+
+using AdvancedSystems.Security.Abstractions;
+using AdvancedSystems.Security.Abstractions.Exceptions;
+using AdvancedSystems.Security.Cryptography;
+
+using static System.Net.WebRequestMethods;
+
+namespace AdvancedSystems.Security.Extensions;
+
+///
+/// Defines functions for interacting with X.509 certificates.
+///
+///
+public static partial class CertificateExtensions
+{
+ ///
+ /// Retrieves an X.509 certificate from the specified store using the provided thumbprint.
+ ///
+ ///
+ /// The type of the certificate store, which must implement the interface.
+ ///
+ ///
+ /// The certificate store from which to retrieve the certificate.
+ ///
+ ///
+ /// The thumbprint of the certificate to locate.
+ ///
+ ///
+ /// The object if the certificate is found.
+ ///
+ ///
+ /// Thrown when no certificate with the specified thumbprint is found in the store.
+ ///
+ public static X509Certificate2 GetCertificate(this T store, string thumbprint) where T : ICertificateStore
+ {
+ store.Open(OpenFlags.ReadOnly);
+
+ var certificate = store.Certificates
+ .Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false)
+ .OfType()
+ .FirstOrDefault();
+
+ return certificate
+ ?? throw new CertificateNotFoundException("No valid certificate matching the search criteria could be found in the store.");
+ }
+
+ ///
+ /// Attempts to parse the specified distinguished name (DN) string into a object.
+ ///
+ ///
+ /// The DN string to parse, typically in the X.500 or LDAP DN format.
+ ///
+ ///
+ /// When this method returns, contains the parsed object if the parsing was successful;
+ /// otherwise, .
+ ///
+ ///
+ /// if the parsing was successful; otherwise, .
+ ///
+ ///
+ /// The X.500 Distinguished Name (DN) and the LDAP Distinguished Name (DN) differ in syntax and conventions.
+ ///
+ ///
+ ///
+ /// X.500 Format
+ ///
+ ///
+ /// Comes from the X.500 standard for directory services
+ ///
+ ///
+ /// -
+ ///
+ /// Separator
+ ///
+ ///
+ /// Components are separated by commas (,).
+ ///
+ ///
+ /// -
+ ///
+ /// Order
+ ///
+ ///
+ /// Attributes are typically listed in the most significant to least significant order
+ /// (root to leaf).
+ ///
+ ///
+ /// -
+ ///
+ /// Attributes
+ ///
+ ///
+ /// Attributes are case-insensitive but are conventionally written in uppercase. Note that
+ /// attribute names are more verbose in some X.500 implementations.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// LDAP
+ ///
+ ///
+ /// A streamlined version of the X.500 DN, tailored for LDAP (Lightweight Directory Access Protocol).
+ ///
+ ///
+ /// -
+ ///
+ /// Separator
+ ///
+ ///
+ /// Components are separated by commas (,), like X.500, but semicolons (;) are also sometimes
+ /// allowed (albeit less common).
+ ///
+ ///
+ /// -
+ ///
+ /// Order
+ ///
+ ///
+ /// Attributes are typically listed in the least significant to most significant order (leaf to root),
+ /// though many implementations allow flexibility.
+ ///
+ ///
+ /// -
+ ///
+ /// Attributes
+ ///
+ ///
+ /// LDAP uses abbreviated attribute names (e.g., CN for common name, O for organization, OU for organizational unit).
+ /// It is also more permissive with regards to case sensitivity and attribute formatting.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool TryParseDistinguishedName(string distinguishedName, out DistinguishedName? result)
+ {
+ var rdns = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var dn = new X500DistinguishedName(distinguishedName);
+
+ foreach (var rdn in dn.EnumerateRelativeDistinguishedNames())
+ {
+ string? attribute = rdn.GetSingleElementType().FriendlyName;
+ string value = rdn.GetSingleElementValue() ?? string.Empty;
+
+ if (string.IsNullOrEmpty(attribute)) continue;
+
+ rdns.Add(attribute, value);
+ }
+
+ bool hasCountry = rdns.TryGetValue(RDN.C, out string? country);
+ bool hasCommonName = rdns.TryGetValue(RDN.CN, out string? commonName);
+ bool hasLocality = rdns.TryGetValue(RDN.L, out string? locality);
+ bool hasOrganization = rdns.TryGetValue(RDN.O, out string? organization);
+ bool hasOrganizationalUnit = rdns.TryGetValue(RDN.OU, out string? organizationalUnit);
+ bool hasState = rdns.TryGetValue(RDN.S, out string? state);
+
+ bool hasRelativeDistinguishedNames = hasCountry
+ || hasCommonName
+ || hasLocality
+ || hasOrganization
+ || hasOrganizationalUnit
+ || hasState;
+
+ result = hasRelativeDistinguishedNames ? new DistinguishedName
+ {
+ CommonName = commonName,
+ OrganizationalUnit = organizationalUnit,
+ Organization = organization,
+ Locality = locality,
+ State = state,
+ Country = country,
+ } : null;
+
+ return hasRelativeDistinguishedNames;
+ }
+}
\ No newline at end of file
diff --git a/AdvancedSystems.Security/Extensions/CertificateStore.cs b/AdvancedSystems.Security/Extensions/CertificateStore.cs
deleted file mode 100644
index 2e4afd4..0000000
--- a/AdvancedSystems.Security/Extensions/CertificateStore.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System.Linq;
-using System.Security.Cryptography.X509Certificates;
-
-using AdvancedSystems.Security.Abstractions;
-using AdvancedSystems.Security.Abstractions.Exceptions;
-
-namespace AdvancedSystems.Security.Extensions;
-
-///
-/// Defines functions for interacting with X.509 certificates.
-///
-///
-public static class CertificateStore
-{
- ///
- /// Retrieves an X.509 certificate from the specified store using the provided thumbprint.
- ///
- ///
- /// The type of the certificate store, which must implement the interface.
- ///
- ///
- /// The certificate store from which to retrieve the certificate.
- ///
- ///
- /// The thumbprint of the certificate to locate.
- ///
- ///
- /// The object if the certificate is found.
- ///
- ///
- /// Thrown when no certificate with the specified thumbprint is found in the store.
- ///
- public static X509Certificate2 GetCertificate(this T store, string thumbprint) where T : ICertificateStore
- {
- store.Open(OpenFlags.ReadOnly);
-
- var certificate = store.Certificates
- .Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false)
- .OfType()
- .FirstOrDefault();
-
- return certificate
- ?? throw new CertificateNotFoundException("No valid certificate matching the search criteria could be found in the store.");
- }
-}
\ No newline at end of file
diff --git a/AdvancedSystems.Security/Options/CertificateOptions.cs b/AdvancedSystems.Security/Options/CertificateOptions.cs
index 5296e4e..505c02b 100644
--- a/AdvancedSystems.Security/Options/CertificateOptions.cs
+++ b/AdvancedSystems.Security/Options/CertificateOptions.cs
@@ -2,7 +2,7 @@
namespace AdvancedSystems.Security.Options;
-public sealed class CertificateOptions
+public sealed record CertificateOptions
{
[Key]
[Required(AllowEmptyStrings = false)]
diff --git a/AdvancedSystems.Security/Options/CertificateStoreOptions.cs b/AdvancedSystems.Security/Options/CertificateStoreOptions.cs
index 8e163ca..dde2e45 100644
--- a/AdvancedSystems.Security/Options/CertificateStoreOptions.cs
+++ b/AdvancedSystems.Security/Options/CertificateStoreOptions.cs
@@ -3,7 +3,7 @@
namespace AdvancedSystems.Security.Options;
-public sealed class CertificateStoreOptions
+public sealed record CertificateStoreOptions
{
[Required]
[EnumDataType(typeof(StoreName))]
diff --git a/AdvancedSystems.Security/Options/RSACryptoOptions.cs b/AdvancedSystems.Security/Options/RSACryptoOptions.cs
index 308c0c7..3e24f72 100644
--- a/AdvancedSystems.Security/Options/RSACryptoOptions.cs
+++ b/AdvancedSystems.Security/Options/RSACryptoOptions.cs
@@ -3,7 +3,7 @@
namespace AdvancedSystems.Security.Options;
-public sealed class RSACryptoOptions
+public sealed record RSACryptoOptions
{
public required HashAlgorithmName HashAlgorithmName { get; set; }
diff --git a/AdvancedSystems.Security/Options/Sections.cs b/AdvancedSystems.Security/Options/Sections.cs
deleted file mode 100644
index 940d22a..0000000
--- a/AdvancedSystems.Security/Options/Sections.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace AdvancedSystems.Security.Options;
-
-public readonly record struct Sections
-{
- public const string CERTIFICATE_STORE = "Certificate:Store";
-
- public const string CERTIFICATE = "Certificate";
-
- public const string RSA = "RSA";
-}
\ No newline at end of file