Skip to content
This repository was archived by the owner on Aug 7, 2020. It is now read-only.

Commit b274523

Browse files
authored
Encrypt the columns (#28)
* Encrypt the columns in CI/CD * Include a PFX backup until I can fix #30 * Use an encrypted secret for the PFX password
1 parent 816fd82 commit b274523

File tree

6 files changed

+82
-30
lines changed

6 files changed

+82
-30
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,7 @@ MigrationBackup/
350350
# End of https://www.gitignore.io/api/visualstudio
351351

352352
#Docker stuff
353-
volumes/
353+
volumes/
354+
355+
# Certificates exported by New-EncryptionKeys
356+
*.cer

AlwaysEncryptedSampleCMK.pfx

2.57 KB
Binary file not shown.

Invoke-EncryptColumns.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ param(
1212
[string] $AppColumnKeyName = "AppColumnsKey",
1313
[string] $LogColumnKeyName = "LogColumnsKey",
1414
[switch] $Script,
15-
[string] $LogFileDirectory = "$pwd"
15+
[string] $LogFileDirectory = $pwd
1616

1717
)
1818

New-EncryptionKeys.ps1

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ param(
77
[Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string] $ConnectionString,
88
[string] $MasterKeyDNSName = "CN=Always Encrypted Sample Cert",
99
[switch] $RemoveExistingCerts,
10+
[switch] $ExportCertificate,
11+
[switch] $ExportCertificateKeys,
1012
[string] $MasterKeySQLName = "AlwaysEncryptedSampleCMK",
1113
[string] $AuthColumnKeyName = "AuthColumnsKey",
1214
[string] $AppColumnKeyName = "AppColumnsKey",
@@ -26,34 +28,63 @@ catch {
2628
if ($RemoveExistingCerts) {
2729
Write-Verbose "Removing All Existing Certificates Named $($MasterKeyDNSName)"
2830
$existingColumns = Get-SqlColumnEncryptionKey -InputObject $smoDatabase
29-
$existingColumns | %{
31+
$existingColumns | ForEach-Object {
3032
Remove-SqlColumnEncryptionKey -Name $_.Name -InputObject $smoDatabase
3133
}
3234
Remove-SqlColumnMasterKey -Name $MasterKeySQLName -InputObject $smoDatabase
3335
Get-ChildItem Cert:\CurrentUser\My | Where-Object subject -eq $MasterKeyDNSName | Remove-Item
3436
}
3537

36-
$cert = New-SelfSignedCertificate `
37-
-Subject $MasterKeyDNSName `
38-
-CertStoreLocation Cert:\CurrentUser\My `
39-
-KeyExportPolicy Exportable `
40-
-Type DocumentEncryptionCert `
41-
-KeyUsage DataEncipherment `
42-
-KeySpec KeyExchange
43-
$cmkPath = "CurrentUser/My/$($cert.ThumbPrint)"
44-
Write-Verbose "Certificate Master Key Path: $($cmkPath)"
45-
46-
# Create a SqlColumnMasterKeySettings object for your column master key.
47-
$cmkSettings = New-SqlCertificateStoreColumnMasterKeySettings `
48-
-CertificateStoreLocation "CurrentUser" `
49-
-Thumbprint $cert.Thumbprint
50-
51-
New-SqlColumnMasterKey -Name $MasterKeySQLName -InputObject $smoDatabase -ColumnMasterKeySettings $cmkSettings | Out-Null
52-
53-
@($AuthColumnKeyName, $AppColumnKeyName, $LogColumnKeyName) | %{
54-
New-SqlColumnEncryptionKey `
55-
-InputObject $smoDatabase `
56-
-ColumnMasterKey $MasterKeySQLName `
57-
-Name $_ | Out-Null
38+
$Cert = (Get-ChildItem Cert:\CurrentUser\My | Where-Object subject -eq 'CN=Always Encrypted Sample Cert') | Select-Object Thumbprint -First 1
39+
if ($Cert) {
40+
Write-Verbose "Certificate `"$($MasterKeyDNSName)`" Already exists"
41+
}
42+
else {
43+
Write-Host "Creating Self Signed Certificate `"$($MasterKeyDNSName)`""
44+
$Cert = New-SelfSignedCertificate `
45+
-Subject $MasterKeyDNSName `
46+
-CertStoreLocation Cert:\CurrentUser\My `
47+
-KeyExportPolicy Exportable `
48+
-Type DocumentEncryptionCert `
49+
-KeyUsage DataEncipherment `
50+
-KeySpec KeyExchange
51+
= "CurrentUser/My/$($cert.ThumbPrint)"
52+
Write-Verbose "Certificate Master Key Path: $($cmkPath)"
53+
}
54+
55+
if ($ExportCertificate) {
56+
Get-ChildItem Cert:\CurrentUser\My |
57+
Where-Object subject -eq "CN=Always Encrypted Sample Cert" |
58+
Export-Certificate -FilePath "$($MasterKeySQLName).cer" | Out-Null
59+
}
60+
61+
if ($ExportCertificateKeys) {
62+
Get-ChildItem Cert:\CurrentUser\My |
63+
Where-Object subject -eq "CN=Always Encrypted Sample Cert" |
64+
Export-PfxCertificate -FilePath "$($MasterKeySQLName).pfx" -Password (ConvertTo-SecureString -String "1234" -Force -AsPlainText) | Out-Null
65+
}
66+
67+
if($smoDatabase.ColumnMasterKeys['AlwaysEncryptedSampleCMK']) {
68+
Write-Warning "Master Key Reference $($MasterKeySQLName) already exists in the database."
69+
}
70+
else {
71+
# Create a SqlColumnMasterKeySettings object for your column master key.
72+
$cmkSettings = New-SqlCertificateStoreColumnMasterKeySettings `
73+
-CertificateStoreLocation "CurrentUser" `
74+
-Thumbprint $Cert.Thumbprint
75+
76+
New-SqlColumnMasterKey -Name $MasterKeySQLName -InputObject $smoDatabase -ColumnMasterKeySettings $cmkSettings | Out-Null
77+
}
78+
79+
$ExistingColumnKeys = $smoDatabase.ColumnEncryptionKeys
80+
@($AuthColumnKeyName, $AppColumnKeyName, $LogColumnKeyName) | ForEach-Object {
81+
if ($ExistingColumnKeys[$_]) {
82+
Write-Warning "Column Encryption Key already $_ exists."
83+
}
84+
else {
85+
$smoDatabase | New-SqlColumnEncryptionKey `
86+
-ColumnMasterKey $MasterKeySQLName `
87+
-Name $_ | Out-Null
88+
}
5889
}
5990

ReadMe.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ ALTER ROLE db_owner
3939
ADD MEMBER AlwaysEncryptedOwner;
4040
```
4141

42-
Initially the columns are not encrypted. To encrypt them run the included
43-
`Encryption.ps1`. This script supports the `-Debug` and `-Verbose` parameters
44-
This will create a self signed certificate in your user script repository and
45-
encrypt all the appropriate columns.
42+
Initially the columns are not encrypted. To encrypt them run the included powershell scripts.
43+
These script supports the `-Debug` and `-Verbose` parameters.
44+
This will create a self signed certificate in your user script repository and encrypt all the appropriate columns.
45+
46+
```PowerShell
47+
$ConnectionString = 'Data Source=localhost,1433;Initial Catalog=AlwaysEncryptedSample;UID=AlwaysEncryptedOwner;PWD=7aO!z@xUu!4r6EvD#D&l$sz6&h^rhxL6fzAHMpnOga@LO*WdsEdpfh4^Egtl;Application Name=AppVeyor CI Process;Column Encryption Setting=Enabled'
48+
.\New-EncryptionKeys.ps1 $ConnectionString -ExportCertificate
49+
.\Invoke-EncryptColumns.ps1 -ConnectionString $ConnectionString
50+
```
4651

4752
## Schemas
4853

appveyor.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ version: 0.1.{build}
22
image: Visual Studio 2015
33
install:
44
- ps: $env:SQL_SERVER_BACKUP_DIRECTORY=(Get-ItemProperty "HKLM:\Software\Microsoft\Microsoft SQL Server\MSSQL13.SQL2016\MSSqlServer").BackupDirectory
5+
# The Visual Studio 2015 Imge doesn't have the newer version of New-SelfSignedCertificate with the paramaters I need to call.
6+
# For now I'm restoring a password protected pfx. Yes this is an issue for secrets management, I'll addres in the near future.
7+
- ps: Import-PfxCertificate -FilePath "$($env:SQL_SERVER_COLUMN_CERTIFICATE).pfx" -CertStoreLocation "Cert:\CurrentUser\My" -Password (ConvertTo-SecureString -String $env:PFX_PASSWORD -Force -AsPlainText) | Out-Null
58
assembly_info:
69
patch: true
710
file: 'GlobalAssemblyInfo.cs'
@@ -12,15 +15,18 @@ environment:
1215
MSBUILD_LOG_VERSION: 2.0.94
1316
MSBUILD_LOG_FILE: msbuild.binlog
1417
NUGET_VERBOSITY: quiet
18+
PFX_PASSWORD: # The decrypted value is 1234. I'm just doing this to demo secret management.
19+
secure: 7/Xb2kAfb/4yKaHFokWjUA==
1520
SQL_SERVER_INSTANCE: (local)\SQL2016
1621
SQL_SERVER_USER: AlwaysEncryptedOwner
1722
SQL_SERVER_PASSWORD: 7aO!z@xUu!4r6EvD#D&l$sz6&h^rhxL6fzAHMpnOga@LO*WdsEdpfh4^Egtl
1823
SQL_SERVER_DATABASE: AlwaysEncryptedSample
1924
SQL_SERVER_BACKUP_FILE: $(SQL_SERVER_DATABASE).bak
2025
SQL_SERVER_DACPAC: $(SQL_SERVER_DATABASE).dacpac
2126
SQL_SERVER_GENERATED_SCHEMA: $(SQL_SERVER_DATABASE).sql
22-
SQL_SERVER_CONNECTION_STRING: "Data Source=$(SQL_SERVER_INSTANCE);Initial Catalog=$(SQL_SERVER_DATABASE);UID=$(SQL_SERVER_USER);PWD=$(SQL_SERVER_PASSWORD);Application Name=AppVeyor CI Process;Column Encryption Setting=Enabled"
27+
SQL_SERVER_CONNECTION_STRING: "Data Source=$(SQL_SERVER_INSTANCE);Initial Catalog=$(SQL_SERVER_DATABASE);Integrated Security=SSPI;Application Name=AppVeyor CI Process;Column Encryption Setting=Enabled"
2328
SQL_SERVER_VERIFICATION_LOG: $(SQL_SERVER_DATABASE).verification.log
29+
SQL_SERVER_COLUMN_CERTIFICATE: AlwaysEncryptedSampleCMK
2430
matrix:
2531
- {}
2632
services:
@@ -33,6 +39,10 @@ cache:
3339
- packages -> **\packages.config
3440
before_build:
3541
- cmd: sqlcmd -S "%SQL_SERVER_INSTANCE%" -i .\appveyor\init.sql
42+
# See here regarding -AboutClobber https://dba.stackexchange.com/a/174717/1817
43+
- ps: Install-Module sqlserver -Scope CurrentUser -AllowClobber
44+
- ps: .\New-EncryptionKeys.ps1 -ConnectionString $env:SQL_SERVER_CONNECTION_STRING -ExportCertificate
45+
- ps: .\Invoke-EncryptColumns.ps1 -ConnectionString $env:SQL_SERVER_CONNECTION_STRING
3646
- cmd: nuget restore -Verbosity %NUGET_VERBOSITY%
3747
- cmd: nuget install MSBuild.StructuredLogger -Version %MSBUILD_LOG_VERSION% -SolutionDirectory . -Verbosity %NUGET_VERBOSITY%
3848
build:
@@ -59,6 +69,9 @@ artifacts:
5969
- path: $(SQL_SERVER_VERIFICATION_LOG)
6070
name: Verifiction Query Results
6171
type: file
72+
- path: $(SQL_SERVER_COLUMN_CERTIFICATE).cer
73+
name: Column Master Key Certificate
74+
type: file
6275
- path: $(MSBUILD_LOG_FILE)
6376
name: MSBuild BInary Log
6477
type: file

0 commit comments

Comments
 (0)