.Net Core 5.0 - Sql Azure + Always Encrypted + Managed Identity .NET Core 5.0 - Sql Azure + 始终加密 + 托管标识

4

我有一个Azure SQL数据库,其中包含加密列(使用Azure KeyVault始终加密)。我可以从SSMS访问此数据库,并且可以看到解密后的数据。

我还有一个使用.Net Core 5.0制作的Web应用程序,部署在Azure App Service上。该应用服务已启用托管身份验证,并且具有可对该SQL数据库进行解密的enc/dec密钥的Key Vault,其访问策略设置允许此应用服务解密数据。

Web应用程序使用托管身份验证,因为我可以看到未加密的数据被检索而无任何问题。

此外,连接字符串确实包括Column Encryption Setting=enabled;。以下是连接字符串:

Server=tcp:server.database.windows.net,1433;Database=somedb;Column Encryption Setting=enabled;

问题是我找不到任何这种设置的样例。 我找到了一些并且我理解我需要注册SqlColumnEncryptionAzureKeyVaultProvider。 这是我的代码来获取SqlConnection:
    internal static class AzureSqlConnection
    {
        private static bool _isInitialized;

        private static void InitKeyVaultProvider(ILogger logger)
        {
            /*
             * from here - https://github.com/dotnet/SqlClient/blob/master/release-notes/add-ons/AzureKeyVaultProvider/1.2/1.2.0.md
             *      and  - https://github.com/dotnet/SqlClient/blob/master/doc/samples/AzureKeyVaultProviderExample.cs
             *
             */

            try
            {
                // Initialize AKV provider
                SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider =
                    new SqlColumnEncryptionAzureKeyVaultProvider(AzureActiveDirectoryAuthenticationCallback);

                // Register AKV provider
                SqlConnection.RegisterColumnEncryptionKeyStoreProviders(
                    new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(1, StringComparer.OrdinalIgnoreCase)
                    {
                        {SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, sqlColumnEncryptionAzureKeyVaultProvider}
                    });

                _isInitialized = true;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Could not register SqlColumnEncryptionAzureKeyVaultProvider");
                throw;
            }
        }

        internal static async Task<SqlConnection> GetSqlConnection(string connectionString, ILogger logger)
        {
            if (!_isInitialized) InitKeyVaultProvider(logger);

            try
            {
                SqlConnection conn = new SqlConnection(connectionString);
                /*
                         * This is Managed Identity (not Always Encrypted)
                         *  https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi#modify-aspnet-core
                         *
                         */
#if !DEBUG
                conn.AccessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
                logger.LogInformation($"token: {conn.AccessToken}");
#endif
                await conn.OpenAsync();
                return conn;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Could not establish a connection to SQL Server");
                throw;
            }
        }

        private static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource, string scope)
        {
            return await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");

            //AuthenticationContext? authContext = new AuthenticationContext(authority);
            //ClientCredential clientCred = new ClientCredential(s_clientId, s_clientSecret);
            //AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
            //if (result == null)
            //{
            //    throw new InvalidOperationException($"Failed to retrieve an access token for {resource}");
            //}

            //return result.AccessToken;
        }
    }

这段代码不会抛出任何异常,并且适用于非加密查询。但是对于加密查询,我收到以下错误:

解密列加密键失败。无效的密钥存储提供程序名称:'AZURE_KEY_VAULT'。密钥存储提供程序名称必须表示系统密钥存储提供程序或已注册的自定义密钥存储提供程序。有效的系统密钥存储提供程序名称为:'MSSQL_CERTIFICATE_STORE'、'MSSQL_CNG_STORE'、'MSSQL_CSP_PROVIDER'。当前已注册的有效自定义密钥存储提供程序名称为:。请在数据库中验证列主密钥定义中的密钥存储提供程序信息,并验证应用程序中使用的所有自定义密钥存储提供程序是否已正确注册。解密列加密键失败。无效的密钥存储提供程序名称:'AZURE_KEY_VAULT'。密钥存储提供程序名称必须表示系统密钥存储提供程序或已注册的自定义密钥存储提供程序。有效的系统密钥存储提供程序名称为:'MSSQL_CERTIFICATE_STORE'、'MSSQL_CNG_STORE'、'MSSQL_CSP_PROVIDER'。当前已注册的有效自定义密钥存储提供程序名称为:。请在数据库中验证列主密钥定义中的密钥存储提供程序信息,并验证应用程序中使用的所有自定义密钥存储提供程序是否已正确注册。

看起来密钥保管库提供程序没有注册。

我该怎么做才能让它查询加密数据?

使用的软件包

    <PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.6.0" />
    <PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.0" />
    <PackageReference Include="Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" Version="1.2.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />

这个有用吗?看起来是同样的问题,尽管被接受的答案似乎不适用于这里。https://dev59.com/u63la4cB1Zd3GeqPKk2V - Nick.McDermaid
我理解你的痛苦...这个几乎有用但并不完全。https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/create-and-store-column-master-keys-always-encrypted?view=sql-server-2017#making-azure-key-vault-keys-available-to-applications-and-users - Nick.McDermaid
请问您能提供您为MSI配置的访问策略吗? - Jim Xu
像往常一样:关键字:获取,列表;加密:解包,封装,验证,签名。否则它在其他地方都无法工作。 我认为不真正需要关键字列表。但是它仍然存在。 - AlexB
4个回答

2

事实证明,在使用MSI时,无法在.NET 5中读取解密数据。 MS软件包中存在错误,应用程序服务从未获得授权。

您必须使用Service Principal。这非常有效!

更新

我必须感谢提供有效解决方案的MS工程师:

public static async Task<string> KeyVaultAuthenticationCallback(string authority, string resource, string scope)
{
     return await Task.Run(() => new ManagedIdentityCredential().GetToken(new TokenRequestContext(new string [] {"https://vault.azure.net/.default"})).Token);
     /********************** Alternatively, to use User Assigned Managed Identity ****************/
     // var clientId = {clientId_of_UserAssigned_Identity};
     // return await Task.Run(() => new ManagedIdentityCredential(clientId).GetToken(new TokenRequestContext(new string [] {"https://vault.azure.net/.default"})).Token);
}

2

我可以使用这段代码,它提供了一个TokenCredential给SqlColumnEncryption提供程序。当部署为应用服务时,DefaultAzureCredential会返回托管标识:

            SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new DefaultAzureCredential());
            Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>
            {
                { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider }
            };
            SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);


从你的startup.Configure方法中调用它。

0

这不是 .NET 5 的问题。您已经采用了 Azure Key Vault 的示例身份验证回调,并将其更改为针对 Azure SQL DB 而不是 AKV 资源进行特定设置。您需要调整回调以获取有效的 AKV 令牌。这只是使用 Azure.Core 和 Azure.Identity 库获取令牌的一种方式:

    private static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource, string scope)
    {
        return await Task.Run(() => new ManagedIdentityCredential().GetToken(new TokenRequestContext(new string [] {"https://vault.azure.net/.default"})).Token);
    }

除了这个没有作用。我试过了 - https://github.com/dotnet/SqlClient/issues/847#issuecomment-754111237 - AlexB
我主要只是指出你的错误。你的回答作出了一个完全不正确的笼统陈述:“在使用MSI时,无法读取解密后的数据。MS packages中存在一个bug,应用程序服务从未被授权。” 我试图引导你寻找解决方案,而你正在将加密、MSI和.NET 5等所有内容混为一谈,这种分组毫无意义。它们都是独立的功能。您需要实现正确的代码,使它们正常工作。具体来说,就是检索访问Azure秘钥库的正确访问令牌的代码。 - David Engel
明白了。是的,微软的人给了我一些想法(和代码!)让我去尝试。所以这个星期我会继续努力并更新我的答案。 - AlexB

0

针对Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider v3的答案

SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new(new ManagedIdentityCredential());

Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new()
{
    [SqlColumnEncryptionAzureKeyVaultProvider.ProviderName] = azureKeyVaultProvider
};

SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);

microsoft/sql-server-samples 中获取,几乎让我失去理智。

除此之外,还需要:

  • 在 Key Vault 的 IAM 选项卡中分配应用服务托管标识 Key Vault Crypto User 角色。

  • 授予应用服务托管标识 DB 用户 VIEW ANY COLUMN MASTER KEY DEFINITIONVIEW ANY COLUMN ENCRYPTION KEY DEFINITION 权限:

    GRANT VIEW ANY COLUMN MASTER KEY DEFINITION to [app-service-user]
    GRANT VIEW ANY COLUMN ENCRYPTION KEY DEFINITION to [app-service-user]
    
  • 允许应用服务通过 Key Vault 的防火墙访问(即将应用服务的出站 IP 添加到 Key Vault 的防火墙规则中)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接