Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace Microsoft.Data.SqlClient.AlwaysEncrypted;
/// This takes ownership of the RSA instance supplied to it, disposing of it when Dispose is called.
/// </para>
/// </remarks>
internal readonly ref struct ColumnMasterKeyMetadata // : IDisposable
internal readonly ref struct ColumnMasterKeyMetadata : IDisposable
{
private static readonly HashAlgorithmName s_hashAlgorithm = HashAlgorithmName.SHA256;

Expand All @@ -49,7 +49,6 @@ private struct Sha256Hash
#endif
private readonly RSA _rsa;

// @TODO: SqlColumnEncryptionCertificateStoreProvider.SignMasterKeyMetadata and .VerifyMasterKeyMetadata should use this type.
/// <summary>
/// Represents metadata associated with a column master key, including its cryptographic hash, path, provider name,
/// and enclave computation settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace Microsoft.Data.SqlClient.AlwaysEncrypted;
/// This takes ownership of the RSA instance supplied to it, disposing of it when Dispose is called.
/// </para>
/// </remarks>
internal readonly ref struct EncryptedColumnEncryptionKeyParameters // : IDisposable
internal readonly ref struct EncryptedColumnEncryptionKeyParameters : IDisposable
{
private const byte AlgorithmVersion = 0x01;

Expand All @@ -64,7 +64,6 @@ namespace Microsoft.Data.SqlClient.AlwaysEncrypted;
private readonly string _keyType;
private readonly string _keyPathReference;

// @TODO: SqlColumnEncryptionCertificateStoreProvider, SqlColumnEncryptionCngProvider and SqlColumnEncryptionCspProvider should use this type.
/// <summary>
/// Initializes a new instance of the <see cref="EncryptedColumnEncryptionKeyParameters"/> struct with the specified
/// RSA key, key path, key type, and key path reference.
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1783,31 +1783,16 @@ internal static Exception InvalidCiphertextLengthInEncryptedCEK(string keyType,
return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEK, actual, expected, keyType, masterKeyPath, keyPathReference), TdsEnums.TCE_PARAM_ENCRYPTED_CEK);
}

internal static Exception InvalidCiphertextLengthInEncryptedCEKCertificate(int actual, int expected, string certificateName)
{
return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCiphertextLengthInEncryptedCEKCertificate, actual, expected, certificateName), TdsEnums.TCE_PARAM_ENCRYPTED_CEK);
}

internal static Exception InvalidSignatureInEncryptedCEK(string keyType, string keyPathReference, int actual, int expected, string masterKeyPath)
{
return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEK, actual, expected, keyType, masterKeyPath, keyPathReference), TdsEnums.TCE_PARAM_ENCRYPTED_CEK);
}

internal static Exception InvalidSignatureInEncryptedCEKCertificate(int actual, int expected, string masterKeyPath)
{
return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignatureInEncryptedCEKCertificate, actual, expected, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK);
}

internal static Exception InvalidSignature(string masterKeyPath, string keyType)
{
return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidSignature, keyType, masterKeyPath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK);
}

internal static Exception InvalidCertificateSignature(string certificatePath)
{
return ADP.Argument(StringsHelper.GetString(Strings.TCE_InvalidCertificateSignature, certificatePath), TdsEnums.TCE_PARAM_ENCRYPTED_CEK);
}

internal static Exception CertificateWithNoPrivateKey(string keyPath, bool isSystemOp)
{
if (isSystemOp)
Expand Down
27 changes: 0 additions & 27 deletions src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 0 additions & 9 deletions src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4077,21 +4077,12 @@
<data name="TCE_InvalidCiphertextLengthInEncryptedCEK" xml:space="preserve">
<value>The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key ({2}) in '{3}'. The encrypted column encryption key may be corrupt, or the specified {4} path may be incorrect.</value>
</data>
<data name="TCE_InvalidCiphertextLengthInEncryptedCEKCertificate" xml:space="preserve">
<value>The specified encrypted column encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.</value>
</data>
<data name="TCE_InvalidSignatureInEncryptedCEK" xml:space="preserve">
<value>The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key ({2}) in '{3}'. The encrypted column encryption key may be corrupt, or the specified {4} path may be incorrect.</value>
</data>
<data name="TCE_InvalidSignatureInEncryptedCEKCertificate" xml:space="preserve">
<value>The specified encrypted column encryption key's signature length: {0} does not match the signature length: {1} when using column master key (certificate) in '{2}'. The encrypted column encryption key may be corrupt, or the specified certificate path may be incorrect.</value>
</data>
<data name="TCE_InvalidSignature" xml:space="preserve">
<value>The specified encrypted column encryption key signature does not match the signature computed with the column master key ({0}) in '{1}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.</value>
</data>
<data name="TCE_InvalidCertificateSignature" xml:space="preserve">
<value>The specified encrypted column encryption key signature does not match the signature computed with the column master key (certificate) in '{0}'. The encrypted column encryption key may be corrupt, or the specified path may be incorrect.</value>
</data>
<data name="TCE_CertificateWithNoPrivateKey" xml:space="preserve">
<value>Certificate specified in key path '{0}' does not have a private key to encrypt a column encryption key. Verify the certificate is imported correctly.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Microsoft.Data.SqlClient;
using System.Diagnostics;
using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup;
using Xunit;

namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted
Expand Down Expand Up @@ -49,6 +50,8 @@ public void TestAeadCryptoWithNativeBaseline()
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))]
public void TestRsaCryptoWithNativeBaseline()
{
SqlColumnEncryptionCertificateStoreProvider rsaProvider = new();

// Initialize the reader for resource text file which has the native code generated baseline.
CryptoNativeBaselineReader cryptoNativeBaselineReader = new CryptoNativeBaselineReader();

Expand All @@ -64,24 +67,49 @@ public void TestRsaCryptoWithNativeBaseline()
byte[] rsaKeyPair = cryptoParametersListForTest[0].RsaKeyPair;
byte[] rsaPfx = cryptoParametersListForTest[1].RsaKeyPair;

// For each crypto vector, run the test to compare the output generated through sqlclient's code and the native code.
foreach (CryptoVector cryptoParameter in cryptoParametersListForTest)
// Convert the PFX into a certificate and install it into the local user's certificate store.
// We can only do this cross-platform on the CurrentUser store, which matches the baseline data we have.
Debug.Assert(rsaPfx != null && rsaPfx.Length > 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an xUnit Assert() ? Here and below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. There were a few other existing instances in the same test, I've changed those too.


X509Store store = null;
bool addedToStore = false;
#if NET9_0_OR_GREATER
using X509Certificate2 x509 = X509CertificateLoader.LoadPkcs12(rsaPfx, @"P@zzw0rD!SqlvN3x+");
#else
using X509Certificate2 x509 = new(rsaPfx, @"P@zzw0rD!SqlvN3x+");
#endif
Debug.Assert(x509.HasPrivateKey);

try
{
if (cryptoParameter.CryptNativeTestVectorTypeVal == CryptNativeTestVectorType.Rsa)
{
// Verify that we are using the right padding scheme for RSA encryption
byte[] plaintext = CertificateUtility.DecryptRsaDirectly(rsaPfx, cryptoParameter.CiphertextCek, @"Test");
Assert.True(cryptoParameter.PlaintextCek.SequenceEqual(plaintext), "Plaintext CEK Value does not match with the native code baseline.");
store = new(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);

// Verify that the signed blob is conforming to our envelope (SHA-256, PKCS 1 padding)
bool signatureVerified = CertificateUtility.VerifyRsaSignatureDirectly(cryptoParameter.HashedCek, cryptoParameter.SignedCek, rsaPfx);
Assert.True(signatureVerified, "Plaintext CEK signature scheme does not match with the native code baseline.");
store.Add(x509);
addedToStore = true;

//// TODO: Programmatically install the in-memory PFX into the right store (based on path) & use the public API
//plaintext = Utility.VerifyRsaSignature(cryptoParameter.PathCek, cryptoParameter.FinalcellCek, rsaPfx);
//CError.Compare(cryptoParameter.PlaintextCek.SequenceEqual(plaintext), "Plaintext CEK Value does not match with the native code baseline (end to end).");
// For each crypto vector, run the test to compare the output generated through sqlclient's code and the native code.
foreach (CryptoVector cryptoParameter in cryptoParametersListForTest)
{
if (cryptoParameter.CryptNativeTestVectorTypeVal == CryptNativeTestVectorType.Rsa)
{
Debug.Assert(cryptoParameter.PathCek != null && cryptoParameter.PathCek.StartsWith("CurrentUser/My"));

// Decrypt the supplied final cell CEK, and ensure that the plaintext CEK value matches the native code baseline.
byte[] plaintext = rsaProvider.DecryptColumnEncryptionKey(cryptoParameter.PathCek, "RSA_OAEP", cryptoParameter.FinalcellCek);
Assert.Equal(cryptoParameter.PlaintextCek, plaintext);
}
}
}
finally
{
if (addedToStore)
{
store.Remove(x509);
}

store?.Dispose();
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ private CertificateUtility()
public static MethodInfo sqlAeadAes256CbcHmac256FactoryCreate = sqlAeadAes256CbcHmac256Factory.GetMethod("Create", BindingFlags.Instance | BindingFlags.NonPublic);
public static Type sqlClientEncryptionAlgorithm = systemData.GetType("Microsoft.Data.SqlClient.SqlClientEncryptionAlgorithm");
public static MethodInfo sqlClientEncryptionAlgorithmEncryptData = sqlClientEncryptionAlgorithm.GetMethod("EncryptData", BindingFlags.Instance | BindingFlags.NonPublic);
public static Type SqlColumnEncryptionCertificateStoreProvider = systemData.GetType("Microsoft.Data.SqlClient.SqlColumnEncryptionCertificateStoreProvider");
public static MethodInfo SqlColumnEncryptionCertificateStoreProviderRSADecrypt = SqlColumnEncryptionCertificateStoreProvider.GetMethod("RSADecrypt", BindingFlags.Instance | BindingFlags.NonPublic);
public static MethodInfo SqlColumnEncryptionCertificateStoreProviderRSAVerifySignature = SqlColumnEncryptionCertificateStoreProvider.GetMethod("RSAVerifySignature", BindingFlags.Instance | BindingFlags.NonPublic);
public static MethodInfo sqlClientEncryptionAlgorithmDecryptData = sqlClientEncryptionAlgorithm.GetMethod("DecryptData", BindingFlags.Instance | BindingFlags.NonPublic);
public static Type SqlSymmetricKeyCache = systemData.GetType("Microsoft.Data.SqlClient.SqlSymmetricKeyCache");
public static MethodInfo SqlSymmetricKeyCacheGetInstance = SqlSymmetricKeyCache.GetMethod("GetInstance", BindingFlags.Static | BindingFlags.NonPublic);
Expand Down Expand Up @@ -102,41 +99,6 @@ internal static void CleanSqlClientCache()
ClearCache(cache);
}

internal static byte[] DecryptRsaDirectly(byte[] rsaPfx, byte[] ciphertextCek, string masterKeyPath)
{
Debug.Assert(rsaPfx != null && rsaPfx.Length > 0);
// The rest of the parameters may be invalid for exception handling test cases

#if NET9_0_OR_GREATER
X509Certificate2 x509 = X509CertificateLoader.LoadPkcs12(rsaPfx, @"P@zzw0rD!SqlvN3x+");
#else
X509Certificate2 x509 = new(rsaPfx, @"P@zzw0rD!SqlvN3x+");
#endif
Debug.Assert(x509.HasPrivateKey);

SqlColumnEncryptionCertificateStoreProvider rsaProvider = new SqlColumnEncryptionCertificateStoreProvider();
Object RsaDecryptionResult = SqlColumnEncryptionCertificateStoreProviderRSADecrypt.Invoke(rsaProvider, new object[] { ciphertextCek, x509 });

return (byte[])RsaDecryptionResult;
}

internal static bool VerifyRsaSignatureDirectly(byte[] hashedCek, byte[] signedCek, byte[] rsaPfx)
{
Debug.Assert(rsaPfx != null && rsaPfx.Length > 0);

#if NET9_0_OR_GREATER
X509Certificate2 x509 = X509CertificateLoader.LoadPkcs12(rsaPfx, @"P@zzw0rD!SqlvN3x+");
#else
X509Certificate2 x509 = new(rsaPfx, @"P@zzw0rD!SqlvN3x+");
#endif
Debug.Assert(x509.HasPrivateKey);

SqlColumnEncryptionCertificateStoreProvider rsaProvider = new SqlColumnEncryptionCertificateStoreProvider();
Object RsaVerifySignatureResult = SqlColumnEncryptionCertificateStoreProviderRSAVerifySignature.Invoke(rsaProvider, new object[] { hashedCek, signedCek, x509 });

return (bool)RsaVerifySignatureResult;
}

/// <summary>
/// Decrypt Data using AEAD
/// </summary>
Expand Down
Loading