private static SqlClientSymmetricKey GetKeyFromLocalProviders(SqlEncryptionKeyInfo keyInfo, SqlConnection connection, SqlCommand command) { string serverName = connection.DataSource; Debug.Assert(serverName is not null, @"serverName should not be null."); Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); ThrowIfKeyPathIsNotTrustedForServer(serverName, keyInfo.keyPath); if (!TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command)) { throw SQL.UnrecognizedKeyStoreProviderName(keyInfo.keyStoreName, SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(), GetListOfProviderNamesThatWereSearched(connection, command)); } // Decrypt the CEK // We will simply bubble up the exception from the DecryptColumnEncryptionKey function. byte[] plaintextKey; try { plaintextKey = provider.DecryptColumnEncryptionKey(keyInfo.keyPath, keyInfo.algorithmName, keyInfo.encryptedKey); } catch (Exception e) { // Generate a new exception and throw. string keyHex = GetBytesAsString(keyInfo.encryptedKey, fLast: true, countOfBytes: 10); throw SQL.KeyDecryptionFailed(keyInfo.keyStoreName, keyHex, e); } return(new SqlClientSymmetricKey(plaintextKey)); }
/// <summary> /// Verifies Column Master Key Signature. /// </summary> internal static void VerifyColumnMasterKeySignature(string keyStoreName, string keyPath, bool isEnclaveEnabled, byte[] CMKSignature, SqlConnection connection, SqlCommand command) { bool isValidSignature = false; try { Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); if (CMKSignature is null || CMKSignature.Length == 0) { throw SQL.ColumnMasterKeySignatureNotFound(keyPath); } ThrowIfKeyPathIsNotTrustedForServer(connection.DataSource, keyPath); // Attempt to look up the provider and verify CMK Signature if (!TryGetColumnEncryptionKeyStoreProvider(keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command)) { throw SQL.InvalidKeyStoreProviderName(keyStoreName, SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(), GetListOfProviderNamesThatWereSearched(connection, command)); } if (ShouldUseInstanceLevelProviderFlow(keyStoreName, connection, command)) { isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, CMKSignature); } else { bool?signatureVerificationResult = ColumnMasterKeyMetadataSignatureVerificationCache.GetSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature); if (signatureVerificationResult is null) { // We will simply bubble up the exception from VerifyColumnMasterKeyMetadata function. isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, CMKSignature); ColumnMasterKeyMetadataSignatureVerificationCache.AddSignatureVerificationResult(keyStoreName, keyPath, isEnclaveEnabled, CMKSignature, isValidSignature); } else { isValidSignature = signatureVerificationResult.Value; } } } catch (Exception e) { throw SQL.UnableToVerifyColumnMasterKeySignature(e); } if (!isValidSignature) { throw SQL.ColumnMasterKeySignatureVerificationFailed(keyPath); } }
/// <summary> /// <para> Retrieves Symmetric Key (in plaintext) given the encryption material.</para> /// </summary> internal SqlClientSymmetricKey GetKey(SqlEncryptionKeyInfo keyInfo, SqlConnection connection, SqlCommand command) { string serverName = connection.DataSource; Debug.Assert(serverName is not null, @"serverName should not be null."); StringBuilder cacheLookupKeyBuilder = new StringBuilder(serverName, capacity: serverName.Length + SqlSecurityUtility.GetBase64LengthFromByteLength(keyInfo.encryptedKey.Length) + keyInfo.keyStoreName.Length + 2 /*separators*/); #if DEBUG int capacity = cacheLookupKeyBuilder.Capacity; #endif //DEBUG cacheLookupKeyBuilder.Append(":"); cacheLookupKeyBuilder.Append(Convert.ToBase64String(keyInfo.encryptedKey)); cacheLookupKeyBuilder.Append(":"); cacheLookupKeyBuilder.Append(keyInfo.keyStoreName); string cacheLookupKey = cacheLookupKeyBuilder.ToString(); #if DEBUG Debug.Assert(cacheLookupKey.Length <= capacity, "We needed to allocate a larger array"); #endif //DEBUG // Lookup the key in cache if (!(_cache.Get(cacheLookupKey) is SqlClientSymmetricKey encryptionKey)) { Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths is not null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); SqlSecurityUtility.ThrowIfKeyPathIsNotTrustedForServer(serverName, keyInfo.keyPath); // Key Not found, attempt to look up the provider and decrypt CEK if (!SqlSecurityUtility.TryGetColumnEncryptionKeyStoreProvider(keyInfo.keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider, connection, command)) { throw SQL.UnrecognizedKeyStoreProviderName(keyInfo.keyStoreName, SqlConnection.GetColumnEncryptionSystemKeyStoreProvidersNames(), SqlSecurityUtility.GetListOfProviderNamesThatWereSearched(connection, command)); } // Decrypt the CEK // We will simply bubble up the exception from the DecryptColumnEncryptionKey function. byte[] plaintextKey; try { // to prevent conflicts between CEK caches, global providers should not use their own CEK caches provider.ColumnEncryptionKeyCacheTtl = new TimeSpan(0); plaintextKey = provider.DecryptColumnEncryptionKey(keyInfo.keyPath, keyInfo.algorithmName, keyInfo.encryptedKey); } catch (Exception e) { // Generate a new exception and throw. string keyHex = SqlSecurityUtility.GetBytesAsString(keyInfo.encryptedKey, fLast: true, countOfBytes: 10); throw SQL.KeyDecryptionFailed(keyInfo.keyStoreName, keyHex, e); } encryptionKey = new SqlClientSymmetricKey(plaintextKey); // If the cache TTL is zero, don't even bother inserting to the cache. if (SqlConnection.ColumnEncryptionKeyCacheTtl != TimeSpan.Zero) { // In case multiple threads reach here at the same time, the first one wins. // The allocated memory will be reclaimed by Garbage Collector. DateTimeOffset expirationTime = DateTimeOffset.UtcNow.Add(SqlConnection.ColumnEncryptionKeyCacheTtl); _cache.Add(cacheLookupKey, encryptionKey, expirationTime); } } return(encryptionKey); }