From 319b72535d3793b777d0b367f8a2753c20cc0fa7 Mon Sep 17 00:00:00 2001 From: Maytham Fahmi <9260645+maythamfahmi@users.noreply.github.com> Date: Sun, 3 Nov 2024 09:42:07 +0100 Subject: [PATCH] #104 coverage test for X509Certificate2 --- CryptoNet.Models/CryptoNetInfo.cs | 48 +++++++++++++++++------- CryptoNet.UnitTests/CryptoNetRsaTests.cs | 48 +++++++++++++++++++----- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/CryptoNet.Models/CryptoNetInfo.cs b/CryptoNet.Models/CryptoNetInfo.cs index c6848ae..de18e5a 100644 --- a/CryptoNet.Models/CryptoNetInfo.cs +++ b/CryptoNet.Models/CryptoNetInfo.cs @@ -8,6 +8,7 @@ using System; using System.ComponentModel; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; namespace CryptoNet.Models; @@ -19,16 +20,35 @@ public class CryptoNetInfo public AesDetail? AesDetail { get; set; } } -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public class RsaDetail { - public RSA Rsa { get; set; } - + public RSA? Rsa { get; set; } public byte[] PublicKey { get; set; } public byte[] PrivateKey { get; set; } -} -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + public RsaDetail(RSA rsa) + { + Rsa = rsa ?? throw new ArgumentNullException(nameof(rsa)); + PublicKey = Array.Empty(); + PrivateKey = Array.Empty(); + } + + public RsaDetail(byte[] publicKey, byte[] privateKey) + { + if (publicKey == null || publicKey.Length <= 0) + { + throw new ArgumentNullException(nameof(publicKey)); + } + + if (privateKey == null || privateKey.Length <= 0) + { + throw new ArgumentNullException(nameof(privateKey)); + } + + PublicKey = publicKey; + PrivateKey = privateKey; + } +} public class AesDetail { @@ -44,24 +64,24 @@ public AesDetail(byte[] key, byte[] iv) throw new ArgumentNullException(nameof(iv)); } - AesKeyValue = new AesKeyValue() - { - Key = key, - Iv = iv - }; + AesKeyValue = new AesKeyValue(key, iv); } public Aes? Aes { get; set; } public AesKeyValue AesKeyValue { get; set; } } -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public class AesKeyValue { - public byte[] Key { get; set; } - public byte[] Iv { get; set; } + public byte[] Key { get; } + public byte[] Iv { get; } + + public AesKeyValue(byte[] key, byte[] iv) + { + Key = key ?? throw new ArgumentNullException(nameof(key)); + Iv = iv ?? throw new ArgumentNullException(nameof(iv)); + } } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public enum KeyType { diff --git a/CryptoNet.UnitTests/CryptoNetRsaTests.cs b/CryptoNet.UnitTests/CryptoNetRsaTests.cs index bf046c7..c24caaa 100644 --- a/CryptoNet.UnitTests/CryptoNetRsaTests.cs +++ b/CryptoNet.UnitTests/CryptoNetRsaTests.cs @@ -201,11 +201,12 @@ public void Encrypt_With_PublicKey_Decrypt_With_PrivateKey_Of_Content_Test() Common.ConfidentialDummyData.ShouldBe(decryptedData); } - [Ignore("Private: This test works only on local Windows enviroment for debugging and testing. You can also put your own certifciate when debugging and testing")] + [Test] public void Encrypt_Decrypt_Using_X509_Certificate_Test() { // Arrange - X509Certificate2? certificate = CryptoNetExtensions.GetCertificateFromStore("CN=Maytham"); + // You can change to test real system certificate by using CryptoNetExtensions.GetCertificateFromStore("CN=MaythamCertificateName") + X509Certificate2 ? certificate = CreateSelfSignedCertificate(); var rsaPublicKey = new CryptoNetRsa(certificate, KeyType.PublicKey); var rsaPrivateKey = new CryptoNetRsa(certificate, KeyType.PrivateKey); @@ -218,11 +219,12 @@ public void Encrypt_Decrypt_Using_X509_Certificate_Test() } - [Ignore("Private: This test works only on local Windows enviroment for debugging and testing. You can also put your own certifciate when debugging and testing")] + [Test] public void Export_Public_Key_For_X509_Certificate_Test() { // Arrange - X509Certificate2? certificate = CryptoNetExtensions.GetCertificateFromStore("CN=Maytham"); + // You can change to test real system certificate by using CryptoNetExtensions.GetCertificateFromStore("CN=MaythamCertificateName") + X509Certificate2? certificate = CreateSelfSignedCertificate(); var rsa = new CryptoNetRsa(certificate, KeyType.PublicKey); // Act @@ -233,16 +235,17 @@ public void Export_Public_Key_For_X509_Certificate_Test() publicKey.ShouldNotBeEmpty(); } - [Ignore("Private: This test works only on local Windows enviroment for debugging and testing. You can also put your own certifciate when debugging and testing")] + [Test] public void Customize_PEM_Key_Encryption_Decryption_Test() { // Arrange - X509Certificate2? cert = CryptoNetExtensions.GetCertificateFromStore("CN=Maytham"); + // You can change to test real system certificate by using CryptoNetExtensions.GetCertificateFromStore("CN=MaythamCertificateName") + X509Certificate2? certificate = CreateSelfSignedCertificate(); - var pubKeyPem = Common.ExportPemKey(cert!, false); - var priKeyPem = Common.ExportPemKey(cert!); + var pubKeyPem = Common.ExportPemKey(certificate!, false); + var priKeyPem = Common.ExportPemKey(certificate!); var password = "password"; - var encryptedPriKeyBytes = Common.ExportPemKeyWithPassword(cert!, password); + var encryptedPriKeyBytes = Common.ExportPemKeyWithPassword(certificate!, password); // Act ICryptoNetRsa cryptoNet1 = ImportPemKeyWithPassword(encryptedPriKeyBytes, password); @@ -273,4 +276,31 @@ public static ICryptoNetRsa ImportPemKeyWithPassword(byte[] encryptedPrivateKey, cryptoNet.Info.RsaDetail?.Rsa?.ImportEncryptedPkcs8PrivateKey(password, encryptedPrivateKey, out _); return cryptoNet; } + + public static X509Certificate2 CreateSelfSignedCertificate() + { + using var rsa = RSA.Create(2048); // Generate a new RSA key pair for the certificate + var request = new CertificateRequest( + "CN=TestCertificate", + rsa, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1 + ); + + // Add extensions (e.g., for key usage, if needed) + request.CertificateExtensions.Add( + new X509KeyUsageExtension( + X509KeyUsageFlags.DigitalSignature, + critical: true + ) + ); + + // Create a self-signed certificate that is valid for one year + var certificate = request.CreateSelfSigned( + DateTimeOffset.Now.AddDays(-1), + DateTimeOffset.Now.AddYears(1) + ); + + return certificate; + } }