【问题标题】:.Net Core 5.0 - Sql Azure + Always Encrypted + Managed Identity.Net Core 5.0 - Sql Azure + Always Encrypted + Managed Identity
【发布时间】:2020-12-11 04:35:18
【问题描述】:

我有一个带有加密列的 Azure SQL Db(始终使用 Azure KeyVault 加密)。我可以从 SSMS 访问这个数据库,我可以看到解密后的数据。

我还有一个使用 .Net Core 5.0 制作的 Web 应用程序,它部署到 Azure 应用程序服务。应用服务已打开托管标识,并且具有该 SQL Db 的编码/解码密钥的 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://docs.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" />

【问题讨论】:

  • 这有帮助吗?它看起来像同样的问题,虽然接受的答案似乎不适用于这里stackoverflow.com/questions/51618582/…
  • 我感觉到你的痛苦....这几乎有用,但没有用。 docs.microsoft.com/en-us/sql/relational-databases/security/…
  • 能否提供您为 MSI 配置的访问策略?
  • 像往常一样:键:获取,列表;加密:打开、包装、验证、签名。否则它不会在其他任何地方工作。我相信 Key-List 并不是真正需要的。但无论如何它都在那里

标签: azure .net-core azure-sql-database azure-keyvault always-encrypted


【解决方案1】:

事实证明,当使用 MSI 时,无法读取 .NET 5 中的解密数据。 MS包中存在错误,应用服务从未被授权。

您必须使用服务主体。这就像一个魅力!

更新

我要感谢提供有效解决方案的 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】:

    我能够使用此代码,该代码使用向 SqlColumnEncryption 提供程序提供 TokenCredential。 DefaultAzureCredential 在部署为应用服务时返回托管标识:

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

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

    【讨论】:

      【解决方案3】:

      这不是 .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);
          }
      

      【讨论】:

      • 除非这不起作用。我试过了 - github.com/dotnet/SqlClient/issues/847#issuecomment-754111237
      • 我主要只是指出你的错误。您的回答提出了一个完全不正确的一揽子声明:“使用 MSI 时,无法读取 .NET 5 中的解密数据。MS 包中存在错误,应用服务从未获得授权。”我试图向您指出存在解决方案的方向,并且当分组没有意义时,您将加密、MSI 和 .NET 5 混为一谈。它们都是独立的特征。您需要实现使它们工作的正确代码。具体来说,检索用于访问 Azure 密钥保管库的正确访问令牌的代码。
      • 知道了。是的,MS 家伙给了我一些想法(和代码!)来尝试。所以我这周会继续努力并更新我的答案。
      猜你喜欢
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-25
      • 2020-04-24
      • 2022-01-02
      相关资源
      最近更新 更多