16

I'm trying to use the Always Encrypted feature of SQL Server 2016 with .NET Core and seems like it can not be used (yet). Trying to import the Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider from Nuget, I get an error stating it is not compatible:

Package Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider 1.0.201501028 is not compatible with netstandard1.6 (.NETStandard,Version=v1.6)

Any ideas on how/where to get a compatible version?

TT.
  • 15,774
  • 6
  • 47
  • 88
Los Morales
  • 2,061
  • 7
  • 26
  • 42

4 Answers4

17

Always Encrypted is now supported in .Net Core 3.1 LTS.

You have to use the Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider nuget package

    Install-Package Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider -Version 1.1.1

Make sure you have a Keyvault setup.

FOR DEBUGGING your account in VS has to have sufficent rights to access the keyvault. (When published the app itself has to have sufficent rights : see https://learn.microsoft.com/en-us/azure/key-vault/managed-identity) Get and List permissions alone might not be sufficient.

Then in program.cs :

using Microsoft.AspNetCore.Hosting;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
using Microsoft.Extensions.Hosting;
//namespaces etc omitted
 public static IHostBuilder CreateHostBuilder(string[] args) =>
     Host.CreateDefaultBuilder(args)
         .ConfigureAppConfiguration((context, config) =>
         {
             var keyVaultEndpoint = GetKeyVaultEndpoint();
             if (!string.IsNullOrEmpty(keyVaultEndpoint))
             {
                 var azureServiceTokenProvider = new AzureServiceTokenProvider();
                 var keyVaultClient = new KeyVaultClient(
                     new KeyVaultClient.AuthenticationCallback(
                         azureServiceTokenProvider.KeyVaultTokenCallback));
                 config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
                 SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new KeyVaultClient.AuthenticationCallback(
                         azureServiceTokenProvider.KeyVaultTokenCallback));
                 SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders: new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
                 {
                     { 
                         SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, sqlColumnEncryptionAzureKeyVaultProvider
                     }
                 });                     
             }
         })
         .ConfigureWebHostDefaults(webBuilder =>
         {
             webBuilder.UseStartup<Startup>();
         });
    private static string GetKeyVaultEndpoint() => "https://YOURKEYVAULT.vault.azure.net/";
}

In StartUp.cs ConfigureServices:

using Microsoft.Data.SqlClient;
//Code omitted
services.AddDbContext<EnitiesModel>(options =>
            options.UseSqlServer(new SqlConnection(Configuration.GetConnectionString("EntitiesModel"))));

Make sure your connectionstring contains the Column Encryption Setting=Enabled parameter:

 "ConnectionStrings": {
"EntitiesModel": "Server=SOMESERVER.database.windows.net;Database=SOMEDB;Trusted_Connection=False;Encrypt=True;Integrated Security=False;
MultipleActiveResultSets=true;persist security info=True;user id=SOMEDBACCOUNT;password=SOMEPASSWORD;
Column Encryption Setting=enabled;"   
  }

Small gotcha : If you used DB scaffolding make sure the Model connectionstring has the Column Encryption Setting aswell! (if you did not change it, it is standard inside the DBModel class after scaffolding with a VS warning)

This should get you up and running...

Tim Bijnens
  • 310
  • 2
  • 11
  • Do you know if it's possible to use this with a key vault with an app registration? I'm developing an app that will be deployed in IIS not Azure. So I don't have an app service. But I am using keyvaults to store connection string and tokens. So I have an app registration in Azure which has access to the key vault. The app registration doesn't support MSI so I can't use your method. But I also can't find how I'm supposed to get it working in my situation either. – Mat H. May 08 '20 at 19:44
  • Maybe via an app configuration? https://learn.microsoft.com/en-us/azure/azure-app-configuration/use-key-vault-references-dotnet-core?tabs=cmd%2Ccore2x – Tim Bijnens May 10 '20 at 22:05
  • Thanks for the suggestion. I was able to get it working by setting up the SqlColumnEncryptionAzureKeyVaultProvider to get a token by using the app registration's client id and secret with the ActiveDirectory ClientCredential class. – Mat H. May 11 '20 at 12:27
  • 1
    I've read through dozens of pages of documentation and examples trying to get azure based column encryption to work with EF. This is maybe the only example that does all three. +1. – Vok May 14 '20 at 07:52
  • One thing to add: Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider is NOT the same as Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider Most outdated documentation is using the .SqlServer namespace which was half my problem. – Vok May 14 '20 at 07:53
  • Thank you. Been trying to get it working for days. You saved me. – Francis Jul 08 '20 at 12:32
  • Can always encrypted with ef core work without AzureKeyVaultProvider ? But with windows Certificates ? – I.Boras Aug 22 '21 at 10:38
14

Disclaimer: I am a Program Manager at Microsoft

Always Encrypted is currently not supported in .NET Core. It is on our roadmap, we don't have a timeline for it yet.

This is now supported. See answers below.

silent
  • 14,494
  • 4
  • 46
  • 86
  • For those interested, similar Q/A here: http://stackoverflow.com/questions/41175489/column-encryption-in-asp-mvc-app-with-sql-server-2016-using-net-core-ef-core – Los Morales Jan 06 '17 at 17:22
  • 5
    @Jakub Szymaszek - Microsoft What about now , is there any new updates regarding this issue? – Ahmed Elbatt May 14 '18 at 10:48
  • 1
    What about now? – Stuart Dobson Oct 03 '19 at 22:01
  • any update for this issue ? I think this is major issue for security that MS left behind when catching up update for framework and it does't well organize with other product. – Daleman Nov 06 '19 at 03:54
  • Any update after Mocrosoft.Data.SqlClient 1.1 release ? (https://www.youtube.com/watch?v=zhzRzRBDTyE) – Daleman Dec 06 '19 at 11:19
6

It's now supported in .NET Core 3.0 Preview 5, which provides a new SqlClient supporting Always Encrypted and more. See this comment for more info.

For the Key Vault provider, you need to use Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider instead.

Kexy Biscuit
  • 390
  • 3
  • 11
  • That isn't the Key Vault provider though, but I think support in SqlClient for column encryption does enable them to do the key vault provider work. – Ian1971 Oct 15 '19 at 08:17
  • Key Vault provider is also updated 2 months ago: https://www.nuget.org/packages/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider supports .NET Core 2.1 and later. – Kexy Biscuit Jan 14 '20 at 02:10
0

A variant of the Program.cs from Tim's answer above, but for apps registered with Azure App Registration:

namespace Sample
{
   public class Program
   {
       public static void Main(string[] args)
       {
           CreateHostBuilder(args).Build().Run();
       }

       public static IHostBuilder CreateHostBuilder(string[] args) =>
     Host.CreateDefaultBuilder(args)
         .ConfigureAppConfiguration((context, config) =>
         {
             var keyVaultEndpoint = GetKeyVaultEndpoint();
             if (!string.IsNullOrEmpty(keyVaultEndpoint))
             {
                 var azureServiceTokenProvider = new AzureServiceTokenProvider(keyVaultEndpoint);
                 var keyVaultClient = new KeyVaultClient(
                     new KeyVaultClient.AuthenticationCallback(
                         azureServiceTokenProvider.KeyVaultTokenCallback));
                 
                 SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new KeyVaultClient.AuthenticationCallback(
                         azureServiceTokenProvider.KeyVaultTokenCallback));
                 SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders: new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
                 {
                     {
                         SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, sqlColumnEncryptionAzureKeyVaultProvider
                     }
                 });
             }
         })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

       private static string GetKeyVaultEndpoint() => "RunAs=App;AppId=<app ID>;TenantId=<tenant ID>.onmicrosoft.com;AppKey=<app secret>";
   }
 }