Skip to content

Commit

Permalink
Tests | Remove hardcoded certificates from functional tests (#3034)
Browse files Browse the repository at this point in the history
* Remove default .pfx file from TDS.Servers

Clients requiring a server certificate will need to generate their own.

* Infrastructure work - base class, project targets

* TestUtilities no longer targets netstandard2.0, so is now able to use CertificateRequest.
* Added a reference from Tests to TestUtilities.
* Added a base CertificateFixtureBase class. This provides basic infrastructure to generate a certificate and add it to a store (with cleanup on disposal.)

* Reworked SqlColumnEncryptionCertificateStoreProviderShould

Removed multiple hardcoded references to three certificates.
Also removed references to TestCertificate12.

* Cleanup: ExceptionsCertStore

This test class covered similar ground to the existing SqlColumnEncryptionCertificateStoreProviderShould test class.

* Rework ExceptionsAlgorithmErrors

Removes the last reference to Utility.CreateCertificate, replacing it with one which is generated dynamically.
Accordingly, removes Utility.CreateCertificate.

* Collection fixtures are unnecessary

* Update build.proj

* Running PowerShell with "runas" verb

* Switched to new .NET 9.0 APIs
  • Loading branch information
edwardneal authored Dec 2, 2024
1 parent 85044cd commit bb4c3b7
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 411 deletions.
1 change: 1 addition & 0 deletions build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<FunctionalTests Include="**/tools/TDS/TDS/TDS.csproj" />
<FunctionalTests Include="**/tools/TDS/TDS.EndPoint/TDS.EndPoint.csproj" />
<FunctionalTests Include="**/tools/TDS/TDS.Servers/TDS.Servers.csproj" />
<FunctionalTests Include="**/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj"/>
<FunctionalTests Include="**/tools/CoreFx.Private.TestUtilities/CoreFx.Private.TestUtilities.csproj" />
<FunctionalTests Include="**/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj" />
<FunctionalTests Include="**/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,38 @@
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.Data.SqlClient.TestUtilities.Fixtures;
using Xunit;
using static Microsoft.Data.SqlClient.Tests.AlwaysEncryptedTests.Utility;

namespace Microsoft.Data.SqlClient.Tests.AlwaysEncryptedTests
{
public class ExceptionsAlgorithmErrors : IClassFixture<CertFixture>
public class ExceptionsAlgorithmErrors : IClassFixture<ColumnEncryptionCertificateFixture>
{
// Reflection
public static Assembly systemData = Assembly.GetAssembly(typeof(SqlConnection));
public static Type sqlClientSymmetricKey = systemData.GetType("Microsoft.Data.SqlClient.SqlClientSymmetricKey");
public static ConstructorInfo sqlColumnEncryptionKeyConstructor = sqlClientSymmetricKey.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(byte[]) }, null);

private readonly ColumnEncryptionCertificateFixture _fixture;
private readonly byte[] _cek;
private readonly byte[] _encryptedCek;
private readonly string _certificatePath;

public ExceptionsAlgorithmErrors(ColumnEncryptionCertificateFixture fixture)
{
// Disable the cache to avoid false failures.
SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled = false;

SqlColumnEncryptionCertificateStoreProvider provider = new SqlColumnEncryptionCertificateStoreProvider();
X509Certificate2 currUserCertificate = fixture.GetCertificate(StoreLocation.CurrentUser);

_cek = GenerateRandomBytes(32);
_fixture = fixture;
_certificatePath = string.Format("CurrentUser/My/{0}", currUserCertificate.Thumbprint);
_encryptedCek = provider.EncryptColumnEncryptionKey(_certificatePath, "RSA_OAEP", _cek);
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public void TestNullCEK()
Expand Down Expand Up @@ -52,9 +72,9 @@ public void TestInvalidEncryptionType()
{
const byte invalidEncryptionType = 3;
Object cipherMD = GetSqlCipherMetadata(0, 2, null, invalidEncryptionType, 0x01);
AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
AddEncryptionKeyToCipherMD(cipherMD, _encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidEncryptionType,
"AEAD_AES_256_CBC_HMAC_SHA256", invalidEncryptionType, "'Deterministic', 'Randomized'");
Expand All @@ -74,7 +94,7 @@ public void TestInvalidCipherText()
string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidCipherTextSize,
invalidCiphertextLength, 65);
byte[] cipherText = GenerateRandomBytes(invalidCiphertextLength); // minimum length is 65
TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptDataUsingAED(cipherText, CertFixture.cek, CColumnEncryptionType.Deterministic));
TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptDataUsingAED(cipherText, _cek, CColumnEncryptionType.Deterministic));
Assert.Contains(expectedMessage, e.InnerException.Message);
}

Expand All @@ -85,10 +105,10 @@ public void TestInvalidAlgorithmVersion()
string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidAlgorithmVersion,
40, "01");
byte[] plainText = Encoding.Unicode.GetBytes("Hello World");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);
// Put a version number of 0x10
cipherText[0] = 0x40;
TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptDataUsingAED(cipherText, CertFixture.cek, CColumnEncryptionType.Deterministic));
TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptDataUsingAED(cipherText, _cek, CColumnEncryptionType.Deterministic));
Assert.Contains(expectedMessage, e.InnerException.Message);
}

Expand All @@ -98,13 +118,13 @@ public void TestInvalidAuthenticationTag()
{
string expectedMessage = SystemDataResourceManager.Instance.TCE_InvalidAuthenticationTag;
byte[] plainText = Encoding.Unicode.GetBytes("Hello World");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);
// Zero out 4 bytes of authentication tag
for (int i = 0; i < 4; i++)
{
cipherText[i + 1] = 0x00;
}
TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptDataUsingAED(cipherText, CertFixture.cek, CColumnEncryptionType.Deterministic));
TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptDataUsingAED(cipherText, _cek, CColumnEncryptionType.Deterministic));
Assert.Contains(expectedMessage, e.InnerException.Message);
}

Expand All @@ -115,9 +135,9 @@ public void TestNullColumnEncryptionAlgorithm()
string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_NullColumnEncryptionAlgorithm,
"'AEAD_AES_256_CBC_HMAC_SHA256'");
Object cipherMD = GetSqlCipherMetadata(0, 0, null, 1, 0x01);
AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
AddEncryptionKeyToCipherMD(cipherMD, _encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

TargetInvocationException e = Assert.Throws<TargetInvocationException>(() => DecryptWithKey(cipherText, cipherMD));
Assert.Contains(expectedMessage, e.InnerException.Message);
Expand All @@ -133,9 +153,9 @@ public void TestUnknownEncryptionAlgorithmId()
string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnknownColumnEncryptionAlgorithmId,
unknownEncryptionAlgoId, "'1', '2'");
Object cipherMD = GetSqlCipherMetadata(0, unknownEncryptionAlgoId, null, 1, 0x01);
AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
AddEncryptionKeyToCipherMD(cipherMD, _encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

Exception decryptEx = Assert.Throws<TargetInvocationException>(() => DecryptWithKey(plainText, cipherMD));
Assert.Matches(expectedMessage, decryptEx.InnerException.Message);
Expand All @@ -157,9 +177,9 @@ public void TestUnknownCustomKeyStoreProvider()
string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnrecognizedKeyStoreProviderName,
invalidProviderName, "'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'", "");
Object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x03);
AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, invalidProviderName, "RSA_OAEP");
AddEncryptionKeyToCipherMD(cipherMD, _encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, invalidProviderName, "RSA_OAEP");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

Exception decryptEx = Assert.Throws<TargetInvocationException>(() => DecryptWithKey(plainText, cipherMD));
Assert.Contains(expectedMessage, decryptEx.InnerException.Message);
Expand All @@ -179,9 +199,9 @@ public void TestTceUnknownEncryptionAlgorithm()
string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnknownColumnEncryptionAlgorithm,
unknownEncryptionAlgorithm, "'AEAD_AES_256_CBC_HMAC_SHA256'");
Object cipherMD = GetSqlCipherMetadata(0, 0, "Dummy", 1, 0x01);
AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
AddEncryptionKeyToCipherMD(cipherMD, _encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

Exception decryptEx = Assert.Throws<TargetInvocationException>(() => DecryptWithKey(cipherText, cipherMD));
Assert.Contains(expectedMessage, decryptEx.InnerException.Message);
Expand All @@ -194,15 +214,15 @@ public void TestTceUnknownEncryptionAlgorithm()
[PlatformSpecific(TestPlatforms.Windows)]
public void TestExceptionsFromCertStore()
{
byte[] corruptedCek = GenerateInvalidEncryptedCek(CertFixture.cek, ECEKCorruption.SIGNATURE);
byte[] corruptedCek = GenerateInvalidEncryptedCek(_cek, ECEKCorruption.SIGNATURE);

string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_KeyDecryptionFailedCertStore,
"MSSQL_CERTIFICATE_STORE", BitConverter.ToString(corruptedCek, corruptedCek.Length - 10, 10));

Object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x01);
AddEncryptionKeyToCipherMD(cipherMD, corruptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
AddEncryptionKeyToCipherMD(cipherMD, corruptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

Exception decryptEx = Assert.Throws<TargetInvocationException>(() => DecryptWithKey(cipherText, cipherMD));
Assert.Matches(expectedMessage, decryptEx.InnerException.Message);
Expand All @@ -224,9 +244,9 @@ public void TestExceptionsFromCustomKeyStore()
SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders);

object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x01);
AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "DummyProvider", "DummyAlgo");
AddEncryptionKeyToCipherMD(cipherMD, _encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, _certificatePath, "DummyProvider", "DummyAlgo");
byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld");
byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic);
byte[] cipherText = EncryptDataUsingAED(plainText, _cek, CColumnEncryptionType.Deterministic);

Exception decryptEx = Assert.Throws<TargetInvocationException>(() => DecryptWithKey(cipherText, cipherMD));
Assert.Contains(expectedMessage, decryptEx.InnerException.Message);
Expand All @@ -238,35 +258,4 @@ public void TestExceptionsFromCustomKeyStore()
}
}
}

public class CertFixture : IDisposable
{
private readonly SqlColumnEncryptionCertificateStoreProvider provider = new SqlColumnEncryptionCertificateStoreProvider();

public static X509Certificate2 certificate;
public static string thumbprint;
public static string certificatePath;
public static byte[] cek;
public static byte[] encryptedCek;

public CertFixture()
{
if (certificate == null)
{
certificate = Utility.CreateCertificate();
}
thumbprint = certificate.Thumbprint;
certificatePath = string.Format("CurrentUser/My/{0}", thumbprint);
cek = GenerateRandomBytes(32);
encryptedCek = provider.EncryptColumnEncryptionKey(certificatePath, "RSA_OAEP", cek);

// Disable the cache to avoid false failures.
SqlConnection.ColumnEncryptionQueryMetadataCacheEnabled = false;
}

public void Dispose()
{
// Do NOT remove certificate for concurrent consistency. Certificates are used for other test cases as well.
}
}
}
Loading

0 comments on commit bb4c3b7

Please sign in to comment.