适用于:
SQL Server
Azure SQL 数据库
Azure SQL 托管实例
本教程介绍如何开始使用 Always Encrypted。 本教程涵盖:
先决条件
对于本教程的内容,你需要:
注意
Microsoft建议在运行 Always Encrypted PowerShell 脚本时使用 PowerShell 7 或更高版本。 PowerShell 7 提供了改进的跨平台支持、更好的性能和与 SqlServer 模块(v22+)的最新兼容性,这是许多 Always Encrypted 方案所必需的。
步骤 1:创建和填充数据库架构
在此步骤中,你将创建 HR 架构和 Employees 表。 然后,向表中填充一些数据。
连接到数据库。 有关如何从 SSMS 连接到数据库的说明,请参阅 快速入门:使用 SQL Server Management Studio (SSMS) 连接和查询 Azure SQL 数据库或 Azure SQL 托管实例 或 快速入门:使用 SQL Server Management Studio (SSMS) 连接和查询 SQL Server 实例。
打开 ContosoHR 数据库的新查询窗口。
粘贴并执行以下语句以创建名为 Employees 的新表。
CREATE SCHEMA [HR];
GO
CREATE TABLE [HR].[Employees]
(
[EmployeeID] [int] IDENTITY(1,1) NOT NULL
, [SSN] [char](11) NOT NULL
, [FirstName] [nvarchar](50) NOT NULL
, [LastName] [nvarchar](50) NOT NULL
, [Salary] [money] NOT NULL
) ON [PRIMARY];
粘贴并执行以下语句,向Employees表中添加几条员工记录。
INSERT INTO [HR].[Employees]
(
[SSN]
, [FirstName]
, [LastName]
, [Salary]
)
VALUES
(
'795-73-9838'
, N'Catherine'
, N'Abel'
, $31692
);
INSERT INTO [HR].[Employees]
(
[SSN]
, [FirstName]
, [LastName]
, [Salary]
)
VALUES
(
'990-00-6818'
, N'Kim'
, N'Abercrombie'
, $55415
);
在 PowerShell 会话中执行以下命令。 请确保将连接字符串更新为你的服务器地址,并使用适用于你的数据库的身份验证设置。
Import-Module "SqlServer" -MinimumVersion 22.0.50
# Connect to your database
# Set the valid server name, database name and authentication keywords in the connection string
$serverName = "<server name>.database.windows.net"
$databaseName = "ContosoHR"
$connStr = "Server=tcp:$serverName,1433;Database=$databaseName;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Authentication=Active Directory Interactive"
try {
$database = Get-SqlDatabase -ConnectionString $connStr -Encrypt Mandatory -ErrorAction Stop
}
catch {
Write-Error "Failed to connect. Verify server name, database name, Azure SQL firewall access, and Microsoft Entra permissions."
throw
}
$query = @'
IF SCHEMA_ID(N'HR') IS NULL
BEGIN
EXEC(N'CREATE SCHEMA [HR]');
END;
IF OBJECT_ID(N'HR.Employees', N'U') IS NULL
BEGIN
CREATE TABLE [HR].[Employees]
(
[EmployeeID] INT IDENTITY(1,1) NOT NULL,
[SSN] CHAR(11) NOT NULL,
[FirstName] NVARCHAR(50) NOT NULL,
[LastName] NVARCHAR(50) NOT NULL,
[Salary] MONEY NOT NULL,
CONSTRAINT [PK_HR_Employees] PRIMARY KEY CLUSTERED ([EmployeeID])
);
END;
'@
#write-host @query
Invoke-SqlCmd -ConnectionString $connStr -Query $query
# Add a few rows to the Employees table.
$query = @'
INSERT INTO [HR].[Employees]
(
[SSN]
, [FirstName]
, [LastName]
, [Salary]
)
VALUES
(
'795-73-9838'
, N'Catherine'
, N'Abel'
, $31692
);
INSERT INTO [HR].[Employees]
(
[SSN]
, [FirstName]
, [LastName]
, [Salary]
)
VALUES
(
'990-00-6818'
, N'Kim'
, N'Abercrombie'
, $55415
);
'@
Invoke-SqlCmd -ConnectionString $connStr -Query $query
Write-Host 'Completed successfully.'
步骤 2:加密列
在此步骤中,你将为 Always Encrypted 配置列主密钥和列加密密钥。 然后,你将对 Employees 表中的 SSN 列和 Salary 列进行加密。
SSMS 提供了一个向导,通过设置列主密钥、列加密密钥并加密选定的列即可轻松地配置 Always Encrypted。
在“对象资源管理器”中,展开“数据库”>“ContosoHR”>“表”。
右键单击 Employees 表,然后选择 Encrypt Columns 以打开 Always Encrypted 向导。
在向导的“简介”页上,选择“下一步”。
在“列选择”页面上。
- 选择“SSN”和“Salary”列。 为 SSN 列选择“确定性加密”,并为“Salary”列选择“随机加密”。 确定性加密支持某些查询,例如对加密列执行相等比较的点查找查询。 随机加密不支持在加密列上进行任何计算。
- 对于这两列,都将 CEK-Auto1(新) 保留为列加密密钥。 此密钥尚不存在,将由向导生成。
- 选择下一步。
在“主密钥配置”页上,配置向导将生成的新列主密钥。 首先需要选择要用于存储列主密钥的位置。 向导支持两种密钥存储库类型:
- Azure 密钥保管库 - 如果数据库位于 Azure 中,则建议使用此选项
- Windows 证书存储
通常建议使用 Azure 密钥保管库 选项,尤其是在数据库位于 Azure 中时。
要使用 Azure 密钥保管库:
- 选择“Azure 密钥保管库”。
- 选择“登录”并完成登录到 Azure 的步骤。
- 登录后,页面将显示你有权访问的订阅和密钥保管库列表。 选择包含要使用的密钥保管库的 Azure 订阅。
- 选择密钥保管库。
- 选择下一步。
要使用 Windows 证书存储区:
选择“Windows 证书存储”。
保留默认选项 当前用户 - 这会指示向导在 当前用户 存储中生成证书(即新的列主密钥)。
选择下一步。
在“就地加密设置”页上,不需要其他配置,因为数据库未启用 Enclave。 选择下一步。
在“运行设置”页上,系统会询问是要继续加密还是生成稍后要执行的 PowerShell 脚本。 保留默认设置,然后选择“下一步”。
在摘要页面上,向导会告知您它将执行的操作。 检查所有信息是否正确,然后选择“完成”。
在“结果”页上,可以监视向导操作的进度。 等待所有操作成功完成,然后选择“关闭”。
(可选)浏览向导在数据库中所做的更改。
展开 ContosoHR>安全性>始终加密密钥,以查看向导创建的列主密钥和列加密密钥的元数据对象。
还可以针对包含密钥元数据的系统目录视图运行以下查询。
SELECT * FROM sys.column_master_keys;
SELECT * FROM sys.column_encryption_keys
SELECT * FROM sys.column_encryption_key_values
在对象资源管理器中,右键单击Employees表,然后依次选择将表编写为脚本>CREATE 到>新建查询编辑器窗口。 这将打开一个新的查询窗口,其中包含 CREATE TABLEEmployees 表的语句。 请注意 社会安全号码 和 薪资 列的定义中出现的 ENCRYPTED WITH 子句。
还可以针对 sys.columns 运行以下查询,以检索两个加密列的列级加密元数据。
SELECT
[name]
, [encryption_type]
, [encryption_type_desc]
, [encryption_algorithm_name]
, [column_encryption_key_id]
FROM sys.columns
WHERE [encryption_type] IS NOT NULL;
在密钥存储库中创建列主密钥。
如果使用 Azure 密钥保管库,请执行以下命令,以在密钥保管库中创建非对称密钥。 请务必提供正确的订阅 ID、包含密钥保管库的资源组名称和密钥保管库名称。
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$SubscriptionId,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ResourceGroupName,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$KeyVaultName,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$KeyVaultKeyName,
[Parameter(Mandatory = $false)]
[ValidateSet('RSA', 'RSA-HSM', 'EC', 'EC-HSM', 'oct')]
[string]$KeyType = 'RSA',
[Parameter(Mandatory = $false)]
[ValidateSet('Software', 'HSM')]
[string]$Destination = 'Software'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module Az.Accounts -ErrorAction Stop
Import-Module Az.KeyVault -ErrorAction Stop
Import-Module Az.Resources -ErrorAction Stop
Write-Host "[AKV] Signing in to Azure"
Connect-AzAccount -ErrorAction Stop | Out-Null
Write-Host "[AKV] Setting subscription context to '$SubscriptionId'"
Set-AzContext -SubscriptionId $SubscriptionId -ErrorAction Stop | Out-Null
Write-Host "[AKV] Validating Key Vault '$KeyVaultName' in resource group '$ResourceGroupName'"
$vault = Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -ErrorAction Stop
Write-Host "[AKV] Checking if key '$KeyVaultKeyName' already exists"
$existingKey = Get-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyVaultKeyName -ErrorAction SilentlyContinue
if ($existingKey) {
Write-Host "[AKV] Key already exists. Returning existing key metadata."
$existingKey
return
}
Write-Host "[AKV] Creating key '$KeyVaultKeyName' (Type=$KeyType, Destination=$Destination)"
$keyVaultKey = Add-AzKeyVaultKey `
-VaultName $KeyVaultName `
-Name $KeyVaultKeyName `
-Destination $Destination `
-KeyType $KeyType `
-ErrorAction Stop
Write-Host '[AKV] Key created successfully.'
$keyVaultKey
如果使用 Windows 证书存储,请执行以下命令,以在“当前用户”存储中创建证书。
$cert = New-SelfSignedCertificate -Subject "HRCMK" -CertStoreLocation Cert:CurrentUser\My -KeyExportPolicy Exportable -Type DocumentEncryptionCert -KeyUsage DataEncipherment -KeySpec KeyExchange
使用 SqlServer PowerShell 模块连接到数据库。 请务必为数据库提供有效的连接字符串。 创建一个列主密钥元数据对象,用于引用您在密钥存储中创建的物理列主密钥。
如果使用 Azure 密钥保管库,请执行以下命令。
Write-Host "[AE] Connecting to Azure SQL and creating metadata"
$keyVaultAccessToken = (Get-AzAccessToken -ResourceUrl "https://vault.azure.net").Token
$serverName = "<server name>.database.windows.net"
$databaseName = "ContosoHR"
$connStr = "Server=tcp:$serverName,1433;Database=$databaseName;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Authentication=Active Directory Interactive"
try {
$database = Get-SqlDatabase -ConnectionString $connStr -Encrypt Mandatory -ErrorAction Stop
}
catch {
Write-Error "Failed to connect. Verify server name, database name, Azure SQL firewall access, and Microsoft Entra permissions."
throw
}
$cmkSettings = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyURL $keyVaultKey.Key.Kid
$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $existingCmk) {
New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
}
Write-Host "[AE] Connecting to Azure SQL and creating metadata"
$serverName = "<server name>.database.windows.net"
$databaseName = "ContosoHR"
$connStr = "Server=tcp:$serverName,1433;Database=$databaseName;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Authentication=Active Directory Interactive"
try {
$database = Get-SqlDatabase -ConnectionString $connStr -Encrypt Mandatory -ErrorAction Stop
}
catch {
Write-Error "Failed to connect. Verify server name, database name, Azure SQL firewall access, and Microsoft Entra permissions."
throw
}
$cmkSettings = New-SqlCertificateStoreColumnMasterKeySettings -CertificateStoreLocation "CurrentUser" -Thumbprint $cert.Thumbprint
$existingCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $existingCmk) {
New-SqlColumnMasterKey -Name $CmkName -InputObject $database -ColumnMasterKeySettings $cmkSettings | Out-Null
}
生成列加密密钥,使用列主密钥对其加密,并在数据库中创建列加密密钥元数据。
$existingCek = Get-SqlColumnEncryptionKey -InputObject $database | Where-Object { $_.Name -eq $CekName }
if (-not $existingCek) {
New-SqlColumnEncryptionKey -Name $CekName -InputObject $database -ColumnMasterKey $CmkName -KeyVaultAccessToken $keyVaultAccessToken | Out-Null
}
对 Employees 表中的 SSN 列和 Salary 列进行加密。 为 SSN 列选择“确定性加密”,并为“Salary”列选择“随机加密”。 确定性加密支持某些查询,例如对加密列执行相等比较的点查找查询。 随机加密不支持在加密列上进行任何计算。
# Encrypt the SSN and Salary columns
$ces = @()
$ces += New-SqlColumnEncryptionSettings -ColumnName "HR.Employees.SSN" -EncryptionType "Deterministic" -EncryptionKey $cekName
$ces += New-SqlColumnEncryptionSettings -ColumnName "HR.Employees.Salary" -EncryptionType "Randomized" -EncryptionKey $cekName
Set-SqlColumnEncryption -InputObject $database -ColumnEncryptionSettings $ces -LogFileDirectory .
(可选)浏览你在数据库中所做的更改。
运行以下命令,查询包含有关列主密钥元数据和所创建列加密密钥的系统目录视图。
$query = @'
SELECT * FROM sys.column_master_keys;
SELECT * FROM sys.column_encryption_keys
SELECT * FROM sys.column_encryption_key_values
'@
Invoke-SqlCmd -ConnectionString $connectionString -Query $query
运行以下命令查询 sys.columns,以检索两个加密列的列级加密元数据。
$query = @'
SELECT
[name]
, [encryption_type]
, [encryption_type_desc]
, [encryption_algorithm_name]
, [column_encryption_key_id]
FROM sys.columns
WHERE [encryption_type] IS NOT NULL;
'@
Invoke-SqlCmd -ConnectionString $connectionString -Query $query
步骤 3:查询加密列
连接到数据库,并在连接中禁用 Always Encrypted。
- 打开一个新的查询窗口。
- 在查询窗口中右键单击任意位置,然后选择“连接”和“更改连接”>。 这将打开“连接到数据库引擎”对话框。
- 选择“选项”。<< 这将显示“连接到数据库引擎”对话框中的其他选项卡。
- 选择“Always Encrypted”选项卡。
- 务必不要选中“启用 Always Encrypted(列加密)”。
- 选择“连接” 。
粘贴并执行以下查询。 查询应会返回二进制加密数据。
SELECT [SSN], [Salary] FROM [HR].[Employees]
连接到数据库,并在连接中启用 Always Encrypted。
- 在查询窗口中右键单击任意位置,然后选择“连接”和“更改连接”>。 这将打开“连接到数据库引擎”对话框。
- 选择“选项”。<< 这将显示“连接到数据库引擎”对话框中的其他选项卡。
- 选择“Always Encrypted”选项卡。
- 选择“启用 Always Encrypted(列加密)”。
- 选择“连接” 。
重新运行同一查询。 由于在连接时已为数据库连接启用了 Always Encrypted,因此 SSMS 中的客户端驱动程序将尝试解密两个加密列中存储的数据。 如果使用 Azure 密钥保管库,系统可能会提示登录到 Azure。
启用Always Encrypted 的参数化。 使用此功能,可以运行按加密列筛选数据(或将数据插入加密列)的查询。
- 在 SSMS 的主菜单中,选择“查询”。
- 选择“查询选项…”。
- 转到执行>高级。
- 请确保已选中“为 Always Encrypted 启用参数化”。
- 选择“确定”。
粘贴并执行以下查询,该查询按加密的 SSN 列筛选数据。 查询应会返回一行,其中包含纯文本值。
DECLARE @SSN [char](11) = '795-73-9838'
SELECT [SSN], [Salary] FROM [HR].[Employees]
WHERE [SSN] = @SSN
(可选)如果使用 Azure 密钥保管库 并且配置了访问策略权限模型,请按照以下步骤操作,看看用户在无权访问保护数据的列主密钥的情况下尝试从加密列中检索纯文本数据时会发生什么情况。
- 在密钥保管库的访问策略中,移除你自己的密钥
unwrap 权限。 有关详细信息,请参阅分配 密钥保管库 访问策略。
- 由于 SSMS 中的客户端驱动程序会将从密钥保管库获取的列加密密钥缓存 2 小时,请关闭 SSMS 后重新将其打开。 这将确保密钥缓存为空。
- 连接到数据库,并在连接中启用 Always Encrypted。
- 粘贴并执行以下查询。 查询应会失败并显示错误消息,指示缺少所需的
unwrap 权限。
SELECT [SSN], [Salary] FROM [HR].[Employees]
连接到数据库并禁用 Always Encrypted,然后运行查询以从加密列读取数据。 查询应会返回二进制数组形式的加密数据。
$query = "SELECT [SSN], [Salary] FROM [HR].[Employees]"
Invoke-SqlCmd -ConnectionString $connectionString -Query $query
连接到数据库并启用 Always Encrypted,然后运行查询以从加密列读取数据。 由于有权访问保护加密列的列主密钥,因此查询应会返回纯文本数据。
$query = "SELECT [SSN], [Salary] FROM [HR].[Employees]"
Invoke-SqlCmd -ConnectionString "$connectionString; Column Encryption Setting = Enabled" -Query $query
注意
Invoke-SqlCmd 不支持可以按加密列筛选数据或插入数据的查询。 此类查询需要参数化,但 Invoke-SqlCmd 不支持参数化查询。
后续步骤
另请参阅