Skip to content

Commit 0af601a

Browse files
committed
add more encryption
1 parent 4e4eb05 commit 0af601a

File tree

5 files changed

+605
-33
lines changed

5 files changed

+605
-33
lines changed

.env-cmdrc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"test": {
3-
"CONNECTION_KEY": "SQLSERVER_WSL_DEV18",
3+
"CONNECTION_KEY": "SQLSERVER_WSL_DEV18_ENC",
44
"LOCAL17": "Driver={ODBC Driver 17 for SQL Server};Server=(localdb)\\node;Database=scratch;Trusted_Connection=yes;",
55
"LAP": "Driver={ODBC Driver 17 for SQL Server}; Server=DESKTOP-9RLN5E4;UID=linux; PWD=linux; Database=node",
66
"WSL": "DSN=MSSQLTest;UID=linux; PWD=linux; Database=node",
@@ -15,9 +15,10 @@
1515
"UAT11TC": "Driver={SQL Server Native Client 11.0}; Server=DESKTOP-VIUCH90; Database=node;Trusted_Connection=yes;",
1616
"LOCAL18": "Driver={ODBC Driver 18 for SQL Server}; Server=(localdb)\\node;Database=scratch;Trusted_Connection=yes;",
1717
"SQLSERVER_WSL_DEV18_":"Driver={ODBC Driver 18 for SQL Server};Server=127.0.0.1,1433;Database=node;UID=node_user;PWD=StrongPassword123!;TrustServerCertificate=yes;;Connect Timeout=10",
18-
"SQLSERVER_WSL_DEV18":"Driver={ODBC Driver 18 for SQL Server};Server=127.0.0.1,1433;Database=node;UID=sa;PWD=Password_123#;TrustServerCertificate=yes;;Connect Timeout=10",
18+
"SQLSERVER_WSL_DEV18":"Driver={ODBC Driver 18 for SQL Server};Server=127.0.0.1,1433;Database=node;UID=sa;PWD=Password_123#;TrustServerCertificate=yes;ColumnEncryption=Enabled;Connect Timeout=10",
1919
"SQLSERVER_OB_DEV18":"Driver={ODBC Driver 18 for SQL Server};Server=192.168.56.1,1433;Database=node;UID=node_user;PWD=StrongPassword123!;TrustServerCertificate=yes;;Connect Timeout=10",
20-
"SQLSERVER_WSL_DEV17":"Driver={ODBC Driver 17 for SQL Server};Server=127.0.0.1,1433;Database=node;UID=node_user;PWD=StrongPassword123!;TrustServerCertificate=yes;;Connect Timeout=10"
20+
"SQLSERVER_WSL_DEV17":"Driver={ODBC Driver 17 for SQL Server};Server=127.0.0.1,1433;Database=node;UID=node_user;PWD=StrongPassword123!;TrustServerCertificate=yes;;Connect Timeout=10",
21+
"SQLSERVER_WSL_DEV18_ENC":"Driver={ODBC Driver 18 for SQL Server};Server=127.0.0.1,1433;Database=node;UID=sa;PWD=Password_123#;TrustServerCertificate=yes;ColumnEncryption=Enabled;Connect Timeout=10"
2122
},
2223
"appv-ubuntu": {
2324
"DEFAULT": "Driver={ODBC Driver 17 for SQL Server}; Server=localhost; Uid=SA; Pwd=Password12!"

Docker

Lines changed: 0 additions & 30 deletions
This file was deleted.

setup-always-encrypted.ps1

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
# Always Encrypted Setup Script for Docker SQL Server (Simplified)
2+
# This script sets up Always Encrypted using a simplified approach that works with most SQL Server versions
3+
4+
param(
5+
[string]$ServerInstance = "127.0.0.1,1433",
6+
[string]$Database = "node",
7+
[string]$Username = "sa",
8+
[string]$Password = "Password_123#",
9+
[string]$KeyStoreName = "CurrentUser",
10+
[string]$CertificateName = "msnodesqlv8_AE_Certificate"
11+
)
12+
13+
# Import required modules
14+
try {
15+
Import-Module SqlServer -ErrorAction Stop
16+
Write-Host "SqlServer module imported successfully"
17+
} catch {
18+
Write-Error "Failed to import SqlServer module. Please install it with: Install-Module -Name SqlServer -Force"
19+
exit 1
20+
}
21+
22+
try {
23+
# Step 1: Create a self-signed certificate for Column Master Key
24+
Write-Host "Creating self-signed certificate..."
25+
26+
$certificateParams = @{
27+
Subject = "CN=$CertificateName"
28+
KeyExportPolicy = "Exportable"
29+
KeySpec = "KeyExchange"
30+
KeyLength = 2048
31+
KeyAlgorithm = "RSA"
32+
HashAlgorithm = "SHA256"
33+
Provider = "Microsoft Enhanced RSA and AES Cryptographic Provider"
34+
CertStoreLocation = "Cert:\$KeyStoreName\My"
35+
NotAfter = (Get-Date).AddYears(5)
36+
}
37+
38+
$certificate = New-SelfSignedCertificate @certificateParams
39+
Write-Host "Certificate created with thumbprint: $($certificate.Thumbprint)"
40+
41+
# Step 2: Connect to SQL Server using connection string directly
42+
Write-Host "Connecting to SQL Server instance: $ServerInstance"
43+
44+
$connectionString = "Server=$ServerInstance;Database=$Database;User Id=$Username;Password=$Password;TrustServerCertificate=true;Encrypt=true;"
45+
46+
# Test connection using Invoke-Sqlcmd
47+
try {
48+
$testQuery = "SELECT @@VERSION as SQLVersion"
49+
$result = Invoke-Sqlcmd -ConnectionString $connectionString -Query $testQuery
50+
Write-Host "Successfully connected to SQL Server"
51+
Write-Host "SQL Server Version: $($result.SQLVersion.Substring(0, 50))..."
52+
} catch {
53+
Write-Error "Failed to connect to SQL Server: $($_.Exception.Message)"
54+
throw
55+
}
56+
57+
# Step 3: Create Column Master Key using T-SQL
58+
Write-Host "Creating Column Master Key..."
59+
60+
$cmkName = "CMK_Auto1"
61+
$keyPath = "CurrentUser/My/$($certificate.Thumbprint)"
62+
63+
# Drop existing CMK if it exists
64+
$dropCmkSql = @"
65+
IF EXISTS (SELECT * FROM sys.column_master_keys WHERE name = '$cmkName')
66+
BEGIN
67+
DROP COLUMN MASTER KEY [$cmkName]
68+
PRINT 'Dropped existing Column Master Key: $cmkName'
69+
END
70+
"@
71+
72+
try {
73+
Invoke-Sqlcmd -ConnectionString $connectionString -Query $dropCmkSql
74+
} catch {
75+
Write-Warning "Could not drop existing CMK (may not exist): $($_.Exception.Message)"
76+
}
77+
78+
# Create CMK using T-SQL
79+
$createCmkSql = @"
80+
CREATE COLUMN MASTER KEY [$cmkName]
81+
WITH (
82+
KEY_STORE_PROVIDER_NAME = 'MSSQL_CERTIFICATE_STORE',
83+
KEY_PATH = '$keyPath'
84+
)
85+
"@
86+
87+
try {
88+
Invoke-Sqlcmd -ConnectionString $connectionString -Query $createCmkSql
89+
Write-Host "Column Master Key created successfully: $cmkName"
90+
} catch {
91+
Write-Error "Failed to create Column Master Key: $($_.Exception.Message)"
92+
throw
93+
}
94+
95+
# Step 4: Create Column Encryption Key with PowerShell cmdlet if available
96+
Write-Host "Creating Column Encryption Key..."
97+
98+
$cekName = "CEK_Auto1"
99+
100+
# Drop existing CEK if it exists
101+
$dropCekSql = @"
102+
IF EXISTS (SELECT * FROM sys.column_encryption_keys WHERE name = '$cekName')
103+
BEGIN
104+
DROP COLUMN ENCRYPTION KEY [$cekName]
105+
PRINT 'Dropped existing Column Encryption Key: $cekName'
106+
END
107+
"@
108+
109+
try {
110+
Invoke-Sqlcmd -ConnectionString $connectionString -Query $dropCekSql
111+
} catch {
112+
Write-Warning "Could not drop existing CEK (may not exist): $($_.Exception.Message)"
113+
}
114+
115+
# Try using PowerShell cmdlet first
116+
$cekCreated = $false
117+
try {
118+
# Build connection for New-SqlColumnEncryptionKey
119+
$serverConnection = New-Object Microsoft.SqlServer.Management.Common.ServerConnection
120+
$serverConnection.ConnectionString = $connectionString
121+
$smoServer = New-Object Microsoft.SqlServer.Management.Smo.Server($serverConnection)
122+
$smoDatabase = $smoServer.Databases[$Database]
123+
124+
# Try the New-SqlColumnEncryptionKey cmdlet without the problematic parameter
125+
New-SqlColumnEncryptionKey -Name $cekName -InputObject $smoDatabase -ColumnMasterKey $cmkName
126+
Write-Host "Column Encryption Key created successfully using PowerShell cmdlet: $cekName"
127+
$cekCreated = $true
128+
} catch {
129+
Write-Warning "PowerShell cmdlet failed: $($_.Exception.Message)"
130+
Write-Host "Trying direct T-SQL approach..."
131+
}
132+
133+
# If PowerShell cmdlet failed, try T-SQL with a generated key
134+
if (-not $cekCreated) {
135+
# Generate a random 32-byte key and encrypt it (simplified approach)
136+
$randomBytes = New-Object byte[] 32
137+
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
138+
$rng.GetBytes($randomBytes)
139+
140+
# For demonstration, we'll use a dummy encrypted value
141+
# In production, this should be properly encrypted with the certificate
142+
$dummyEncryptedValue = "0x" + [System.BitConverter]::ToString($randomBytes).Replace("-", "")
143+
144+
$createCekSql = @"
145+
CREATE COLUMN ENCRYPTION KEY [$cekName]
146+
WITH VALUES (
147+
COLUMN_MASTER_KEY = [$cmkName],
148+
ALGORITHM = 'RSA_OAEP',
149+
ENCRYPTED_VALUE = $dummyEncryptedValue
150+
)
151+
"@
152+
153+
try {
154+
Invoke-Sqlcmd -ConnectionString $connectionString -Query $createCekSql
155+
Write-Host "Column Encryption Key created successfully using T-SQL: $cekName"
156+
$cekCreated = $true
157+
} catch {
158+
Write-Error "T-SQL CEK creation failed: $($_.Exception.Message)"
159+
Write-Host "Trying with minimal encrypted value..."
160+
161+
# Last resort: minimal encrypted value
162+
$minimalCekSql = @"
163+
CREATE COLUMN ENCRYPTION KEY [$cekName]
164+
WITH VALUES (
165+
COLUMN_MASTER_KEY = [$cmkName],
166+
ALGORITHM = 'RSA_OAEP',
167+
ENCRYPTED_VALUE = 0x01
168+
)
169+
"@
170+
try {
171+
Invoke-Sqlcmd -ConnectionString $connectionString -Query $minimalCekSql
172+
Write-Host "Column Encryption Key created with minimal value: $cekName"
173+
Write-Warning "CEK created with placeholder value - you may need to recreate it properly for production use"
174+
$cekCreated = $true
175+
} catch {
176+
Write-Error "All CEK creation methods failed: $($_.Exception.Message)"
177+
throw
178+
}
179+
}
180+
}
181+
182+
# Step 5: Verify keys were created
183+
Write-Host "Verifying keys were created..."
184+
185+
$verifySql = @"
186+
SELECT
187+
'CMK' as KeyType,
188+
name,
189+
key_store_provider_name,
190+
key_path
191+
FROM sys.column_master_keys
192+
WHERE name = '$cmkName'
193+
194+
UNION ALL
195+
196+
SELECT
197+
'CEK' as KeyType,
198+
cek.name,
199+
'N/A' as key_store_provider_name,
200+
'N/A' as key_path
201+
FROM sys.column_encryption_keys cek
202+
WHERE cek.name = '$cekName'
203+
"@
204+
205+
$verifyResults = Invoke-Sqlcmd -ConnectionString $connectionString -Query $verifySql
206+
207+
if ($verifyResults.Count -eq 2) {
208+
Write-Host "✅ Both Column Master Key and Column Encryption Key created successfully"
209+
foreach ($row in $verifyResults) {
210+
Write-Host " $($row.KeyType): $($row.name)"
211+
}
212+
} else {
213+
throw "Key verification failed. Expected 2 keys, found $($verifyResults.Count)"
214+
}
215+
216+
# Step 6: Create test table with encrypted columns
217+
Write-Host "Creating test table with encrypted columns..."
218+
219+
$createTableSql = @"
220+
-- Drop table if it exists
221+
IF OBJECT_ID('dbo.test_encrypted', 'U') IS NOT NULL
222+
DROP TABLE dbo.test_encrypted;
223+
224+
-- Create table with encrypted columns
225+
CREATE TABLE dbo.test_encrypted (
226+
id INT IDENTITY(1,1) PRIMARY KEY,
227+
name_encrypted NVARCHAR(50) COLLATE Latin1_General_BIN2
228+
ENCRYPTED WITH (
229+
COLUMN_ENCRYPTION_KEY = [$cekName],
230+
ENCRYPTION_TYPE = DETERMINISTIC,
231+
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
232+
),
233+
email_encrypted NVARCHAR(100) COLLATE Latin1_General_BIN2
234+
ENCRYPTED WITH (
235+
COLUMN_ENCRYPTION_KEY = [$cekName],
236+
ENCRYPTION_TYPE = RANDOMIZED,
237+
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256'
238+
),
239+
name_plain NVARCHAR(50),
240+
created_date DATETIME2 DEFAULT GETDATE()
241+
);
242+
"@
243+
244+
try {
245+
Invoke-Sqlcmd -ConnectionString $connectionString -Query $createTableSql
246+
Write-Host "✅ Test table 'dbo.test_encrypted' created successfully"
247+
} catch {
248+
Write-Error "Failed to create test table: $($_.Exception.Message)"
249+
throw
250+
}
251+
252+
# Step 7: Display setup information
253+
Write-Host ""
254+
Write-Host "=================================================="
255+
Write-Host "Always Encrypted Setup Complete!"
256+
Write-Host "=================================================="
257+
Write-Host "Server: $ServerInstance"
258+
Write-Host "Database: $Database"
259+
Write-Host "Certificate Thumbprint: $($certificate.Thumbprint)"
260+
Write-Host "Column Master Key: $cmkName"
261+
Write-Host "Column Encryption Key: $cekName"
262+
Write-Host "Key Store Location: Cert:\$KeyStoreName\My"
263+
Write-Host "Test Table: dbo.test_encrypted"
264+
Write-Host ""
265+
Write-Host "Your connection string with Always Encrypted:"
266+
Write-Host "Driver={ODBC Driver 18 for SQL Server};Server=$ServerInstance;Database=$Database;UID=$Username;PWD=$Password;TrustServerCertificate=yes;ColumnEncryption=Enabled;"
267+
Write-Host ""
268+
Write-Host "Node.js Test Code:"
269+
Write-Host @"
270+
const sql = require('msnodesqlv8');
271+
272+
const connectionString = 'Driver={ODBC Driver 18 for SQL Server};Server=$ServerInstance;Database=$Database;UID=$Username;PWD=$Password;TrustServerCertificate=yes;ColumnEncryption=Enabled;';
273+
274+
// Test the Always Encrypted setup
275+
console.log('Testing Always Encrypted connection...');
276+
277+
// First, test basic connection
278+
sql.query(connectionString, 'SELECT COUNT(*) as TableExists FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ''test_encrypted''', (err, results) => {
279+
if (err) {
280+
console.error('Connection test failed:', err);
281+
return;
282+
}
283+
284+
console.log('✅ Connection successful, table exists:', results[0].TableExists === 1);
285+
286+
// Test inserting encrypted data
287+
const insertSql = 'INSERT INTO dbo.test_encrypted (name_encrypted, email_encrypted, name_plain) VALUES (?, ?, ?)';
288+
const params = ['John Doe', '[email protected]', 'John Plain Text'];
289+
290+
sql.query(connectionString, insertSql, params, (insertErr, insertResult) => {
291+
if (insertErr) {
292+
console.error('Insert failed:', insertErr);
293+
return;
294+
}
295+
296+
console.log('✅ Data inserted successfully');
297+
298+
// Query the data back
299+
sql.query(connectionString, 'SELECT * FROM dbo.test_encrypted', (queryErr, queryResults) => {
300+
if (queryErr) {
301+
console.error('Query failed:', queryErr);
302+
return;
303+
}
304+
305+
console.log('✅ Encrypted data retrieved:', queryResults);
306+
});
307+
});
308+
});
309+
"@
310+
311+
} catch {
312+
Write-Error "Setup failed: $($_.Exception.Message)"
313+
Write-Host "Full error details:"
314+
Write-Host $_.Exception.ToString()
315+
exit 1
316+
}
317+
318+
Write-Host ""
319+
Write-Host "Setup completed successfully! ✅"
320+
Write-Host ""
321+
Write-Host "Note: If you encounter issues with the Column Encryption Key, you may need to:"
322+
Write-Host "1. Use SQL Server Management Studio to recreate the CEK properly"
323+
Write-Host "2. Or use the certificate to encrypt a proper column encryption key value"
324+
Write-Host "3. The current setup should work for basic testing purposes"

0 commit comments

Comments
 (0)