From b39ff096cbaad796d5b91262b1141ad3b7fb9213 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 15 May 2024 06:51:08 +0100 Subject: [PATCH 1/5] Merged SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs --- .../src/Microsoft.Data.SqlClient.csproj | 1 - ...umnEncryptionEnclaveProvider.NetCoreApp.cs | 25 ------------------- .../SqlColumnEncryptionEnclaveProvider.cs | 7 +++++- 3 files changed, 6 insertions(+), 27 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 2f65a99cd3..6c0a25a552 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -653,7 +653,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs deleted file mode 100644 index fd81db557d..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.NetCoreApp.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - /// - /// The base class that defines the interface for enclave providers for Always Encrypted. An enclave is a protected region of memory inside SQL Server, used for computations on encrypted columns. An enclave provider encapsulates the client-side implementation details of the enclave attestation protocol as well as the logic for creating and caching enclave sessions. - /// - internal abstract partial class SqlColumnEncryptionEnclaveProvider - { - /// Performs enclave attestation, generates a symmetric key for the session, creates a an enclave session and stores the session information in the cache. - /// The information the provider uses to attest the enclave and generate a symmetric key for the session. The format of this information is specific to the enclave attestation protocol. - /// A Diffie-Hellman algorithm object encapsulating a client-side key pair. - /// The set of parameters required for enclave session. - /// The set of extra data needed for attesting the enclave. - /// The length of the extra data needed for attesting the enclave. - /// The requested enclave session or null if the provider does not implement session caching. - /// A counter that the enclave provider is expected to increment each time SqlClient retrieves the session from the cache. The purpose of this field is to prevent replay attacks. - internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, - out SqlEnclaveSession sqlEnclaveSession, out long counter); - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs index 9223702509..5faede3ea8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs @@ -2,10 +2,12 @@ // 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.Security.Cryptography; + namespace Microsoft.Data.SqlClient { /// - internal abstract partial class SqlColumnEncryptionEnclaveProvider + internal abstract class SqlColumnEncryptionEnclaveProvider { /// internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); @@ -13,6 +15,9 @@ internal abstract partial class SqlColumnEncryptionEnclaveProvider /// internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); + /// + internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); + /// internal abstract void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession); } From c6f0591229f806ec5b32f8b997af11038bd0c542 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 15 May 2024 06:57:06 +0100 Subject: [PATCH 2/5] Merged SqlColumnEncryptionEnclaveProvider --- .../src/Microsoft.Data.SqlClient.csproj | 4 ++- .../netfx/src/Microsoft.Data.SqlClient.csproj | 5 ++-- .../SqlColumnEncryptionEnclaveProvider.cs | 25 ------------------- .../SqlColumnEncryptionEnclaveProvider.cs | 10 ++++---- 4 files changed, 10 insertions(+), 34 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs (52%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 6c0a25a552..cd019f005f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -401,6 +401,9 @@ Microsoft\Data\SqlClient\SqlCollation.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionEnclaveProvider.cs + Microsoft\Data\SqlClient\SqlCommandBuilder.cs @@ -652,7 +655,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 5cfe740f6c..779ad1cdc5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -441,8 +441,8 @@ Microsoft\Data\SqlClient\SqlCollation.cs - - Microsoft\Data\SqlClient\SqlColumnEncryptionCertificateStoreProvider.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionEnclaveProvider.cs Microsoft\Data\SqlClient\SqlColumnEncryptionKeyStoreProvider.cs @@ -682,7 +682,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs deleted file mode 100644 index de26b79232..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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.Security.Cryptography; - -namespace Microsoft.Data.SqlClient -{ - /// - internal abstract class SqlColumnEncryptionEnclaveProvider - { - - /// - internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); - - /// - internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); - - /// - internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); - - /// - internal abstract void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession); - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs similarity index 52% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs index 5faede3ea8..21a62a80c3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs @@ -6,19 +6,19 @@ namespace Microsoft.Data.SqlClient { - /// + /// internal abstract class SqlColumnEncryptionEnclaveProvider { - /// + /// internal abstract void GetEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, bool generateCustomData, bool isRetry, out SqlEnclaveSession sqlEnclaveSession, out long counter, out byte[] customData, out int customDataLength); - /// + /// internal abstract SqlEnclaveAttestationParameters GetAttestationParameters(string attestationUrl, byte[] customData, int customDataLength); - /// + /// internal abstract void CreateEnclaveSession(byte[] enclaveAttestationInfo, ECDiffieHellman clientDiffieHellmanKey, EnclaveSessionParameters enclaveSessionParameters, byte[] customData, int customDataLength, out SqlEnclaveSession sqlEnclaveSession, out long counter); - /// + /// internal abstract void InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession); } } From 843dd8496f33cba389f3543168c0f2b661fc1ebc Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 15 May 2024 07:19:12 +0100 Subject: [PATCH 3/5] Merged SqlColumnEncryptionCspProvider --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../SqlColumnEncryptionCspProvider.cs | 436 ------------------ .../SqlColumnEncryptionCspProvider.Windows.cs | 12 +- 4 files changed, 12 insertions(+), 444 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs (93%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index cd019f005f..fd33045ccb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -833,6 +833,9 @@ Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionCspProvider.Windows.cs + Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs @@ -851,7 +854,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 779ad1cdc5..73bc1d18e5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -441,6 +441,9 @@ Microsoft\Data\SqlClient\SqlCollation.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionCspProvider.cs + Microsoft\Data\SqlClient\SqlColumnEncryptionEnclaveProvider.cs @@ -681,7 +684,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs deleted file mode 100644 index 6296d4005d..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs +++ /dev/null @@ -1,436 +0,0 @@ -// 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.Diagnostics; -using System.Security.Cryptography; -using System.Text; -using Microsoft.Win32; - -namespace Microsoft.Data.SqlClient -{ - /// - public class SqlColumnEncryptionCspProvider : SqlColumnEncryptionKeyStoreProvider - { - /// - public const string ProviderName = @"MSSQL_CSP_PROVIDER"; - - /// - /// RSA_OAEP is the only algorithm supported for encrypting/decrypting column encryption keys using this provider. - /// For now, we are keeping all the providers in sync. - /// - private const string RSAEncryptionAlgorithmWithOAEP = @"RSA_OAEP"; - - - private const string HashingAlgorithm = @"SHA256"; - - /// - /// Algorithm version - /// - private readonly byte[] _version = new byte[] { 0x01 }; - - /// - public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) - { - // Validate the input parameters - ValidateNonEmptyCSPKeyPath(masterKeyPath, isSystemOp: true); - - if (null == encryptedColumnEncryptionKey) - { - throw SQL.NullEncryptedColumnEncryptionKey(); - } - - if (0 == encryptedColumnEncryptionKey.Length) - { - throw SQL.EmptyEncryptedColumnEncryptionKey(); - } - - // Validate encryptionAlgorithm - ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true); - - // Create RSA Provider with the given CSP name and key name - RSACryptoServiceProvider rsaProvider = CreateRSACryptoProvider(masterKeyPath, isSystemOp: true); - - // Validate whether the key is RSA one or not and then get the key size - int keySizeInBytes = GetKeySize(rsaProvider); - - // Validate and decrypt the EncryptedColumnEncryptionKey - // Format is - // version + keyPathLength + ciphertextLength + keyPath + ciphervtext + signature - // - // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and - // we will not validate it against the data contained in the CMK metadata (masterKeyPath). - - // Validate the version byte - if (encryptedColumnEncryptionKey[0] != _version[0]) - { - throw SQL.InvalidAlgorithmVersionInEncryptedCEK(encryptedColumnEncryptionKey[0], _version[0]); - } - - // Get key path length - int currentIndex = _version.Length; - UInt16 keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(UInt16); - - // Get ciphertext length - UInt16 cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(UInt16); - - // Skip KeyPath - // KeyPath exists only for troubleshooting purposes and doesnt need validation. - currentIndex += keyPathLength; - - // validate the ciphertext length - if (cipherTextLength != keySizeInBytes) - { - throw SQL.InvalidCiphertextLengthInEncryptedCEKCsp(cipherTextLength, keySizeInBytes, masterKeyPath); - } - - // Validate the signature length - // Signature length should be same as the key side for RSA PKCSv1.5 - int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; - if (signatureLength != keySizeInBytes) - { - throw SQL.InvalidSignatureInEncryptedCEKCsp(signatureLength, keySizeInBytes, masterKeyPath); - } - - // Get ciphertext - byte[] cipherText = new byte[cipherTextLength]; - Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherText.Length); - currentIndex += cipherTextLength; - - // Get signature - byte[] signature = new byte[signatureLength]; - Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length); - - // Compute the hash to validate the signature - byte[] hash; - using (SHA256Cng sha256 = new SHA256Cng()) - { - sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length); - hash = sha256.Hash; - } - - Debug.Assert(hash != null, @"hash should not be null while decrypting encrypted column encryption key."); - - // Validate the signature - if (!RSAVerifySignature(hash, signature, rsaProvider)) - { - throw SQL.InvalidSignature(masterKeyPath); - } - - // Decrypt the CEK - return RSADecrypt(rsaProvider, cipherText); - } - - /// - public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) - { - // Validate the input parameters - ValidateNonEmptyCSPKeyPath(masterKeyPath, isSystemOp: false); - - if (null == columnEncryptionKey) - { - throw SQL.NullColumnEncryptionKey(); - } - else if (0 == columnEncryptionKey.Length) - { - throw SQL.EmptyColumnEncryptionKey(); - } - - // Validate encryptionAlgorithm - ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: false); - - // Create RSA Provider with the given CSP name and key name - RSACryptoServiceProvider rsaProvider = CreateRSACryptoProvider(masterKeyPath, isSystemOp: false); - - // Validate whether the key is RSA one or not and then get the key size - int keySizeInBytes = GetKeySize(rsaProvider); - - // Construct the encryptedColumnEncryptionKey - // Format is - // version + keyPathLength + ciphertextLength + ciphertext + keyPath + signature - // - // We currently only support one version - byte[] version = new byte[] { _version[0] }; - - // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath - byte[] masterKeyPathBytes = Encoding.Unicode.GetBytes(masterKeyPath.ToLowerInvariant()); - byte[] keyPathLength = BitConverter.GetBytes((Int16)masterKeyPathBytes.Length); - - // Encrypt the plain text - byte[] cipherText = RSAEncrypt(rsaProvider, columnEncryptionKey); - byte[] cipherTextLength = BitConverter.GetBytes((Int16)cipherText.Length); - Debug.Assert(cipherText.Length == keySizeInBytes, @"cipherText length does not match the RSA key size"); - - // Compute hash - // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext) - byte[] hash; - using (SHA256Cng sha256 = new SHA256Cng()) - { - sha256.TransformBlock(version, 0, version.Length, version, 0); - sha256.TransformBlock(keyPathLength, 0, keyPathLength.Length, keyPathLength, 0); - sha256.TransformBlock(cipherTextLength, 0, cipherTextLength.Length, cipherTextLength, 0); - sha256.TransformBlock(masterKeyPathBytes, 0, masterKeyPathBytes.Length, masterKeyPathBytes, 0); - sha256.TransformFinalBlock(cipherText, 0, cipherText.Length); - hash = sha256.Hash; - } - - // Sign the hash - byte[] signedHash = RSASignHashedData(hash, rsaProvider); - Debug.Assert(signedHash.Length == keySizeInBytes, @"signed hash length does not match the RSA key size"); - Debug.Assert(RSAVerifySignature(hash, signedHash, rsaProvider), @"Invalid signature of the encrypted column encryption key computed."); - - // Construct the encrypted column encryption key - // EncryptedColumnEncryptionKey = version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature - int encryptedColumnEncryptionKeyLength = version.Length + cipherTextLength.Length + keyPathLength.Length + cipherText.Length + masterKeyPathBytes.Length + signedHash.Length; - byte[] encryptedColumnEncryptionKey = new byte[encryptedColumnEncryptionKeyLength]; - - // Copy version byte - int currentIndex = 0; - Buffer.BlockCopy(version, 0, encryptedColumnEncryptionKey, currentIndex, version.Length); - currentIndex += version.Length; - - // Copy key path length - Buffer.BlockCopy(keyPathLength, 0, encryptedColumnEncryptionKey, currentIndex, keyPathLength.Length); - currentIndex += keyPathLength.Length; - - // Copy ciphertext length - Buffer.BlockCopy(cipherTextLength, 0, encryptedColumnEncryptionKey, currentIndex, cipherTextLength.Length); - currentIndex += cipherTextLength.Length; - - // Copy key path - Buffer.BlockCopy(masterKeyPathBytes, 0, encryptedColumnEncryptionKey, currentIndex, masterKeyPathBytes.Length); - currentIndex += masterKeyPathBytes.Length; - - // Copy ciphertext - Buffer.BlockCopy(cipherText, 0, encryptedColumnEncryptionKey, currentIndex, cipherText.Length); - currentIndex += cipherText.Length; - - // copy the signature - Buffer.BlockCopy(signedHash, 0, encryptedColumnEncryptionKey, currentIndex, signedHash.Length); - - return encryptedColumnEncryptionKey; - } - - /// - public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) - { - throw new NotSupportedException(); - } - - /// - public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) - { - throw new NotSupportedException(); - } - - /// - /// This function validates that the encryption algorithm is RSA_OAEP and if it is not, - /// then throws an exception - /// - /// Asymmetric key encryptio algorithm - /// Indicates if ADO.NET calls or the customer calls the API - private void ValidateEncryptionAlgorithm(string encryptionAlgorithm, bool isSystemOp) - { - // This validates that the encryption algorithm is RSA_OAEP - if (null == encryptionAlgorithm) - { - throw SQL.NullKeyEncryptionAlgorithm(isSystemOp); - } - - if (string.Equals(encryptionAlgorithm, RSAEncryptionAlgorithmWithOAEP, StringComparison.OrdinalIgnoreCase) != true) - { - throw SQL.InvalidKeyEncryptionAlgorithm(encryptionAlgorithm, RSAEncryptionAlgorithmWithOAEP, isSystemOp); - } - } - - - /// - /// Checks if the CSP key path is Empty or Null (and raises exception if they are). - /// - /// CSP key path. - /// Indicates if ADO.NET calls or the customer calls the API - private void ValidateNonEmptyCSPKeyPath(string masterKeyPath, bool isSystemOp) - { - if (string.IsNullOrWhiteSpace(masterKeyPath)) - { - if (null == masterKeyPath) - { - throw SQL.NullCspKeyPath(isSystemOp); - } - else - { - throw SQL.InvalidCspPath(masterKeyPath, isSystemOp); - } - } - } - - /// - /// Encrypt the text using specified CSP key. - /// - /// RSACryptoServiceProvider - /// Plain text Column Encryption Key. - /// Returns an encrypted blob or throws an exception if there are any errors. - private byte[] RSAEncrypt(RSACryptoServiceProvider rscp, byte[] columnEncryptionKey) - { - Debug.Assert(columnEncryptionKey != null); - Debug.Assert(rscp != null); - - return rscp.Encrypt(columnEncryptionKey, fOAEP: true); - } - - /// - /// Decrypt the text using specified CSP key. - /// - /// RSACryptoServiceProvider - /// Encrypted Column Encryption Key. - /// Returns the decrypted plaintext Column Encryption Key or throws an exception if there are any errors. - private byte[] RSADecrypt(RSACryptoServiceProvider rscp, byte[] encryptedColumnEncryptionKey) - { - Debug.Assert((encryptedColumnEncryptionKey != null) && (encryptedColumnEncryptionKey.Length != 0)); - Debug.Assert(rscp != null); - - return rscp.Decrypt(encryptedColumnEncryptionKey, fOAEP: true); - } - - /// - /// Generates signature based on RSA PKCS#v1.5 scheme using a specified CSP Key URL. - /// - /// Text to sign. - /// RSA Provider with a given key - /// Signature - private byte[] RSASignHashedData(byte[] dataToSign, RSACryptoServiceProvider rscp) - { - Debug.Assert((dataToSign != null) && (dataToSign.Length != 0)); - Debug.Assert(rscp != null); - - return rscp.SignData(dataToSign, HashingAlgorithm); - } - - /// - /// Verifies the given RSA PKCSv1.5 signature. - /// - /// - /// - /// RSA Provider with a given key - /// true if signature is valid, false if it is not valid - private bool RSAVerifySignature(byte[] dataToVerify, byte[] signature, RSACryptoServiceProvider rscp) - { - Debug.Assert((dataToVerify != null) && (dataToVerify.Length != 0)); - Debug.Assert((signature != null) && (signature.Length != 0)); - Debug.Assert(rscp != null); - - return rscp.VerifyData(dataToVerify, HashingAlgorithm, signature); - } - - /// - /// Gets the public Key size in bytes - /// - /// RSA Provider with a given key - /// Key size in bytes - private int GetKeySize(RSACryptoServiceProvider rscp) - { - Debug.Assert(rscp != null); - - return rscp.KeySize / 8; - } - - /// - /// Creates a RSACryptoServiceProvider from the given key path which contains both CSP name and key name - /// - /// key path in the format of [CAPI provider name]\[key name] - /// Indicates if ADO.NET calls or the customer calls the API - /// - private RSACryptoServiceProvider CreateRSACryptoProvider(string keyPath, bool isSystemOp) - { - // Get CNGProvider and the KeyID - string cspProviderName; - string keyName; - GetCspProviderAndKeyName(keyPath, isSystemOp, out cspProviderName, out keyName); - - // Verify the existence of CSP and then get the provider type - int providerType = GetProviderType(cspProviderName, keyPath, isSystemOp); - - // Create a new instance of CspParameters for an RSA container. - CspParameters cspParams = new CspParameters(providerType, cspProviderName, keyName); - cspParams.Flags = CspProviderFlags.UseExistingKey; - - RSACryptoServiceProvider rscp = null; - - try - { - //Create a new instance of RSACryptoServiceProvider - rscp = new RSACryptoServiceProvider(cspParams); - } - catch (CryptographicException e) - { - const int KEYSETDOESNOTEXIST = -2146893802; - if (e.HResult == KEYSETDOESNOTEXIST) - { - // Key does not exist - throw SQL.InvalidCspKeyIdentifier(keyName, keyPath, isSystemOp); - } - else - { - // bubble up the exception - throw; - } - } - - return rscp; - } - - /// - /// Extracts the CSP provider name and key name from the given key path - /// - /// key path in the format of [CSP provider name]\[key name] - /// Indicates if ADO.NET calls or the customer calls the API - /// output containing the CSP provider name - /// output containing the key name - private void GetCspProviderAndKeyName(string keyPath, bool isSystemOp, out string cspProviderName, out string keyIdentifier) - { - int indexOfSlash = keyPath.IndexOf(@"/"); - if (indexOfSlash == -1) - { - throw SQL.InvalidCspPath(keyPath, isSystemOp); - } - - cspProviderName = keyPath.Substring(0, indexOfSlash); - keyIdentifier = keyPath.Substring(indexOfSlash + 1, keyPath.Length - (indexOfSlash + 1)); - - if (cspProviderName.Length == 0) - { - throw SQL.EmptyCspName(keyPath, isSystemOp); - } - - if (keyIdentifier.Length == 0) - { - throw SQL.EmptyCspKeyId(keyPath, isSystemOp); - } - } - - /// - /// Gets the provider type from a given CAPI provider name - /// - /// CAPI provider name - /// key path in the format of [CSP provider name]\[key name] - /// Indicates if ADO.NET calls or the customer calls the API - /// - private int GetProviderType(string providerName, string keyPath, bool isSystemOp) - { - string keyName = String.Format(@"SOFTWARE\Microsoft\Cryptography\Defaults\Provider\{0}", providerName); - RegistryKey key = Registry.LocalMachine.OpenSubKey(keyName); - if (key == null) - { - throw SQL.InvalidCspName(providerName, keyPath, isSystemOp); - } - - int providerType = (int)key.GetValue(@"Type"); - key.Close(); - - return providerType; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs similarity index 93% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs index 891c0e1c61..afb82e045b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.Windows.cs @@ -11,11 +11,11 @@ namespace Microsoft.Data.SqlClient { - /// + /// public class SqlColumnEncryptionCspProvider : SqlColumnEncryptionKeyStoreProvider { - /// + /// public const string ProviderName = @"MSSQL_CSP_PROVIDER"; /// @@ -34,7 +34,7 @@ public class SqlColumnEncryptionCspProvider : SqlColumnEncryptionKeyStoreProvide /// private readonly byte[] _version = new byte[] { 0x01 }; - /// + /// public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters @@ -128,7 +128,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e return RSADecrypt(rsaProvider, cipherText); } - /// + /// public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { // Validate the input parameters @@ -218,13 +218,13 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e return encryptedColumnEncryptionKey; } - /// + /// public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw new NotSupportedException(); } - /// + /// public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw new NotSupportedException(); From d5886a216c16b70687c6725761243b9fb1240e32 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Wed, 15 May 2024 07:23:39 +0100 Subject: [PATCH 4/5] Merged SqlColumnEncryptionCngProvider --- .../src/Microsoft.Data.SqlClient.csproj | 3 + .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 + .../SqlColumnEncryptionCngProvider.cs | 392 ------------------ .../SqlColumnEncryptionCngProvider.Windows.cs | 12 +- 4 files changed, 12 insertions(+), 398 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs rename src/Microsoft.Data.SqlClient/{netcore => }/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs (93%) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index fd33045ccb..886a956b32 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -833,6 +833,9 @@ Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionCngProvider.Windows.cs + Microsoft\Data\SqlClient\SqlColumnEncryptionCspProvider.Windows.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 73bc1d18e5..2889d03015 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -441,6 +441,9 @@ Microsoft\Data\SqlClient\SqlCollation.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionCngProvider.cs + Microsoft\Data\SqlClient\SqlColumnEncryptionCspProvider.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs deleted file mode 100644 index ad84233197..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs +++ /dev/null @@ -1,392 +0,0 @@ -// 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.Diagnostics; -using System.Security.Cryptography; -using System.Text; - -namespace Microsoft.Data.SqlClient -{ - /// - public class SqlColumnEncryptionCngProvider : SqlColumnEncryptionKeyStoreProvider - { - /// - public const string ProviderName = @"MSSQL_CNG_STORE"; - - /// - /// RSA_OAEP is the only algorithm supported for encrypting/decrypting column encryption keys using this provider. - /// For now, we are keeping all the providers in sync. - /// - private const string RSAEncryptionAlgorithmWithOAEP = @"RSA_OAEP"; - - /// - /// Algorithm version - /// - private readonly byte[] _version = new byte[] { 0x01 }; - - /// - public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) - { - // Validate the input parameters - ValidateNonEmptyKeyPath(masterKeyPath, isSystemOp: true); - - if (null == encryptedColumnEncryptionKey) - { - throw SQL.NullEncryptedColumnEncryptionKey(); - } - - if (0 == encryptedColumnEncryptionKey.Length) - { - throw SQL.EmptyEncryptedColumnEncryptionKey(); - } - - // Validate encryptionAlgorithm - ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true); - - // Create RSA Provider with the given CNG name and key name - RSACng rsaCngProvider = CreateRSACngProvider(masterKeyPath, isSystemOp: true); - - // Validate whether the key is RSA one or not and then get the key size - int keySizeInBytes = GetKeySize(rsaCngProvider); - - // Validate and decrypt the EncryptedColumnEncryptionKey - // Format is - // version + keyPathLength + ciphertextLength + keyPath + ciphervtext + signature - // - // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and - // we will not validate it against the data contained in the CMK metadata (masterKeyPath). - - // Validate the version byte - if (encryptedColumnEncryptionKey[0] != _version[0]) - { - throw SQL.InvalidAlgorithmVersionInEncryptedCEK(encryptedColumnEncryptionKey[0], _version[0]); - } - - // Get key path length - int currentIndex = _version.Length; - UInt16 keyPathLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(UInt16); - - // Get ciphertext length - UInt16 cipherTextLength = BitConverter.ToUInt16(encryptedColumnEncryptionKey, currentIndex); - currentIndex += sizeof(UInt16); - - // Skip KeyPath - // KeyPath exists only for troubleshooting purposes and doesnt need validation. - currentIndex += keyPathLength; - - // validate the ciphertext length - if (cipherTextLength != keySizeInBytes) - { - throw SQL.InvalidCiphertextLengthInEncryptedCEKCng(cipherTextLength, keySizeInBytes, masterKeyPath); - } - - // Validate the signature length - // Signature length should be same as the key side for RSA PKCSv1.5 - int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength; - if (signatureLength != keySizeInBytes) - { - throw SQL.InvalidSignatureInEncryptedCEKCng(signatureLength, keySizeInBytes, masterKeyPath); - } - - // Get ciphertext - byte[] cipherText = new byte[cipherTextLength]; - Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherText.Length); - currentIndex += cipherTextLength; - - // Get signature - byte[] signature = new byte[signatureLength]; - Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length); - - // Compute the hash to validate the signature - byte[] hash; - using (SHA256Cng sha256 = new SHA256Cng()) - { - sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length); - hash = sha256.Hash; - } - - Debug.Assert(hash != null, @"hash should not be null while decrypting encrypted column encryption key."); - - // Validate the signature - if (!RSAVerifySignature(hash, signature, rsaCngProvider)) - { - throw SQL.InvalidSignature(masterKeyPath); - } - - // Decrypt the CEK - return RSADecrypt(rsaCngProvider, cipherText); - } - - /// - public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) - { - // Validate the input parameters - ValidateNonEmptyKeyPath(masterKeyPath, isSystemOp: false); - - if (null == columnEncryptionKey) - { - throw SQL.NullColumnEncryptionKey(); - } - else if (0 == columnEncryptionKey.Length) - { - throw SQL.EmptyColumnEncryptionKey(); - } - - // Validate encryptionAlgorithm - ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: false); - - // CreateCNGProviderWithKey - RSACng rsaCngProvider = CreateRSACngProvider(masterKeyPath, isSystemOp: false); - - // Validate whether the key is RSA one or not and then get the key size - int keySizeInBytes = GetKeySize(rsaCngProvider); - - // Construct the encryptedColumnEncryptionKey - // Format is - // version + keyPathLength + ciphertextLength + ciphertext + keyPath + signature - // - // We currently only support one version - byte[] version = new byte[] { _version[0] }; - - // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath - byte[] masterKeyPathBytes = Encoding.Unicode.GetBytes(masterKeyPath.ToLowerInvariant()); - byte[] keyPathLength = BitConverter.GetBytes((Int16)masterKeyPathBytes.Length); - - // Encrypt the plain text - byte[] cipherText = RSAEncrypt(rsaCngProvider, columnEncryptionKey); - byte[] cipherTextLength = BitConverter.GetBytes((Int16)cipherText.Length); - Debug.Assert(cipherText.Length == keySizeInBytes, @"cipherText length does not match the RSA key size"); - - // Compute hash - // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext) - byte[] hash; - using (SHA256Cng sha256 = new SHA256Cng()) - { - sha256.TransformBlock(version, 0, version.Length, version, 0); - sha256.TransformBlock(keyPathLength, 0, keyPathLength.Length, keyPathLength, 0); - sha256.TransformBlock(cipherTextLength, 0, cipherTextLength.Length, cipherTextLength, 0); - sha256.TransformBlock(masterKeyPathBytes, 0, masterKeyPathBytes.Length, masterKeyPathBytes, 0); - sha256.TransformFinalBlock(cipherText, 0, cipherText.Length); - hash = sha256.Hash; - } - - // Sign the hash - byte[] signedHash = RSASignHashedData(hash, rsaCngProvider); - Debug.Assert(signedHash.Length == keySizeInBytes, @"signed hash length does not match the RSA key size"); - Debug.Assert(RSAVerifySignature(hash, signedHash, rsaCngProvider), @"Invalid signature of the encrypted column encryption key computed."); - - // Construct the encrypted column encryption key - // EncryptedColumnEncryptionKey = version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature - int encryptedColumnEncryptionKeyLength = version.Length + cipherTextLength.Length + keyPathLength.Length + cipherText.Length + masterKeyPathBytes.Length + signedHash.Length; - byte[] encryptedColumnEncryptionKey = new byte[encryptedColumnEncryptionKeyLength]; - - // Copy version byte - int currentIndex = 0; - Buffer.BlockCopy(version, 0, encryptedColumnEncryptionKey, currentIndex, version.Length); - currentIndex += version.Length; - - // Copy key path length - Buffer.BlockCopy(keyPathLength, 0, encryptedColumnEncryptionKey, currentIndex, keyPathLength.Length); - currentIndex += keyPathLength.Length; - - // Copy ciphertext length - Buffer.BlockCopy(cipherTextLength, 0, encryptedColumnEncryptionKey, currentIndex, cipherTextLength.Length); - currentIndex += cipherTextLength.Length; - - // Copy key path - Buffer.BlockCopy(masterKeyPathBytes, 0, encryptedColumnEncryptionKey, currentIndex, masterKeyPathBytes.Length); - currentIndex += masterKeyPathBytes.Length; - - // Copy ciphertext - Buffer.BlockCopy(cipherText, 0, encryptedColumnEncryptionKey, currentIndex, cipherText.Length); - currentIndex += cipherText.Length; - - // copy the signature - Buffer.BlockCopy(signedHash, 0, encryptedColumnEncryptionKey, currentIndex, signedHash.Length); - - return encryptedColumnEncryptionKey; - } - - /// - public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) - { - throw new NotSupportedException(); - } - - /// - public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) - { - throw new NotSupportedException(); - } - - /// - /// This function validates that the encryption algorithm is RSA_OAEP and if it is not, - /// then throws an exception - /// - /// Asymmetric key encryptio algorithm - /// Indicates if ADO.NET calls or the customer calls the API - private void ValidateEncryptionAlgorithm(string encryptionAlgorithm, bool isSystemOp) - { - // This validates that the encryption algorithm is RSA_OAEP - if (null == encryptionAlgorithm) - { - throw SQL.NullKeyEncryptionAlgorithm(isSystemOp); - } - - if (!string.Equals(encryptionAlgorithm, RSAEncryptionAlgorithmWithOAEP, StringComparison.OrdinalIgnoreCase)) - { - throw SQL.InvalidKeyEncryptionAlgorithm(encryptionAlgorithm, RSAEncryptionAlgorithmWithOAEP, isSystemOp); - } - } - - /// - /// Checks if the CNG key path is Empty or Null (and raises exception if they are). - /// - /// keypath containing the CNG provider name and key name - /// Indicates if ADO.NET calls or the customer calls the API - private void ValidateNonEmptyKeyPath(string masterKeyPath, bool isSystemOp) - { - if (string.IsNullOrWhiteSpace(masterKeyPath)) - { - if (null == masterKeyPath) - { - throw SQL.NullCngKeyPath(isSystemOp); - } - else - { - throw SQL.InvalidCngPath(masterKeyPath, isSystemOp); - } - } - } - - /// - /// Encrypt the text using specified CNG key. - /// - /// RSA CNG Provider. - /// Plain text Column Encryption Key. - /// Returns an encrypted blob or throws an exception if there are any errors. - private byte[] RSAEncrypt(RSACng rsaCngProvider, byte[] columnEncryptionKey) - { - Debug.Assert(columnEncryptionKey != null); - Debug.Assert(rsaCngProvider != null); - - return rsaCngProvider.Encrypt(columnEncryptionKey, RSAEncryptionPadding.OaepSHA1); - } - - /// - /// Decrypt the text using the specified CNG key. - /// - /// RSA CNG Provider. - /// Encrypted Column Encryption Key. - /// Returns the decrypted plaintext Column Encryption Key or throws an exception if there are any errors. - private byte[] RSADecrypt(RSACng rsaCngProvider, byte[] encryptedColumnEncryptionKey) - { - Debug.Assert((encryptedColumnEncryptionKey != null) && (encryptedColumnEncryptionKey.Length != 0)); - Debug.Assert(rsaCngProvider != null); - - return rsaCngProvider.Decrypt(encryptedColumnEncryptionKey, RSAEncryptionPadding.OaepSHA1); - } - - /// - /// Generates signature based on RSA PKCS#v1.5 scheme using a specified CNG Key. - /// - /// Text to sign. - /// RSA CNG Provider. - /// Signature - private byte[] RSASignHashedData(byte[] dataToSign, RSACng rsaCngProvider) - { - Debug.Assert((dataToSign != null) && (dataToSign.Length != 0)); - Debug.Assert(rsaCngProvider != null); - - return rsaCngProvider.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - } - - /// - /// Verifies the given RSA PKCSv1.5 signature. - /// - /// - /// - /// RSA CNG Provider. - /// true if signature is valid, false if it is not valid - private bool RSAVerifySignature(byte[] dataToVerify, byte[] signature, RSACng rsaCngProvider) - { - Debug.Assert((dataToVerify != null) && (dataToVerify.Length != 0)); - Debug.Assert((signature != null) && (signature.Length != 0)); - Debug.Assert(rsaCngProvider != null); - - return rsaCngProvider.VerifyData(dataToVerify, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - } - - /// - /// Gets the public Key size in bytes - /// - /// RSA CNG Provider. - /// Key size in bytes - private int GetKeySize(RSACng rsaCngProvider) - { - Debug.Assert(rsaCngProvider != null); - - return rsaCngProvider.KeySize / 8; // Convert from bits to byte - } - - /// - /// Creates a RSACng object from the given keyName - /// - /// - /// Indicates if ADO.NET calls or the customer calls the API - /// - private RSACng CreateRSACngProvider(string keyPath, bool isSystemOp) - { - // Get CNGProvider and the KeyID - string cngProviderName; - string keyIdentifier; - GetCngProviderAndKeyId(keyPath, isSystemOp, out cngProviderName, out keyIdentifier); - - CngProvider cngProvider = new CngProvider(cngProviderName); - CngKey cngKey; - - try - { - cngKey = CngKey.Open(keyIdentifier, cngProvider); - } - catch (CryptographicException) - { - throw SQL.InvalidCngKey(keyPath, cngProviderName, keyIdentifier, isSystemOp); - } - - return new RSACng(cngKey); - } - - /// - /// Extracts the CNG provider and key name from the key path - /// - /// keypath in the format [CNG Provider]\[KeyName] - /// Indicates if ADO.NET calls or the customer calls the API - /// CNG Provider - /// Key identifier inside the CNG provider - private void GetCngProviderAndKeyId(string keyPath, bool isSystemOp, out string cngProvider, out string keyIdentifier) - { - int indexOfSlash = keyPath.IndexOf(@"/"); - if (indexOfSlash == -1) - { - throw SQL.InvalidCngPath(keyPath, isSystemOp); - } - - cngProvider = keyPath.Substring(0, indexOfSlash); - keyIdentifier = keyPath.Substring(indexOfSlash + 1, keyPath.Length - (indexOfSlash + 1)); - - if (cngProvider.Length == 0) - { - throw SQL.EmptyCngName(keyPath, isSystemOp); - } - - if (keyIdentifier.Length == 0) - { - throw SQL.EmptyCngKeyId(keyPath, isSystemOp); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs similarity index 93% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs index a3c92002d1..7ba77a7fa7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.Windows.cs @@ -9,10 +9,10 @@ namespace Microsoft.Data.SqlClient { - /// + /// public class SqlColumnEncryptionCngProvider : SqlColumnEncryptionKeyStoreProvider { - /// + /// public const string ProviderName = @"MSSQL_CNG_STORE"; /// @@ -26,7 +26,7 @@ public class SqlColumnEncryptionCngProvider : SqlColumnEncryptionKeyStoreProvide /// private readonly byte[] _version = new byte[] { 0x01 }; - /// + /// public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters @@ -120,7 +120,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e return RSADecrypt(rsaCngProvider, cipherText); } - /// + /// public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { // Validate the input parameters @@ -210,13 +210,13 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e return encryptedColumnEncryptionKey; } - /// + /// public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw new NotSupportedException(); } - /// + /// public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw new NotSupportedException(); From 787b0996b64e42a94d1a01fa321d5b1c87ebe4dc Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:25:49 +0100 Subject: [PATCH 5/5] Corrected project files post-merge --- .../netcore/src/Microsoft.Data.SqlClient.csproj | 1 - .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 886a956b32..6e3d6000ba 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -856,7 +856,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 2889d03015..774e3fe143 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -441,6 +441,9 @@ Microsoft\Data\SqlClient\SqlCollation.cs + + Microsoft\Data\SqlClient\SqlColumnEncryptionCertificateStoreProvider.cs + Microsoft\Data\SqlClient\SqlColumnEncryptionCngProvider.cs @@ -686,7 +689,6 @@ -