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 8499f7801d..c259692e69 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -420,6 +420,9 @@
Microsoft\Data\SqlClient\SqlCollation.cs
+
+ Microsoft\Data\SqlClient\SqlColumnEncryptionEnclaveProvider.cs
+
Microsoft\Data\SqlClient\SqlCommandBuilder.cs
@@ -673,8 +676,6 @@
-
-
@@ -855,6 +856,12 @@
Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs
+
+ Microsoft\Data\SqlClient\SqlColumnEncryptionCngProvider.Windows.cs
+
+
+ Microsoft\Data\SqlClient\SqlColumnEncryptionCspProvider.Windows.cs
+
Microsoft\Data\SqlClient\SSPI\NativeSSPIContextProvider.cs
@@ -871,8 +878,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
deleted file mode 100644
index 9223702509..0000000000
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs
+++ /dev/null
@@ -1,19 +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.
-
-namespace Microsoft.Data.SqlClient
-{
- ///
- internal abstract partial 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 InvalidateEnclaveSession(EnclaveSessionParameters enclaveSessionParameters, SqlEnclaveSession enclaveSession);
- }
-}
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 9b161fa048..02ac8c7449 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -469,6 +469,15 @@
Microsoft\Data\SqlClient\SqlColumnEncryptionCertificateStoreProvider.cs
+
+ Microsoft\Data\SqlClient\SqlColumnEncryptionCngProvider.cs
+
+
+ Microsoft\Data\SqlClient\SqlColumnEncryptionCspProvider.cs
+
+
+ Microsoft\Data\SqlClient\SqlColumnEncryptionEnclaveProvider.cs
+
Microsoft\Data\SqlClient\SqlColumnEncryptionKeyStoreProvider.cs
@@ -707,9 +716,6 @@
-
-
-
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 9ea5374c9f..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 (encryptedColumnEncryptionKey == null)
- {
- 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 (columnEncryptionKey == null)
- {
- 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 (encryptionAlgorithm == null)
- {
- 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 (masterKeyPath == null)
- {
- 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/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs
deleted file mode 100644
index 9489f1b5f4..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 (encryptedColumnEncryptionKey == null)
- {
- 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 (columnEncryptionKey == null)
- {
- 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 (encryptionAlgorithm == null)
- {
- 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 (masterKeyPath == null)
- {
- 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/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 cb09f49d91..bb28a12d60 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();
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 c356e73527..6a5227d039 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();
diff --git a/src/Microsoft.Data.SqlClient/netfx/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/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs
index de26b79232..21a62a80c3 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlColumnEncryptionEnclaveProvider.cs
@@ -6,20 +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);
}
}