Een Always Encrypted configureren met behulp van Azure Key Vault

VAN TOEPASSING OP: Azure SQL Database Azure SQL Managed Instance

In dit artikel wordt beschreven hoe u gevoelige gegevens in een database in Azure SQL Database beveiligt met gegevensversleuteling met behulp van de wizard Always Encrypted in SQL Server Management Studio (SSMS). Het bevat ook instructies voor het opslaan van elke versleutelingssleutel in Azure Key Vault.

Always Encrypted is een technologie voor gegevensversleuteling waarmee gevoelige data-at-rest op de server, tijdens de verplaatsing tussen client en server en terwijl de gegevens in gebruik zijn, worden beschermd. Always Encrypted zorgt ervoor dat gevoelige gegevens nooit als tekst zonder tekst in het databasesysteem worden weergegeven. Nadat u gegevensversleuteling hebt geconfigureerd, hebben alleen clienttoepassingen of app-servers die toegang hebben tot de sleutels toegang tot niet-versleutelde gegevens. Zie Always Encrypted (database-engine)voor gedetailleerde informatie.

Nadat u de database hebt geconfigureerd voor Always Encrypted, maakt u een clienttoepassing in C# met Visual Studio om te werken met de versleutelde gegevens.

Volg de stappen in dit artikel en leer hoe u een Always Encrypted database kunt instellen in Azure SQL Database of SQL Managed Instance. In dit artikel leert u hoe u de volgende taken uitvoert:

Vereisten

Toegang tot clienttoepassing inschakelen

U moet uw clienttoepassing toegang geven tot uw database in SQL Database door een Azure Active Directory-toepassing (Azure AD) in te stellen en de toepassings-id en sleutel te kopiëren die u nodig hebt om uw toepassing te verifiëren.

Als u de toepassings-id en -sleutel wilt op halen, volgt u de stappen in Een toepassing Azure Active Directory service-principalmaken die toegang hebben tot resources.

Een sleutelkluis maken om uw sleutels op te slaan

Nu uw client-app is geconfigureerd en u uw toepassings-id hebt, is het tijd om een sleutelkluis te maken en het toegangsbeleid te configureren, zodat u en uw toepassing toegang hebben tot de geheimen van de kluis (de Always Encrypted-sleutels). De machtigingen create , get, list, sign, verify, wrapKey en unwrapKey zijn vereist voor het maken van een nieuwe kolommastersleutel en voor het instellen van versleuteling met SQL Server Management Studio.

U kunt snel een sleutelkluis maken door het volgende script uit te voeren. Zie Wat is Azure Key Vault? voor een gedetailleerde uitleg van deze opdrachten en meer informatie over het maken en configureren van een sleutelkluis.

Belangrijk

De Module PowerShell Azure Resource Manager (RM) wordt nog steeds ondersteund door Azure SQL Database, maar alle toekomstige ontwikkeling is voor de Az.Sql-module. De AzureRM-module blijft tot ten minste december 2020 bugfixes ontvangen. De argumenten voor de opdrachten in de Az-module en in de AzureRm-modules zijn vrijwel identiek. Zie Introductie van de nieuwe Az-module van Azure PowerShell voor meer informatie over de compatibiliteit van de argumenten.

$subscriptionName = '<subscriptionName>'
$userPrincipalName = '<username@domain.com>'
$applicationId = '<applicationId from AAD application>'
$resourceGroupName = '<resourceGroupName>' # use the same resource group name when creating your SQL Database below
$location = '<datacenterLocation>'
$vaultName = '<vaultName>'

Connect-AzAccount
$subscriptionId = (Get-AzSubscription -SubscriptionName $subscriptionName).Id
Set-AzContext -SubscriptionId $subscriptionId

New-AzResourceGroup -Name $resourceGroupName -Location $location
New-AzKeyVault -VaultName $vaultName -ResourceGroupName $resourceGroupName -Location $location

Set-AzKeyVaultAccessPolicy -VaultName $vaultName -ResourceGroupName $resourceGroupName -PermissionsToKeys create,get,wrapKey,unwrapKey,sign,verify,list -UserPrincipalName $userPrincipalName
Set-AzKeyVaultAccessPolicy  -VaultName $vaultName  -ResourceGroupName $resourceGroupName -ServicePrincipalName $applicationId -PermissionsToKeys get,wrapKey,unwrapKey,sign,verify,list

Verbinden met SSMS

Open SQL Server Management Studio (SSMS) en maak verbinding met de server of beheerd met uw database.

  1. Open SQL Server Management Studio. (Ga naar Verbinding maken > Database-engine om het venster Verbinding maken met server te openen als deze niet is geopend.)

  2. Voer uw servernaam of exemplaarnaam en referenties in.

    De verbindingsreeks kopiëren

Als het venster Nieuwe firewallregel wordt geopend, meldt u zich aan bij Azure en laat SSMS een nieuwe firewallregel voor u maken.

Een tabel maken

In deze sectie maakt u een tabel voor het opslaan van patiëntgegevens. Deze is in eerste instantie niet versleuteld. U configureert versleuteling in de volgende sectie.

  1. Vouw Databases uit.
  2. Klik met de rechtermuisknop op de database en klik op Nieuwe query.
  3. Plak de volgende Transact-SQL (T-SQL) in het nieuwe queryvenster en voer het uit.
CREATE TABLE [dbo].[Patients](
         [PatientId] [int] IDENTITY(1,1),
         [SSN] [char](11) NOT NULL,
         [FirstName] [nvarchar](50) NULL,
         [LastName] [nvarchar](50) NULL,
         [MiddleName] [nvarchar](50) NULL,
         [StreetAddress] [nvarchar](50) NULL,
         [City] [nvarchar](50) NULL,
         [ZipCode] [char](5) NULL,
         [State] [char](2) NULL,
         [BirthDate] [date] NOT NULL
         PRIMARY KEY CLUSTERED ([PatientId] ASC) ON [PRIMARY] );
GO

Kolommen versleutelen (Always Encrypted)

SSMS biedt een wizard waarmee u eenvoudig een Always Encrypted configureren door de kolomsleutel, de kolomversleutelingssleutel en versleutelde kolommen voor u in te stellen.

  1. Vouw Databases > Clinic-tabellen > uit.

  2. Klik met de rechtermuisknop op de tabel Patiënten en selecteer Kolommen versleutelen om de wizard Always Encrypted openen:

    Schermopname met de knop Kolommen versleutelen... menuoptie.

De Always Encrypted bevat de volgende secties: Kolomselectie, Hoofdsleutelconfiguratie, Validatie en Samenvatting.

Kolomselectie

Klik op Volgende op de pagina Inleiding om de pagina Kolomselectie te openen. Op deze pagina selecteert u welke kolommen u wilt versleutelen, het type versleuteling en welke kolomversleutelingssleutel (CEK) u wilt gebruiken.

Versleutel SSN- en BirthDate-gegevens voor elke patiënt. De SSN-kolom maakt gebruik van deterministische versleuteling, die ondersteuning biedt voor gelijkheidszoekingen, joins en groep op. De kolom BirthDate maakt gebruik van willekeurige versleuteling, die geen ondersteuning biedt voor bewerkingen.

Stel het versleutelingstype voor de kolom SSN in op Deterministisch en de kolom BirthDate op Gerandomiseerd. Klik op Volgende.

Kolommen versleutelen

Hoofdsleutelconfiguratie

Op de pagina Hoofdsleutelconfiguratie stelt u uw CMK in en selecteert u de sleutelopslagprovider waar de CMK wordt opgeslagen. Op dit moment kunt u een CMK opslaan in het Windows-certificaatopslag, Azure Key Vault of een HSM (Hardware Security Module).

In deze zelfstudie leert u hoe u uw sleutels in uw Azure Key Vault.

  1. Selecteer Azure Key Vault.
  2. Selecteer de gewenste sleutelkluis in de vervolgkeuzelijst.
  3. Klik op Volgende.

Hoofdsleutelconfiguratie

Validatie

U kunt de kolommen nu versleutelen of een PowerShell-script opslaan om later uit te voeren. Voor deze zelfstudie selecteert u Doorgaan om nu te voltooien en klikt u op Volgende.

Samenvatting

Controleer of de instellingen juist zijn en klik op Voltooien om de installatie voor de Always Encrypted.

Schermopname van de resultatenpagina met taken die zijn gemarkeerd als geslaagd.

De acties van de wizard controleren

Nadat de wizard is voltooid, wordt uw database ingesteld voor Always Encrypted. De wizard heeft de volgende acties uitgevoerd:

  • U hebt een kolommastersleutel gemaakt en deze opgeslagen in Azure Key Vault.
  • U hebt een kolomversleutelingssleutel gemaakt en deze opgeslagen in Azure Key Vault.
  • De geselecteerde kolommen voor versleuteling geconfigureerd. De tabel Patiënten heeft momenteel geen gegevens, maar bestaande gegevens in de geselecteerde kolommen zijn nu versleuteld.

U kunt het maken van de sleutels in SSMS controleren door Clinic > Security Always Encrypted uit te > breiden.

Een clienttoepassing maken die werkt met de versleutelde gegevens

Nu Always Encrypted is ingesteld, kunt u een toepassing bouwen die invoegingen en selecties uitvoert op de versleutelde kolommen.

Belangrijk

Uw toepassing moet SqlParameter-objecten gebruiken bij het doorgeven van gegevens in platte tekst aan de server met Always Encrypted kolommen. Het doorgeven van letterlijke waarden zonder SqlParameter-objecten leidt tot een uitzondering.

  1. Open Visual Studio en maak een nieuwe C#-consoletoepassing (Visual Studio 2015 en eerder) of console-app (.NET Framework) (Visual Studio 2017 en hoger). Zorg ervoor dat uw project is ingesteld op .NET Framework 4.6 of hoger.
  2. Noem het project AlwaysEncryptedConsoleAKVApp en klik op OK.
  3. Installeer de volgende NuGet-pakketten door naar Tools > NuGet Pakketbeheer > Pakketbeheer Console te gaan.

Voer deze twee regels code uit in Pakketbeheer Console:

Install-Package Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

Wijzig uw connection string om deze in te Always Encrypted

In deze sectie wordt uitgelegd hoe u Always Encrypted in uw database kunt connection string.

Als u Always Encrypted wilt inschakelen, moet u het trefwoord Kolomversleutelingsinstelling toevoegen aan uw connection string instellen op Ingeschakeld.

U kunt dit rechtstreeks in de connection string of u kunt deze instellen met behulp van SqlConnectionStringBuilder. In de voorbeeldtoepassing in de volgende sectie ziet u hoe u SqlConnectionStringBuilder gebruikt.

Schakel Always Encrypted in de connection string

Voeg het volgende trefwoord toe aan uw connection string.

Column Encryption Setting=Enabled

Schakel Always Encrypted sqlConnectionStringBuilder in

De volgende code laat zien hoe u Always Encrypted inschakelen door SqlConnectionStringBuilder.ColumnEncryptionSetting in te stellen op Ingeschakeld.

// Instantiate a SqlConnectionStringBuilder.
SqlConnectionStringBuilder connStringBuilder = new SqlConnectionStringBuilder("replace with your connection string");

// Enable Always Encrypted.
connStringBuilder.ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Enabled;

De Azure Key Vault registreren

De volgende code laat zien hoe u de provider Azure Key Vault registreren bij het ADO.NET stuurprogramma.

private static ClientCredential _clientCredential;

static void InitializeAzureKeyVaultProvider() {
    _clientCredential = new ClientCredential(applicationId, clientKey);

    SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(GetToken);

    Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();

    providers.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);
    SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
}

Always Encrypted consoletoepassing maken

In dit voorbeeld wordt het volgende gedemonstreerd:

  • Wijzig uw connection string om deze in Always Encrypted.
  • Registreer Azure Key Vault als sleutelopslagprovider van de toepassing.
  • Voeg gegevens in de versleutelde kolommen in.
  • Selecteer een record door te filteren op een specifieke waarde in een versleutelde kolom.

Vervang de inhoud van Program.cs door de volgende code. Vervang de connection string voor de globale connectionString-variabele in de regel die direct voorafgaat aan de methode Main door uw geldige connection string van de Azure Portal. Dit is de enige wijziging die u moet maken in deze code.

Voer de app uit om de Always Encrypted in actie te zien.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider;

namespace AlwaysEncryptedConsoleAKVApp {
    class Program {
        // Update this line with your Clinic database connection string from the Azure portal.
        static string connectionString = @"<connection string from the portal>";
        static string applicationId = @"<application ID from your AAD application>";
        static string clientKey = "<key from your AAD application>";

        static void Main(string[] args) {
            InitializeAzureKeyVaultProvider();

            Console.WriteLine("Signed in as: " + _clientCredential.ClientId);

            Console.WriteLine("Original connection string copied from the Azure portal:");
            Console.WriteLine(connectionString);

            // Create a SqlConnectionStringBuilder.
            SqlConnectionStringBuilder connStringBuilder =
                new SqlConnectionStringBuilder(connectionString);

            // Enable Always Encrypted for the connection.
            // This is the only change specific to Always Encrypted
            connStringBuilder.ColumnEncryptionSetting =
                SqlConnectionColumnEncryptionSetting.Enabled;

            Console.WriteLine(Environment.NewLine + "Updated connection string with Always Encrypted enabled:");
            Console.WriteLine(connStringBuilder.ConnectionString);

            // Update the connection string with a password supplied at runtime.
            Console.WriteLine(Environment.NewLine + "Enter server password:");
            connStringBuilder.Password = Console.ReadLine();

            // Assign the updated connection string to our global variable.
            connectionString = connStringBuilder.ConnectionString;

            // Delete all records to restart this demo app.
            ResetPatientsTable();

            // Add sample data to the Patients table.
            Console.Write(Environment.NewLine + "Adding sample patient data to the database...");

            InsertPatient(new Patient() {
                SSN = "999-99-0001",
                FirstName = "Orlando",
                LastName = "Gee",
                BirthDate = DateTime.Parse("01/04/1964")
            });
            InsertPatient(new Patient() {
                SSN = "999-99-0002",
                FirstName = "Keith",
                LastName = "Harris",
                BirthDate = DateTime.Parse("06/20/1977")
            });
            InsertPatient(new Patient() {
                SSN = "999-99-0003",
                FirstName = "Donna",
                LastName = "Carreras",
                BirthDate = DateTime.Parse("02/09/1973")
            });
            InsertPatient(new Patient() {
                SSN = "999-99-0004",
                FirstName = "Janet",
                LastName = "Gates",
                BirthDate = DateTime.Parse("08/31/1985")
            });
            InsertPatient(new Patient() {
                SSN = "999-99-0005",
                FirstName = "Lucy",
                LastName = "Harrington",
                BirthDate = DateTime.Parse("05/06/1993")
            });

            // Fetch and display all patients.
            Console.WriteLine(Environment.NewLine + "All the records currently in the Patients table:");

            foreach (Patient patient in SelectAllPatients()) {
                Console.WriteLine(patient.FirstName + " " + patient.LastName + "\tSSN: " + patient.SSN + "\tBirthdate: " + patient.BirthDate);
            }

            // Get patients by SSN.
            Console.WriteLine(Environment.NewLine + "Now lets locate records by searching the encrypted SSN column.");

            string ssn;

            // This very simple validation only checks that the user entered 11 characters.
            // In production be sure to check all user input and use the best validation for your specific application.
            do {
                Console.WriteLine("Please enter a valid SSN (ex. 999-99-0003):");
                ssn = Console.ReadLine();
            } while (ssn.Length != 11);

            // The example allows duplicate SSN entries so we will return all records
            // that match the provided value and store the results in selectedPatients.
            Patient selectedPatient = SelectPatientBySSN(ssn);

            // Check if any records were returned and display our query results.
            if (selectedPatient != null) {
                Console.WriteLine("Patient found with SSN = " + ssn);
                Console.WriteLine(selectedPatient.FirstName + " " + selectedPatient.LastName + "\tSSN: "
                    + selectedPatient.SSN + "\tBirthdate: " + selectedPatient.BirthDate);
            }
            else {
                Console.WriteLine("No patients found with SSN = " + ssn);
            }

            Console.WriteLine("Press Enter to exit...");
            Console.ReadLine();
        }

        private static ClientCredential _clientCredential;

        static void InitializeAzureKeyVaultProvider() {
            _clientCredential = new ClientCredential(applicationId, clientKey);

            SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider =
              new SqlColumnEncryptionAzureKeyVaultProvider(GetToken);

            Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers =
              new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();

            providers.Add(SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider);
            SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
        }

        public async static Task<string> GetToken(string authority, string resource, string scope) {
            var authContext = new AuthenticationContext(authority);
            AuthenticationResult result = await authContext.AcquireTokenAsync(resource, _clientCredential);

            if (result == null)
                throw new InvalidOperationException("Failed to obtain the access token");
            return result.AccessToken;
        }

        static int InsertPatient(Patient newPatient) {
            int returnValue = 0;

            string sqlCmdText = @"INSERT INTO [dbo].[Patients] ([SSN], [FirstName], [LastName], [BirthDate])
     VALUES (@SSN, @FirstName, @LastName, @BirthDate);";

            SqlCommand sqlCmd = new SqlCommand(sqlCmdText);

            SqlParameter paramSSN = new SqlParameter(@"@SSN", newPatient.SSN);
            paramSSN.DbType = DbType.AnsiStringFixedLength;
            paramSSN.Direction = ParameterDirection.Input;
            paramSSN.Size = 11;

            SqlParameter paramFirstName = new SqlParameter(@"@FirstName", newPatient.FirstName);
            paramFirstName.DbType = DbType.String;
            paramFirstName.Direction = ParameterDirection.Input;

            SqlParameter paramLastName = new SqlParameter(@"@LastName", newPatient.LastName);
            paramLastName.DbType = DbType.String;
            paramLastName.Direction = ParameterDirection.Input;

            SqlParameter paramBirthDate = new SqlParameter(@"@BirthDate", newPatient.BirthDate);
            paramBirthDate.SqlDbType = SqlDbType.Date;
            paramBirthDate.Direction = ParameterDirection.Input;

            sqlCmd.Parameters.Add(paramSSN);
            sqlCmd.Parameters.Add(paramFirstName);
            sqlCmd.Parameters.Add(paramLastName);
            sqlCmd.Parameters.Add(paramBirthDate);

            using (sqlCmd.Connection = new SqlConnection(connectionString)) {
                try {
                    sqlCmd.Connection.Open();
                    sqlCmd.ExecuteNonQuery();
                }
                catch (Exception ex) {
                    returnValue = 1;
                    Console.WriteLine("The following error was encountered: ");
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(Environment.NewLine + "Press Enter key to exit");
                    Console.ReadLine();
                    Environment.Exit(0);
                }
            }
            return returnValue;
        }


        static List<Patient> SelectAllPatients() {
            List<Patient> patients = new List<Patient>();

            SqlCommand sqlCmd = new SqlCommand(
              "SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients]",
                new SqlConnection(connectionString));

            using (sqlCmd.Connection = new SqlConnection(connectionString))

            using (sqlCmd.Connection = new SqlConnection(connectionString)) {
                try {
                    sqlCmd.Connection.Open();
                    SqlDataReader reader = sqlCmd.ExecuteReader();

                    if (reader.HasRows) {
                        while (reader.Read()) {
                            patients.Add(new Patient() {
                                SSN = reader[0].ToString(),
                                FirstName = reader[1].ToString(),
                                LastName = reader["LastName"].ToString(),
                                BirthDate = (DateTime)reader["BirthDate"]
                            });
                        }
                    }
                }
                catch (Exception ex) {
                    throw;
                }
            }

            return patients;
        }

        static Patient SelectPatientBySSN(string ssn) {
            Patient patient = new Patient();

            SqlCommand sqlCmd = new SqlCommand(
                "SELECT [SSN], [FirstName], [LastName], [BirthDate] FROM [dbo].[Patients] WHERE [SSN]=@SSN",
                new SqlConnection(connectionString));

            SqlParameter paramSSN = new SqlParameter(@"@SSN", ssn);
            paramSSN.DbType = DbType.AnsiStringFixedLength;
            paramSSN.Direction = ParameterDirection.Input;
            paramSSN.Size = 11;

            sqlCmd.Parameters.Add(paramSSN);

            using (sqlCmd.Connection = new SqlConnection(connectionString)) {
                try {
                    sqlCmd.Connection.Open();
                    SqlDataReader reader = sqlCmd.ExecuteReader();

                    if (reader.HasRows) {
                        while (reader.Read()) {
                            patient = new Patient() {
                                SSN = reader[0].ToString(),
                                FirstName = reader[1].ToString(),
                                LastName = reader["LastName"].ToString(),
                                BirthDate = (DateTime)reader["BirthDate"]
                            };
                        }
                    }
                    else {
                        patient = null;
                    }
                }
                catch (Exception ex) {
                    throw;
                }
            }
            return patient;
        }

        // This method simply deletes all records in the Patients table to reset our demo.
        static int ResetPatientsTable() {
            int returnValue = 0;

            SqlCommand sqlCmd = new SqlCommand("DELETE FROM Patients");
            using (sqlCmd.Connection = new SqlConnection(connectionString)) {
                try {
                    sqlCmd.Connection.Open();
                    sqlCmd.ExecuteNonQuery();

                }
                catch (Exception ex) {
                    returnValue = 1;
                }
            }
            return returnValue;
        }
    }

    class Patient {
        public string SSN { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
    }
}

Controleren of de gegevens zijn versleuteld

U kunt snel controleren of de werkelijke gegevens op de server zijn versleuteld door een query uit te voeren op de gegevens van patiënten met SSMS (met behulp van uw huidige verbinding waarbij de instelling voor kolomversleuteling nog niet is ingeschakeld).

Voer de volgende query uit op de database Clinic.

SELECT FirstName, LastName, SSN, BirthDate FROM Patients;

U kunt zien dat de versleutelde kolommen geen niet-versleutelde gegevens bevatten.

Schermopname die laat zien dat de versleutelde kolommen geen gegevens zonder tekst bevatten.

Als u SSMS wilt gebruiken voor toegang tot de ongecodeerde gegevens, moet u eerst controleren of de gebruiker de juiste machtigingen heeft voor de Azure Key Vault: get, unwrapKey en verify. Zie Kolommastersleutels maken en opslaan (Always Encrypted)voor gedetailleerde informatie.

Voeg vervolgens de parameter Column Encryption Setting=enabled toe tijdens uw verbinding.

  1. Klik in SSMS met de rechtermuisknop op uw server in Objectverkenner kies Verbinding verbreken.

  2. Klik op > Database-engine verbinden om het venster Verbinding maken met server te openen en klik op Opties.

  3. Klik op Extra verbindingsparameters en typ Instelling voor kolomversleuteling =ingeschakeld.

    Schermopname van het tabblad Extra correctieparameters.

  4. Voer de volgende query uit op de database Clinic.

    SELECT FirstName, LastName, SSN, BirthDate FROM Patients;
    

    U kunt nu de niet-versleutelde gegevens in de versleutelde kolommen zien.

    Nieuwe consoletoepassing

Volgende stappen

Nadat uw database is geconfigureerd voor het gebruik van Always Encrypted, kunt u het volgende doen: