.Net Core 5.0 - Sql Azure + Selalu Dienkripsi + Identitas Terkelola

Dec 11 2020

Saya memiliki Azure SQL Db dengan kolom terenkripsi (Selalu Dienkripsi dengan Azure KeyVault). Saya dapat mengakses db ini dari SSMS dan saya dapat melihat data yang didekripsi.

Saya juga memiliki aplikasi web yang dibuat dengan .Net Core 5.0 yang disebarkan ke Azure App Service. Layanan aplikasi telah mengaktifkan Managed Identity dan Key Vault yang memiliki kunci enc / dec untuk SQL Db tersebut memiliki setelan kebijakan akses untuk mengizinkan layanan aplikasi ini mendekripsi data.

Aplikasi web berfungsi dengan identitas terkelola karena saya dapat melihat bahwa data yang tidak dienkripsi diambil tanpa masalah apa pun.

Juga, string koneksi tidak termasuk Column Encryption Setting=enabled;. Berikut string koneksi:

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

Masalahnya adalah saya tidak dapat menemukan sampel APAPUN dengan pengaturan semacam ini. Saya menemukan beberapa dan saya mengerti bahwa saya harus mendaftar SqlColumnEncryptionAzureKeyVaultProvider. Berikut kode saya untuk mendapatkan 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;
        }
    }

Kode ini tidak memberikan pengecualian apa pun, dan berfungsi untuk kueri yang tidak dienkripsi. Tetapi untuk kueri terenkripsi saya mendapatkan kesalahan berikut:

Gagal mendekripsi kunci enkripsi kolom. Nama penyedia key store tidak valid: 'AZURE_KEY_VAULT'. Nama penyedia penyimpanan kunci harus menunjukkan penyedia penyimpanan kunci sistem atau penyedia penyimpanan kunci kustom terdaftar. Nama penyedia penyimpanan kunci sistem yang valid adalah: 'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'. Nama penyedia penyimpanan kunci kustom yang valid (saat ini terdaftar) adalah:. Harap verifikasi informasi penyedia penyimpanan kunci dalam definisi kunci master kolom dalam database, dan verifikasi semua penyedia penyimpanan kunci kustom yang digunakan dalam aplikasi Anda terdaftar dengan benar. Gagal mendekripsi kunci enkripsi kolom. Nama penyedia key store tidak valid: 'AZURE_KEY_VAULT'. Nama penyedia penyimpanan kunci harus menunjukkan penyedia penyimpanan kunci sistem atau penyedia penyimpanan kunci kustom terdaftar.Nama penyedia penyimpanan kunci sistem yang valid adalah: 'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'. Nama penyedia penyimpanan kunci kustom yang valid (saat ini terdaftar) adalah:. Harap verifikasi informasi penyedia penyimpanan kunci dalam definisi kunci master kolom dalam database, dan verifikasi semua penyedia penyimpanan kunci kustom yang digunakan dalam aplikasi Anda terdaftar dengan benar.

Sepertinya penyedia brankas kunci tidak terdaftar.

Apa yang harus saya lakukan untuk membuatnya berfungsi untuk meminta data terenkripsi?

paket yang digunakan

    <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" />

Jawaban

2 alvipeo Jan 25 2021 at 23:15

Ternyata, tidak mungkin membaca data yang didekripsi di .NET 5 saat MSI digunakan. Ada bug dalam paket MS dan layanan aplikasi tidak pernah diotorisasi.

Anda harus menggunakan Service Principal. Ini bekerja seperti pesona!

Memperbarui

Saya harus berterima kasih kepada para insinyur MS yang menawarkan solusi yang berfungsi:

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);
}
SpinDoctor Apr 10 2021 at 04:19

Saya bisa menggunakan kode ini yang menggunakan menyediakan TokenCredential ke penyedia SqlColumnEncryption. DefaultAzureCredential mengembalikan identitas terkelola saat diterapkan sebagai App Service:

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

Sebut saja dari metode startup.Configure Anda.