|
| 1 | +-- Always Encrypted Demo |
| 2 | + |
| 3 | +USE WideWorldImporters; |
| 4 | +GO |
| 5 | + |
| 6 | +-- WWI have decided to store some national ID and credit card details for suppliers |
| 7 | +-- but these details need to always be encrypted |
| 8 | + |
| 9 | +-- Remove any existing column keys and/or table |
| 10 | +DROP TABLE IF EXISTS Purchasing.Supplier_PrivateDetails; |
| 11 | +IF EXISTS (SELECT 1 FROM sys.column_encryption_keys WHERE name = N'WWI_ColumnEncryptionKey') |
| 12 | +BEGIN |
| 13 | + DROP COLUMN ENCRYPTION KEY WWI_ColumnEncryptionKey; |
| 14 | +END; |
| 15 | +IF EXISTS (SELECT 1 FROM sys.column_master_keys WHERE name = N'WWI_ColumnMasterKey') |
| 16 | +BEGIN |
| 17 | + DROP COLUMN MASTER KEY WWI_ColumnMasterKey; |
| 18 | +END; |
| 19 | +GO |
| 20 | + |
| 21 | +-- We need a column master key. This key is used to encrypt the column encryption keys. |
| 22 | +-- The column master key isn't really stored in the database. It's created and stored on the |
| 23 | +-- client system. SQL Server only holds a link to it so that SQL Server can tell the |
| 24 | +-- client application where to locate the master key. The client system will encrypt a column |
| 25 | +-- encryption key with this master key. |
| 26 | + |
| 27 | +-- The wizard will create a certificate, install it in the certificate store, then |
| 28 | +-- register it with SQL Server via CREATE COLUMN MASTER KEY |
| 29 | + |
| 30 | +-- 1a. In Object Explorer, expand the security node in WideWorldImporters, then expand |
| 31 | +-- the Always Encrypted Keys node and note the contents. |
| 32 | +-- 1b. Right-click the Column Master Keys node and click New Column Master Key. |
| 33 | +-- 1c. For the name, enter WWI_ColumnMasterKey. |
| 34 | +-- 1d. Note the available entries in the Key store dropdown list. Choose Windows Certificate Store - Current User. |
| 35 | +-- This will only be a temporary location for the certificate. |
| 36 | +-- 1e. Click Generate Certificate to create the new certificate. Note that an Always Encrypted certificate |
| 37 | +-- has been created. Ensure that it is selected, then click OK. |
| 38 | + |
| 39 | +-- We have used the MSSQL_CERTIFICATE_STORE which uses the Windows store |
| 40 | +-- but we can use any store that implements the SqlColumnEncryptionKeyStoreProvider |
| 41 | +-- class. (And is registered by calling the SqlConnection.RegisterColumnEncryptionKeyStoreProviders() |
| 42 | +-- method). This requires .NET framework 4.6.1 or later on the client. |
| 43 | + |
| 44 | +-- The certificate could also have been created via the makecert utility and just loaded on the client. |
| 45 | + |
| 46 | +-- We can see the newly created master key. Note the key_path. This path is relative to the client. |
| 47 | + |
| 48 | +SELECT * FROM sys.column_master_keys; |
| 49 | + |
| 50 | +-- The next key that we need is used for performing column encryption. It's held encrypted on the |
| 51 | +-- database server and is decrypted (and cached) on the client application before use. |
| 52 | +-- On the client system, it is protected by the column master key. |
| 53 | + |
| 54 | +-- 2a. In Object Explorer, right-click the Column Encryption Keys node and click New Column Encryption Key. |
| 55 | +-- 2b. In the Name textbox, enter WWI_ColumnEncryptionKey and from the Column master key dropdown list, |
| 56 | +-- select WWI_ColumnMasterKey to be used to encrypt this new key. Then click OK. |
| 57 | + |
| 58 | +-- We can see the newly created encryption key. |
| 59 | + |
| 60 | +SELECT * FROM sys.column_encryption_keys; |
| 61 | + |
| 62 | +-- Now let's create the table that will use always encrypted. |
| 63 | +-- We'll have one deterministic encryption column and two random |
| 64 | +-- encryption (salted) columns. |
| 65 | + |
| 66 | +CREATE TABLE Purchasing.Supplier_PrivateDetails |
| 67 | +( |
| 68 | + SupplierID int |
| 69 | + CONSTRAINT PKFK_Purchasing_Supplier_PrivateDetails PRIMARY KEY |
| 70 | + CONSTRAINT FK_Purchasing_Supplier_PrivateDetails_Suppliers |
| 71 | + FOREIGN KEY REFERENCES Purchasing.Suppliers (SupplierID), |
| 72 | + NationalID nvarchar(30) COLLATE Latin1_General_BIN2 |
| 73 | + ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = WWI_ColumnEncryptionKey, |
| 74 | + ENCRYPTION_TYPE = DETERMINISTIC, |
| 75 | + ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL, |
| 76 | + CreditCardNumber nvarchar(30) COLLATE Latin1_General_BIN2 |
| 77 | + ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = WWI_ColumnEncryptionKey, |
| 78 | + ENCRYPTION_TYPE = RANDOMIZED, |
| 79 | + ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL, |
| 80 | + ExpiryDate nvarchar(5) COLLATE Latin1_General_BIN2 |
| 81 | + ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = WWI_ColumnEncryptionKey, |
| 82 | + ENCRYPTION_TYPE = RANDOMIZED, |
| 83 | + ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL |
| 84 | +); |
| 85 | +GO |
| 86 | + |
| 87 | +-- Note that we can't directly insert unencrypted data |
| 88 | +-- Note the error returned. The data in the columns is only |
| 89 | +-- understood by the client system. |
| 90 | + |
| 91 | +INSERT Purchasing.Supplier_PrivateDetails |
| 92 | + (SupplierID, NationalID, CreditCardNumber, ExpiryDate) |
| 93 | +VALUES |
| 94 | + (1, N'93748567', N'7382-5849-2903-2838', N'11/19'); |
| 95 | +GO |
| 96 | + |
| 97 | +-- Let's ensure the table is empty, then we'll use a client application |
| 98 | +-- to populate the data. Note that we can still perform standard |
| 99 | +-- table operations like truncation. |
| 100 | + |
| 101 | +TRUNCATE TABLE Purchasing.Supplier_PrivateDetails; |
| 102 | +GO |
| 103 | + |
| 104 | +-- 3a. Now execute the .NET app to populate the data |
| 105 | + |
| 106 | +-- Note that it has been inserted but is not visible within the database |
| 107 | + |
| 108 | +SELECT * FROM Purchasing.Supplier_PrivateDetails ORDER BY SupplierID; |
| 109 | +GO |
| 110 | + |
| 111 | +-- To emulate a client application that has access to the keys, we |
| 112 | +-- can use SSMS to connect. Note that this can only work because |
| 113 | +-- the client happens to be the same machine as the server in our |
| 114 | +-- case. |
| 115 | + |
| 116 | +-- 4a. Open the second query window for this demonstration and follow the instructions there. |
| 117 | + |
| 118 | +-- 5a. Clean up afterwards. |
| 119 | + |
| 120 | +-- Remove any existing column keys and/or table |
| 121 | +DROP TABLE IF EXISTS Purchasing.Supplier_PrivateDetails; |
| 122 | +IF EXISTS (SELECT 1 FROM sys.column_encryption_keys WHERE name = N'WWI_ColumnEncryptionKey') |
| 123 | +BEGIN |
| 124 | + DROP COLUMN ENCRYPTION KEY WWI_ColumnEncryptionKey; |
| 125 | +END; |
| 126 | +IF EXISTS (SELECT 1 FROM sys.column_master_keys WHERE name = N'WWI_ColumnMasterKey') |
| 127 | +BEGIN |
| 128 | + DROP COLUMN MASTER KEY WWI_ColumnMasterKey; |
| 129 | +END; |
| 130 | +GO |
| 131 | + |
| 132 | + |
0 commit comments