From ce431e79fd9800940079dba9909df5d02bad2a38 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Wed, 20 Nov 2024 19:37:07 +0000
Subject: [PATCH 1/9] Remove default .pfx file from TDS.Servers
Clients requiring a server certificate will need to generate their own.
---
.../ManualTests/TracingTests/TestTdsServer.cs | 6 +-----
.../tests/tools/TDS/TDS.EndPoint/TDSParser.cs | 6 ++++++
.../tools/TDS/TDS.Servers/TDS.Servers.csproj | 3 ---
.../tools/TDS/TDS.Servers/TDSServerArguments.cs | 2 +-
.../TDS/TDS.Servers/TdsServerCertificate.pfx | Bin 1854 -> 0 bytes
5 files changed, 8 insertions(+), 9 deletions(-)
delete mode 100644 src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TdsServerCertificate.pfx
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs
index a4557d72b6..2e6e3d0dc6 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs
@@ -43,11 +43,7 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool
args.FedAuthRequiredPreLoginOption = SqlServer.TDS.PreLogin.TdsPreLoginFedAuthRequiredOption.FedAuthRequired;
}
- if (encryptionCertificate != null)
- {
- args.EncryptionCertificate = encryptionCertificate;
- }
-
+ args.EncryptionCertificate = encryptionCertificate;
args.Encryption = encryptionType;
TestTdsServer server = engine == null ? new TestTdsServer(args) : new TestTdsServer(engine, args);
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs
index 2027c7bd7f..eceed95d60 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs
@@ -113,6 +113,12 @@ protected void EnableServerTransportEncryption(X509Certificate certificate)
return;
}
+ // The SSL certificate is required for the server to handle transport encryption
+ if (certificate == null)
+ {
+ throw new AuthenticationException("Server is unable to authenticate transport encryption without a valid SSL certificate.");
+ }
+
Log("Enabling server transport encryption...");
// Wrap TDS stream with auto TDS stream
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj
index cb89f362ea..b7757b257b 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDS.Servers.csproj
@@ -26,9 +26,6 @@
-
- Always
-
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs
index 1543ebde63..1ff553bed0 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs
@@ -87,7 +87,7 @@ public TDSServerArguments()
// By Default SQL authentication will be used.
FedAuthRequiredPreLoginOption = TdsPreLoginFedAuthRequiredOption.FedAuthNotRequired;
- EncryptionCertificate = new X509Certificate2("TdsServerCertificate.pfx", "SecretPassword123456");
+ EncryptionCertificate = null;
ServerPrincipalName = AzureADServicePrincipalName;
StsUrl = AzureADProductionTokenEndpoint;
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TdsServerCertificate.pfx b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TdsServerCertificate.pfx
deleted file mode 100644
index a5c48e2aee1f100c63aa698345667d6a3672e97f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1854
zcmY*YcUY767XC8)!iLBQBTBHhV6@_w7-qGg0-`cyU&=*B2xt&w6_gpm62!>xf-(z;
zY*PiK%8-N&Wr=_wOGrRKj37~p(9b^iwojkmbIx<#_q^wve||5-!000pD2M@Wf})4w
zP2+`<2n?ct0rJrd@C{5)Lkv{QKUyd@nt`f<=`uJpt)>z*
zMaig81n7Tf_bum!PEhvL6}8~D&(~w}ek>eF4{RQcM=-5N=SnbjlLrvV1xeMj4Z&{dUf9PRL&
zQ#oZ*^h0s7cvoG^8rQ)O{BA9aIv2LTwyAK7PRH|=+I$a03kfG^_6}k)quaI~qgBl<
zs}hWYWCv2!y|Q7SV&ukxEl2WD>yQ#wq@a~pTY%LbJ$PFz`IUO7YWDF=PbEfgr0>w3
z3K}c!Ak)K1t=Sb0rjYjD5Ush3|^sst-Q)jQCJo`3Adk)i@yB&scBfO~CIOF6BD3
zbC*}M!oEDLp%KNKlGYR__l2$c4oI^Py}!r-b}+)ZIA5i0iV4uMxJJWWt+Sdoi98Id
z`xFnMdARl!@!&guJ$dG?e2ZX_pFWnt+PjO%)*RHY#q=%p!x
z5#1WD*pkXyGoCI}d^)Sc@d4Sqop`gQ?yiqiV}8JIs+LaGin0!hQsMN^R#=unx7u#?
zcuj@0%G+3Hd9LpUFKn~1IlX{gW*P0Ke$Da2!8wH`U3U*^P+w7^h;?RB^O|JT>H|{D
z1SI>{Hy4#BfoL=W0077Z6g>supk7u4A=s`@Q^+%
z@c?uHJ-{6}22cPJa0JkTYZ4qsV5|+V_JAv0n4!Yx2mrx-2q43c3irscMhnhvuu*Y;
z9m0SG43r2=p^*pxcI%hFJBWW67yN2mHA2136IpnGkYfc27A?H!4Ry
zgE;js8Js=v(@g2P@`u|w>HS{yitda9%+-s*cQ=`D`hwZIW}y3=Yre>AAoEt1oD-xIFccSe|ow*y6wqEFUqna}#WG#T?|^ScVH
z0#tZ}lpTG;mwi_<2ghZXf^|oA#qCY;q#nolqSv^zG|tu9PLy%(OwB-oxvja|qidy4
z<0NP&z3>w^9He}#Fo((kcwSNZl^XgQ@jA|?QLml(M-B-
zgJxd;r`Z;-`(bV_!Dsl4a;5?`D@D!sSct+<>MUQqA;o9mT+#S-cU|Ru_Rfy@#D$mx
zK1-8Nu^ls>mWrtd@p5T*x)R?;-gJ!Xd)d#`P_q%TZ}<3tO*OPjijp?7oCb*)3;h?e
zd3z`Prbi?EotNxMC&lc3FqfXu*zCNtY~X!-x9jyYg_vPSg!5casVTAb6jHvzF=jhI
zFi@0eUB2nAsQRf88rZY{mFgH<
zS4t;hEcjMg?ZkbO==2K8F%wQ`IbfbUbK#<2HAU9wX~lmuG43P#$|<2t3)Gaph3EE<$>?RP_hgkFi|I#?*6QO?0=?zXh|3?%c=Y12H9OkbFI9%p~;IC&8TakZJmP6$ttsQiU+!p2Jcoq%vC0KPfX@FkZ>t
o!*E%YHj&r!WPX!~mNWW@t668IJ&=o1TUT#dU>6Bv^S&D54}-)jG5`Po
From b130460e254f1cb968ef3797882a6819c85d438e Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Sun, 24 Nov 2024 15:18:25 +0000
Subject: [PATCH 2/9] 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.)
---
.../Microsoft.Data.SqlClient.Tests.csproj | 1 +
.../Fixtures/CertificateFixtureBase.cs | 229 ++++++++++++++++++
...rosoft.Data.SqlClient.TestUtilities.csproj | 8 +-
3 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
index 1186c3ebdd..38f719b1a5 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
@@ -116,6 +116,7 @@
TDS
+
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs
new file mode 100644
index 0000000000..b05ea370e3
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/CertificateFixtureBase.cs
@@ -0,0 +1,229 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace Microsoft.Data.SqlClient.TestUtilities.Fixtures
+{
+ public abstract class CertificateFixtureBase : IDisposable
+ {
+ private sealed class CertificateStoreContext
+ {
+ public List Certificates { get; }
+
+ public StoreLocation Location { get; }
+
+ public StoreName Name { get; }
+
+ public CertificateStoreContext(StoreLocation location, StoreName name)
+ {
+ Certificates = new List();
+ Location = location;
+ Name = name;
+ }
+ }
+
+ private readonly List _certificateStoreModifications = new List();
+
+ protected static X509Certificate CreateCertificate(string subjectName, IEnumerable dnsNames, IEnumerable ipAddresses)
+ {
+ // This will always generate a certificate with:
+ // * Start date: 24hrs ago
+ // * End date: 24hrs in the future
+ // * Subject: {subjectName}
+ // * Subject alternative names: {dnsNames}, {ipAddresses}
+ // * Public key: 4096-bit RSA
+ // * Hash algorithm: SHA256
+ // * Key usage: digital signature, key encipherment
+ // * Enhanced key usage: server authentication, client authentication
+ DateTimeOffset notBefore = DateTimeOffset.UtcNow.AddDays(-1);
+ DateTimeOffset notAfter = DateTimeOffset.UtcNow.AddDays(1);
+#if NET
+ X500DistinguishedNameBuilder subjectBuilder = new X500DistinguishedNameBuilder();
+ SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
+ RSA rsaKey = RSA.Create(4096);
+ bool hasSans = false;
+
+ subjectBuilder.AddCommonName(subjectName);
+ foreach (string dnsName in dnsNames)
+ {
+ sanBuilder.AddDnsName(dnsName);
+ hasSans = true;
+ }
+ foreach (string ipAddress in ipAddresses)
+ {
+ sanBuilder.AddIpAddress(System.Net.IPAddress.Parse(ipAddress));
+ hasSans = true;
+ }
+
+ CertificateRequest request = new CertificateRequest(subjectBuilder.Build(), rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+
+ request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
+ request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, false));
+ request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection() { new Oid("1.3.6.1.5.5.7.3.1"), new Oid("1.3.6.1.5.5.7.3.2") }, true));
+
+ if (hasSans)
+ {
+ request.CertificateExtensions.Add(sanBuilder.Build());
+ }
+
+ return request.CreateSelfSigned(notBefore, notAfter);
+#else
+ // The CertificateRequest API is available in .NET Core, but was only added to .NET Framework 4.7.2; it thus can't be used in the test projects.
+ // Instead, fall back to running a PowerShell script which calls New-SelfSignedCertificate. This cmdlet also adds the certificate to a specific,
+ // certificate store, so remove it from there.
+ // Normally, the PowerShell script will return zero and print the base64-encoded certificate to stdout. If there's an exception, it'll return 1 and
+ // print the message instead.
+ const string PowerShellCommandTemplate = @"$notBefore = [DateTime]::ParseExact(""{0}"", ""O"", $null)
+$notAfter = [DateTime]::ParseExact(""{1}"", ""O"", $null)
+$subject = ""CN={2}""
+$sAN = @({3})
+
+try
+{{
+ $x509 = New-SelfSignedCertificate -Subject $subject -TextExtension $sAN -KeyLength 4096 -KeyAlgorithm RSA `
+ -CertStoreLocation ""Cert:\CurrentUser\My"" -NotBefore $notBefore -NotAfter $notAfter `
+ -KeyExportPolicy Exportable -HashAlgorithm SHA256
+
+ if ($x509 -eq $null)
+ {{ throw ""Certificate was null!"" }}
+
+ $exportedArray = $x509.Export(""Pkcs12"", ""{4}"")
+ Write-Output $([Convert]::ToBase64String($exportedArray))
+
+ Remove-Item ""Cert:\CurrentUser\My\$($x509.Thumbprint)""
+
+ exit 0
+}}
+catch [Exception]
+{{
+ Write-Output $_.Exception.Message
+ exit 1
+}}";
+ const int PowerShellCommandTimeout = 15_000;
+
+ string sanString = string.Empty;
+ bool hasSans = false;
+ string formattedCommand = null;
+ byte[] passwordBytes = new byte[32];
+ string password = null;
+ Random rnd = new Random();
+ string commandOutput = null;
+
+ foreach (string dnsName in dnsNames)
+ {
+ sanString += string.Format("DNS={0}&", dnsName);
+ hasSans = true;
+ }
+ foreach (string ipAddress in ipAddresses)
+ {
+ sanString += string.Format("IPAddress={0}&", ipAddress);
+ hasSans = true;
+ }
+
+ sanString = hasSans ? "\"2.5.29.17={text}" + sanString.Substring(0, sanString.Length - 1) + "\"" : string.Empty;
+
+ rnd.NextBytes(passwordBytes);
+ password = Convert.ToBase64String(passwordBytes);
+ formattedCommand = string.Format(PowerShellCommandTemplate, notBefore.ToString("O"), notAfter.ToString("O"), subjectName, sanString, password);
+
+ using (Process psProcess = new Process()
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = "powershell.exe",
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ // Pass the Base64-encoded command to remove the need to escape quote marks
+ Arguments = "-EncodedCommand " + Convert.ToBase64String(Encoding.Unicode.GetBytes(formattedCommand))
+ }
+ })
+ {
+ psProcess.Start();
+ commandOutput = psProcess.StandardOutput.ReadToEnd();
+
+ if (!psProcess.WaitForExit(PowerShellCommandTimeout))
+ {
+ psProcess.Kill();
+ throw new Exception("Process did not complete in time, exiting.");
+ }
+
+ // Process completed successfully if it had an exit code of zero, the command output will be the base64-encoded certificate
+ if (psProcess.ExitCode == 0)
+ {
+ return new X509Certificate2(Convert.FromBase64String(commandOutput), password);
+ }
+ else
+ {
+ throw new Exception($"PowerShell command raised exception: {commandOutput}");
+ }
+ }
+#endif
+ }
+
+ protected void AddToStore(X509Certificate2 cert, StoreLocation storeLocation, StoreName storeName)
+ {
+ CertificateStoreContext storeContext = _certificateStoreModifications.Find(csc => csc.Location == storeLocation && csc.Name == storeName);
+
+ if (storeContext == null)
+ {
+ storeContext = new(storeLocation, storeName);
+ _certificateStoreModifications.Add(storeContext);
+ }
+
+ using X509Store store = new X509Store(storeContext.Name, storeContext.Location);
+
+ store.Open(OpenFlags.ReadWrite);
+ if (store.Certificates.Contains(cert))
+ {
+ store.Remove(cert);
+ }
+ store.Add(cert);
+
+ storeContext.Certificates.Add(cert);
+ }
+
+ public virtual void Dispose()
+ {
+ foreach (CertificateStoreContext storeContext in _certificateStoreModifications)
+ {
+ using X509Store store = new X509Store(storeContext.Name, storeContext.Location);
+
+ try
+ {
+ store.Open(OpenFlags.ReadWrite);
+ }
+ catch(Exception)
+ {
+ continue;
+ }
+
+ foreach (X509Certificate2 cert in storeContext.Certificates)
+ {
+ try
+ {
+ if (store.Certificates.Contains(cert))
+ {
+ store.Remove(cert);
+ }
+ }
+ catch (Exception)
+ {
+ continue;
+ }
+
+ cert.Dispose();
+ }
+
+ storeContext.Certificates.Clear();
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj
index 3bd48830cc..2895ef3a6a 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj
@@ -1,6 +1,10 @@
-
+
- netstandard2.0
+ netfx
+ netcoreapp
+ win
+ win-$(Platform)
+
$(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)
$(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)
From 455903142c7d4ea3c2ecc63da75db88aa13e93b0 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Sun, 24 Nov 2024 17:19:06 +0000
Subject: [PATCH 3/9] Reworked
SqlColumnEncryptionCertificateStoreProviderShould
Removed multiple hardcoded references to three certificates.
Also removed references to TestCertificate12.
---
...ncryptionCertificateStoreProviderShould.cs | 195 ++++++------------
.../AlwaysEncryptedTests/Utility.cs | 49 -----
.../Fixtures/CertificateFixtureBase.cs | 27 ++-
.../ColumnEncryptionCertificateFixture.cs | 51 +++++
4 files changed, 135 insertions(+), 187 deletions(-)
create mode 100644 src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Fixtures/ColumnEncryptionCertificateFixture.cs
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs
index ecbd4c5da9..b7ff289a19 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using Microsoft.Data.SqlClient.TestUtilities.Fixtures;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -16,9 +17,11 @@
namespace Microsoft.Data.SqlClient.Tests.AlwaysEncryptedTests
{
- public class SqlColumnEncryptionCertificateStoreProviderWindowsShould : IClassFixture
+ [Collection(nameof(SqlColumnEncryptionCertificateStoreProviderCollection))]
+ public class SqlColumnEncryptionCertificateStoreProviderWindowsShould
{
- private const string MASTER_KEY_PATH = "CurrentUser/My/C74D53B816A971E3FF9714FE1DD2E57E1710D946";
+ private const string PRIMARY_CERTIFICATE_PATH = "CurrentUser/My/{primary_thumbprint}";
+ private const string SECONDARY_CERTIFICATE_PATH = "CurrentUser/My/{secondary_thumbprint}";
private const string ENCRYPTION_ALGORITHM = "RSA_OAEP";
///
@@ -93,14 +96,22 @@ public class SqlColumnEncryptionCertificateStoreProviderWindowsShould : IClassFi
///
private const int CipherTextStartIndex = IVStartIndex + IVLengthInBytes;
+ private readonly ColumnEncryptionCertificateFixture _certFixture;
+
+ public SqlColumnEncryptionCertificateStoreProviderWindowsShould(ColumnEncryptionCertificateFixture certFixture)
+ {
+ _certFixture = certFixture;
+ }
+
[Theory]
[InvalidDecryptionParameters]
[PlatformSpecific(TestPlatforms.Windows)]
public void ThrowExceptionWithInvalidParameterWhileDecryptingColumnEncryptionKey(string errorMsg, Type exceptionType, string masterKeyPath, string encryptionAlgorithm, byte[] bytes)
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- Exception ex = Assert.Throws(exceptionType, () => provider.DecryptColumnEncryptionKey(masterKeyPath, encryptionAlgorithm, bytes));
- Assert.Matches(errorMsg, ex.Message);
+ Exception ex = Assert.Throws(exceptionType,
+ () => provider.DecryptColumnEncryptionKey(ReplaceKeyTokens(masterKeyPath), encryptionAlgorithm, bytes));
+ Assert.Matches(ReplaceKeyTokens(errorMsg), ex.Message);
}
[Theory]
@@ -109,7 +120,7 @@ public void ThrowExceptionWithInvalidParameterWhileDecryptingColumnEncryptionKey
public void ThrowExceptionWithInvalidParameterWhileEncryptingColumnEncryptionKey(string errorMsg, Type exceptionType, string masterKeyPath, string encryptionAlgorithm, byte[] bytes)
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- Exception ex = Assert.Throws(exceptionType, () => provider.EncryptColumnEncryptionKey(masterKeyPath, encryptionAlgorithm, bytes));
+ Exception ex = Assert.Throws(exceptionType, () => provider.EncryptColumnEncryptionKey(ReplaceKeyTokens(masterKeyPath), encryptionAlgorithm, bytes));
Assert.Matches(errorMsg, ex.Message);
}
@@ -124,26 +135,26 @@ public void ThrowExceptionWithInvalidParameterWhileSigningColumnMasterKeyMetadat
}
[Theory]
- [InlineData("CurrentUser/My/C74D53B816A971E3FF9714FE1DD2E57E1710D946")]
- [InlineData("CURRENTUSER/My/C74D53B816A971E3FF9714FE1DD2E57E1710D946")]
- [InlineData("currentuser/My/C74D53B816A971E3FF9714FE1DD2E57E1710D946")]
+ [InlineData("CurrentUser/My/{primary_thumbprint}")]
+ [InlineData("CURRENTUSER/My/{primary_thumbprint}")]
+ [InlineData("currentuser/My/{primary_thumbprint}")]
[PlatformSpecific(TestPlatforms.Windows)]
- public void SetStoreLocationApproperiatelyFromMasterKeyPathRegardlessOfCase(string masterKeyPath)
+ public void SetStoreLocationAppropriatelyFromMasterKeyPathRegardlessOfCase(string masterKeyPath)
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- byte[] ciphertext = provider.EncryptColumnEncryptionKey(masterKeyPath, ENCRYPTION_ALGORITHM, new byte[] { 1, 2, 3, 4, 5 });
+ byte[] ciphertext = provider.EncryptColumnEncryptionKey(ReplaceKeyTokens(masterKeyPath), ENCRYPTION_ALGORITHM, new byte[] { 1, 2, 3, 4, 5 });
Assert.NotNull(ciphertext);
}
[Theory]
- [InlineData("CurrentUser/my/C74D53B816A971E3FF9714FE1DD2E57E1710D946")]
- [InlineData("CurrentUser/MY/C74D53B816A971E3FF9714FE1DD2E57E1710D946")]
- [InlineData("CurrentUser/My/C74D53B816A971E3FF9714FE1DD2E57E1710D946")]
+ [InlineData("CurrentUser/my/{primary_thumbprint}")]
+ [InlineData("CurrentUser/MY/{primary_thumbprint}")]
+ [InlineData("CurrentUser/My/{primary_thumbprint}")]
[PlatformSpecific(TestPlatforms.Windows)]
- public void SetStoreNameApproperiatelyFromMasterKeyPathRegardlessOfCase(string masterKeyPath)
+ public void SetStoreNameAppropriatelyFromMasterKeyPathRegardlessOfCase(string masterKeyPath)
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- byte[] ciphertext = provider.EncryptColumnEncryptionKey(masterKeyPath, ENCRYPTION_ALGORITHM, new byte[] { 1, 2, 3, 4, 5 });
+ byte[] ciphertext = provider.EncryptColumnEncryptionKey(ReplaceKeyTokens(masterKeyPath), ENCRYPTION_ALGORITHM, new byte[] { 1, 2, 3, 4, 5 });
Assert.NotNull(ciphertext);
}
@@ -155,7 +166,7 @@ public void SetStoreNameApproperiatelyFromMasterKeyPathRegardlessOfCase(string m
public void AcceptEncryptionAlgorithmRegardlessOfCase(string algorithm)
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- byte[] ciphertext = provider.EncryptColumnEncryptionKey(MASTER_KEY_PATH, algorithm, new byte[] { 1, 2, 3, 4, 5 });
+ byte[] ciphertext = provider.EncryptColumnEncryptionKey(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), algorithm, new byte[] { 1, 2, 3, 4, 5 });
Assert.NotNull(ciphertext);
}
@@ -171,8 +182,8 @@ public void EncryptKeyAndThenDecryptItSuccessfully(int dataSize)
var randomNumberGenerator = new Random();
randomNumberGenerator.NextBytes(columnEncryptionKey);
- byte[] encryptedData = provider.EncryptColumnEncryptionKey(MASTER_KEY_PATH, ENCRYPTION_ALGORITHM, columnEncryptionKey);
- byte[] decryptedData = provider.DecryptColumnEncryptionKey(MASTER_KEY_PATH, ENCRYPTION_ALGORITHM, encryptedData);
+ byte[] encryptedData = provider.EncryptColumnEncryptionKey(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), ENCRYPTION_ALGORITHM, columnEncryptionKey);
+ byte[] decryptedData = provider.DecryptColumnEncryptionKey(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), ENCRYPTION_ALGORITHM, encryptedData);
Assert.Equal(columnEncryptionKey, decryptedData);
}
@@ -183,10 +194,10 @@ public void EncryptKeyAndThenDecryptItSuccessfully(int dataSize)
public void SignAndVerifyColumnMasterKeyMetadataSuccessfully(bool allowEnclaveComputations)
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- byte[] signature = provider.SignColumnMasterKeyMetadata(MASTER_KEY_PATH, allowEnclaveComputations);
+ byte[] signature = provider.SignColumnMasterKeyMetadata(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), allowEnclaveComputations);
Assert.NotNull(signature);
- Assert.True(provider.VerifyColumnMasterKeyMetadata(MASTER_KEY_PATH, allowEnclaveComputations, signature));
- Assert.False(provider.VerifyColumnMasterKeyMetadata(MASTER_KEY_PATH, !allowEnclaveComputations, signature));
+ Assert.True(provider.VerifyColumnMasterKeyMetadata(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), allowEnclaveComputations, signature));
+ Assert.False(provider.VerifyColumnMasterKeyMetadata(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), !allowEnclaveComputations, signature));
}
[Theory]
@@ -197,10 +208,10 @@ public void FailToVerifyColumnMasterKeyMetadataWithWrongCertificate(bool allowEn
{
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- byte[] signature = provider.SignColumnMasterKeyMetadata(MASTER_KEY_PATH, allowEnclaveComputations);
+ byte[] signature = provider.SignColumnMasterKeyMetadata(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), allowEnclaveComputations);
Assert.NotNull(signature);
Assert.False(
- provider.VerifyColumnMasterKeyMetadata("CurrentUser/My/4281446463C6F7F5B8EDFFA4BD6E345E46857CAD", allowEnclaveComputations, signature));
+ provider.VerifyColumnMasterKeyMetadata(ReplaceKeyTokens(SECONDARY_CERTIFICATE_PATH), allowEnclaveComputations, signature));
}
[Fact]
@@ -209,10 +220,10 @@ public void EncryptAndDecryptDataSuccessfully()
{
var input = new byte[] { 1, 2, 3, 4, 5 };
var provider = new SqlColumnEncryptionCertificateStoreProvider();
- byte[] ciphertext = provider.EncryptColumnEncryptionKey(MASTER_KEY_PATH, ENCRYPTION_ALGORITHM,
+ byte[] ciphertext = provider.EncryptColumnEncryptionKey(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), ENCRYPTION_ALGORITHM,
new byte[] { 1, 2, 3, 4, 5 });
byte[] output =
- provider.DecryptColumnEncryptionKey(MASTER_KEY_PATH, ENCRYPTION_ALGORITHM, ciphertext);
+ provider.DecryptColumnEncryptionKey(ReplaceKeyTokens(PRIMARY_CERTIFICATE_PATH), ENCRYPTION_ALGORITHM, ciphertext);
Assert.Equal(input, output);
}
@@ -222,11 +233,9 @@ public void EncryptAndDecryptDataSuccessfully()
public void TestCEKEncryptionReversal(StoreLocation certificateStoreLocation, String certificateStoreNameAndLocation)
{
Assert.True(!string.IsNullOrWhiteSpace(certificateStoreNameAndLocation));
- string certificateName = @"TestCertificate12";
// Fetch the newly created cert.
- X509Certificate2 masterKeyCertificate = Utility.GetCertificate(certificateName,
- certificateStoreLocation);
+ X509Certificate2 masterKeyCertificate = _certFixture.GetCertificate(certificateStoreLocation);
Assert.True(masterKeyCertificate != null);
@@ -389,11 +398,8 @@ public void TestValidCertificatePaths(string certificateStoreNameAndLocation, ob
certificateStoreLocation = StoreLocation.CurrentUser;
}
- string certificateName = @"TestCertificate12";
-
// Fetch the newly created cert.
- X509Certificate2 masterKeyCertificate = Utility.GetCertificate(certificateName,
- certificateStoreLocation);
+ X509Certificate2 masterKeyCertificate = _certFixture.GetCertificate(certificateStoreLocation);
Assert.True(masterKeyCertificate != null);
@@ -459,6 +465,12 @@ public void TestEncryptedCellValueTampering(string parameterToTamper, Utility.CC
Assert.Matches(expectedErrorMessage, e.InnerException.Message);
}
+ private string ReplaceKeyTokens(string keyPath)
+ {
+ return keyPath?.Replace("{primary_thumbprint}", _certFixture.PrimaryColumnEncryptionCertificate.Thumbprint)
+ ?.Replace("{secondary_thumbprint}", _certFixture.SecondaryColumnEncryptionCertificate.Thumbprint);
+ }
+
public class AeadEncryptionParameters : DataAttribute
{
///
@@ -496,7 +508,7 @@ public override IEnumerable
\ No newline at end of file