Skip to main content

Configuring Azure SQL database to use Key Vault for AlwaysEncrypted columns

Azure SQL v12 databases have the Always Encrypted feature with ability to store cryptographic materials in Azure Key Vault.

In this Blog I'll show how to create a Key Vault and configure an Azure SQL database to use the Key Vault for Always Encrypted.

The following are the requirements to succeed:

  • Azure subscription with access to the Classic Azure Portal (CSP type subscription doesn't work at the time of the writing)
  • an Azure SQL v12 database (you can create it from PowerShell but I have it already created)
  • Resource Group (you can create it from PowerShell but I also have it already created)

Step 1 - log into Azure using the Resource Model


$cred = Get-Credential

# sign in using the ARM model
Login-AzureRmAccount -Credential $cred

# Login-AzureRmAccount returns this details:

# Environment           : <name>
# Account               : <your email>
# TenantId              : 4f..................................
# SubscriptionId        : fc..................................
# SubscriptionName      : <your subscription name>
# CurrentStorageAccount :


Step 2 - Create a new Key Vault in a resource group

# set your variables
$rg = 'rg-BVI' # resource group name
$location='East US' # loction where Key Vault to be created
$vaultName = 'bvi-poc' #vault name
$vaultKeyName = "poc-master-key" #Key name
$SubscriptionId = "fc.........................."

# Sets the context for the below cmdlets to the specified subscription.
$azureCtx = Set-AzureRMConteXt -SubscriptionId $SubscriptionId 

# create Key Vault
New-AzureRmKeyVault -VaultName $vaultName `
 -ResourceGroupName $rg -Location $location


The last cmdlet returns a number of properties but the one we need to remember is

Vault URI                        : https://bvi-poc.vault.azure.net

this value should show up in SQL master key metadata (if everything is configured propely)


Step 3 - Create your master encryption key in the Key Vault

# Grant access to the vault to your account with all permisisons

Set-AzureRmKeyVaultAccessPolicy `
-VaultName $vaultName `
-ResourceGroupName $rg `
-PermissionsToKeys get, create, delete, list, update, `
import, backup, restore, wrapKey,unwrapKey, sign, `
verify -UserPrincipalName $azureCtx.Account

# create your key (to be used as Master Encryption Key for Always Encrypted) in the vault. 
# If you need to be a FIPS compliant than change the destionation from Software to Hardware

$VaultKey = Add-AzureKeyVaultKey -VaultName $vaultName `
-Name $vaultKeyName `
-Destination "Software"

Step 4 - Configure Azure SQL DB to use the Key Vault created earlier

# Import the SqlServer module.
Import-Module "SqlServer"

$serverName = "<your server name>.database.windows.net"
$databaseName = "PoC01"  # your test DB name
$connStr = "Server = " + $serverName + "; Database = " + $databaseName + "; User ID=<user>;Password=<password>"

$connection = New-Object Microsoft.SqlServer.Management.Common.ServerConnection
$connection.ConnectionString = $connStr
$connection.Connect()
$server = New-Object Microsoft.SqlServer.Management.Smo.Server($connection)
$database = $server.Databases[$databaseName] 


# Create a SqlColumnMasterKeySettings object for your column master key. 
$cmkSettings = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyURL $VaultKey.ID

# Create column master key metadata in the database.
$cmkName = "PoC01-CMK"
New-SqlColumnMasterKey -Name $cmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings


To verify that the master encryption key metadata configured properly execute this query against your database

select * from sys.column_master_keys


it should return one row per master key and these columns must be:

name                        PoC01-CMK
key_store_provider_name     AZURE_KEY_VAULT
key_path                    https://bvi-poc.vault.azure.net/...

The key_path value should match to the value of the 'Vault URI' property returned by the last cmdlet in step 2.

Step 5- create column encryption key in the Azure database

# Authenticate to Azure 
Add-SqlAzureAuthenticationContext -Interactive
# use the same credentials as in Step 1

# Generate a column encryption key, encrypt it with the column master key and create column encryption key metadata in the database.

$cekName = "PoC01-CEK"
New-SqlColumnEncryptionKey -Name $cekName `
-InputObject $database -ColumnMasterKey $cmkName


Step 6 - validate the Key Vault configuration from SSMS

In SSMS connect to your test database and create this table

CREATE TABLE [dbo].[Person](
[PersonId] [uniqueidentifier] NOT NULL PRIMARY KEY,
[Firstname] [nvarchar](32) COLLATE  Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [PoC01-CEK],  
        ENCRYPTION_TYPE = RANDOMIZED,  
        ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'),
[Lastname] [nvarchar](32) COLLATE  Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [PoC01-CEK],  
        ENCRYPTION_TYPE = RANDOMIZED,  
        ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'),
[DateOfBirth] [datetime2](7)  ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [PoC01-CEK],  
        ENCRYPTION_TYPE = RANDOMIZED,  
        ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'),
[AdditionalInfo] [nvarchar](250) NULL
)

GO

If in SSMS you right-click on the dbo.person table and select 'Encrypt Columns' in the dialog window you should see that the Firstname, Lastname and DateOfBirth columns already marked as encrypted and the PoC01-CEK is used as a column encryption key


Step 7 - create Azure Application in the Classic Portal

For a client application to access the Key Vault a permissions need to be granted to a security principal (and a secret key needs to be used by the security principal when connecting to the Key Vault).
The security principal gets created when an application is created in Azure AD.
At the time of the writing creating an Application in the new Azure Portal has a bug (it doesn't create a security principal when registering an application in Azure AD). That's why an application need to be registered in the classic Azure Portal.

In the classic portal go to Active Directory -> Applications


and click "ADD" at the bottom of the screen. Then click 'Add an application my organization is developing'

Give the application a name on the next screen
Enter arbitrary sing-on and App ID URI (they can be changed later if needed)




When the application been registered we need to get its client ID and a secret.
Go to the Active Directory -> Applications -> TestKeyVault -> Configuration


scroll down to the client ID and the keys sections.
Copy client Id and under the keys select 1-year or 2-year duration


The secret will be displayed when the configuration is saved (and only for that moment. Do not miss it!)

Step 8 - grant the Client ID permissions on the Key Vault

Now to grant the permissions just execute this cmdlet

$clientid='e9f0f3ec-be3d-4bb0-9a29-eec6bc7cc330'  # appId

Set-AzureRmKeyVaultAccessPolicy  `
 -VaultName $vaultName `
 -ResourceGroupName $rg `
 -ServicePrincipalName `$clientid `
 -PermissionsToKeys get,wrapKey,unwrapKey,sign,verify


And now the client component needs to be developed in accordance with the patterns presented here.


Good luck!



Comments

Popular posts from this blog

SQL 2014 performance - Local disk vs. Azure Blob vs. Azure VM attached disk

Today I decided to compare SQL 2014 (RTM) performance when running a test against  - a local database (created on  WD7500BPKT - 750 GB 7200 RPM)  - a DB created on a disk attached to the A3 (3 cores, 7 GB) VM in Azure - a DB created on an Azure blob The VM in Azure was created in the US East region using the SQL 2014 template from the gallary provided by Microsoft. All databases were created with a single 10 GB data file (10 GB) and 1GB  log file. On the local SQL instance the DB was created as CREATE DATABASE TestDBonLocal ON     (NAME = file_data1, FILENAME = 'C:\TEMP\filedata1.mdf', SIZE = 10GB, FILEGROWTH = 256 MB )  LOG ON  (NAME = file_log1, FILENAME = 'C:\TEMP\filelog1.ldf', SIZE = 1GB, FILEGROWTH = 32 MB)  On the Azure VM the database on the attached disk (the disk was mapped as drive F:) was created as such CREATE DATABASE TestDBonDisk ON     (NAME = file_data1, FILENAME = 'F:\TMP\filedat...

Create 3-Node Windows 2012 Multi-subnet Cluster

Environment There are two Data centers connected via a WAN link. Two Windows 2012 Servers (called SQLDEV1 and SQLDEV2) are located in the Primary Data Center (on the IP subnet 192.168.79.0/24) and the third server is placed in the Secondary Data Center with the 192.168.69.0/24 subnet. We’ll be creating a three-node Windows cluster with no shared storage on the multi subnet network with a file share witness at the Primary Data Center. We’ll be using a file share witness to protect from the cluster failure in a situation when the network between the Data Centers is unavailable and one of the servers in the Primary Data Center is also down (or being rebooted). The final state will look like depicted above: -           Two Virtual IP’s will be assigned (192.168.76.218 and 192.168.69.134) to the cluster -           The servers at the Primary Data Center will have a vote (Vote=1) and the ...

SQL 2012 AlwaysOn: Synchronous vs. Asynchronous commit. Performance impact

Recently I've had a chance to build a 3-server AlwaysOn environment distributed between the primary and secondary data centers. The configuration looks like this: Primary Data Center                         Secondary Data Center                        SQLDEV1                                        SQLDEV3          SQLDEV2 The availability group was crated with synchronous commit replicas on SQLDEV1 and SQLDEV2 and the replica on SQLDEV3 was configured for asynchronous commit. The link between the data centers was not great and when I pinged SQLDEV3 from SQLDEV1 I got these results Approximate round trip times in milli-seconds:     Minimum = 39ms, Maximum = 63ms, Average = 42ms I also created a very simp...