.Net Core 5.0 - SQL Azure + Immer verschlüsselt + Verwaltete Identität
Ich habe eine Azure SQL-Datenbank mit verschlüsselten Spalten (immer mit Azure KeyVault verschlüsselt). Ich kann über SSMS auf diese Datenbank zugreifen und die entschlüsselten Daten sehen.
Ich habe auch eine Web-App mit .Net Core 5.0 erstellt, die für Azure App Service bereitgestellt wird. Für den App-Dienst ist die verwaltete Identität aktiviert, und für Key Vault mit Enc / Dec-Schlüsseln für diese SQL-Datenbank sind Zugriffsrichtlinien festgelegt, damit dieser App-Service die Daten entschlüsseln kann.
Die Web-App arbeitet mit verwalteter Identität, da ich sehe, dass nicht verschlüsselte Daten ohne Probleme abgerufen werden.
Die Verbindungszeichenfolge enthält auch Column Encryption Setting=enabled;
. Hier ist die Verbindungszeichenfolge:
Server=tcp:server.database.windows.net,1433;Database=somedb;Column Encryption Setting=enabled;
Das Problem ist, dass ich mit dieser Art von Setup KEINE Samples finden kann. Ich habe einige gefunden und verstehe, dass ich mich registrieren muss SqlColumnEncryptionAzureKeyVaultProvider
. Hier ist mein Code, um SqlConnection zu erhalten:
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;
}
}
Dieser Code löst keine Ausnahmen aus und funktioniert für unverschlüsselte Abfragen. Bei verschlüsselten Abfragen wird jedoch der folgende Fehler angezeigt:
Fehler beim Entschlüsseln eines Spaltenverschlüsselungsschlüssels. Ungültiger Name des Key Store-Anbieters: 'AZURE_KEY_VAULT'. Der Name eines Schlüsselspeicheranbieters muss entweder einen Systemschlüsselspeicheranbieter oder einen registrierten benutzerdefinierten Schlüsselspeicheranbieter bezeichnen. Gültige Anbieternamen für Systemschlüsselspeicher sind: 'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'. Gültige (derzeit registrierte) benutzerdefinierte Key Store-Anbieternamen sind :. Überprüfen Sie die Informationen zu den Schlüsselspeicheranbietern in den Spaltenhauptschlüsseldefinitionen in der Datenbank und stellen Sie sicher, dass alle in Ihrer Anwendung verwendeten benutzerdefinierten Schlüsselspeicheranbieter ordnungsgemäß registriert sind. Fehler beim Entschlüsseln eines Spaltenverschlüsselungsschlüssels. Ungültiger Name des Key Store-Anbieters: 'AZURE_KEY_VAULT'. Der Name eines Schlüsselspeicheranbieters muss entweder einen Systemschlüsselspeicheranbieter oder einen registrierten benutzerdefinierten Schlüsselspeicheranbieter bezeichnen.Gültige Anbieternamen für Systemschlüsselspeicher sind: 'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'. Gültige (derzeit registrierte) benutzerdefinierte Key Store-Anbieternamen sind :. Überprüfen Sie die Informationen zu den Schlüsselspeicheranbietern in den Spaltenhauptschlüsseldefinitionen in der Datenbank und stellen Sie sicher, dass alle in Ihrer Anwendung verwendeten benutzerdefinierten Schlüsselspeicheranbieter ordnungsgemäß registriert sind.
Es scheint, dass der Key Vault-Anbieter nicht registriert ist.
Was muss ich tun, damit verschlüsselte Daten abgefragt werden?
verwendete Pakete
<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" />
Antworten
Es stellt sich heraus, dass es nicht möglich ist, entschlüsselte Daten in .NET 5 zu lesen, wenn MSI verwendet wird. Es gibt einen Fehler in MS-Paketen und der App-Dienst wird niemals autorisiert.
Sie müssen Service Principal verwenden. Das funktioniert wie ein Zauber!
Aktualisieren
Ich muss mich bei den MS-Ingenieuren bedanken, die eine funktionierende Lösung angeboten haben:
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);
}
Ich konnte diesen Code verwenden, der ein TokenCredential für den SqlColumnEncryption-Anbieter bereitstellt. DefaultAzureCredential gibt eine verwaltete Identität zurück, wenn es als App Service bereitgestellt wird:
SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new DefaultAzureCredential());
Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>
{
{ SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider }
};
SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
Rufen Sie es von Ihrem Start aus auf. Konfigurieren Sie die Methode.