エンクレーブ対応キーをプロビジョニングする

適用対象:Windows Azure SQL Database の SQL Server 2019 (15.x) 以降のバージョン

この記事では、セキュリティで保護されたエンクレーブが設定された Always Encrypted に使用されるサーバー側のセキュリティで保護されたエンクレーブ内での計算をサポートするエンクレーブ対応キーをプロビジョニングする方法について説明します。

Always Encrypted キーの管理に関する一般的なガイドラインとプロセスが、エンクレーブ対応キーをプロビジョニングするときも適用されます。 この記事では、セキュリティで保護されたエンクレーブを持つ Always Encrypted に固有の詳細について説明します。

SQL Server Management Studio または PowerShell を使用してエンクレーブ対応列マスター キーをプロビジョニングするには、新しいキーでエンクレーブ計算がサポートされていることを確認します。 これにより、ツール (SSMS または PowerShell) によって、データベースで列マスター キーのメタデータに CREATE COLUMN MASTER KEY を設定する ENCLAVE_COMPUTATIONS ステートメントが生成されます。 詳細については、「 CREATE COLUMN MASTER KEY (Transact-SQL)」を参照してください。

また、ツールでは、列マスター キーを使用して列マスター プロパティがデジタル署名され、データベース メタデータに署名が格納されます。 署名により、ENCLAVE_COMPUTATIONS の設定での悪意のある改ざんが防止されます。 SQL クライアント ドライバーは、エンクレーブの使用を許可する前に署名を検証します。 これにより、セキュリティ管理者は、エンクレーブ内でどの列データを計算できるかを制御できます。

メタデータで列マスター キーを定義した後、 ENCLAVE_COMPUTATIONS プロパティは不変であり、変更することはできません。 既存の列マスター キーで暗号化されている列暗号化キーを使用してエンクレーブ計算を有効にするには、列マスター キーをローテーションして、エンクレーブ対応の列マスター キーに置き換えます。 「エンクレーブ対応キーをローテーションする」をご覧ください。

現時点では、SSMS と PowerShell の両方で、Azure Key Vault または Windows 証明書ストアに格納されたエンクレーブ対応の列マスター キーがサポートされています。 ハードウェア セキュリティ モジュール (CNG または CAPI を使用) はサポートされていません。

エンクレーブ対応の列暗号化キーを作成するには、エンクレーブ対応の列マスター キーを選択して新しいキーを暗号化する必要があります。

次のセクションでは、SSMS と PowerShell を使用してエンクレーブ対応キーをプロビジョニングする方法の詳細について説明します。

SQL Server Management Studio を使用してエンクレーブ対応キーをプロビジョニングする

SQL Server Management Studio では、以下のプロビジョニングを行うことができます。

  • [新しい列マスター キー] ダイアログを使用したエンクレーブ対応の列マスター キー。
  • [新しい列の暗号化キー] ダイアログを使用するエンクレーブ対応の列暗号化キーの作成。

Always Encrypted ウィザードを使用すれば、エンクレーブ対応列マスター キーとエンクレーブ対応列暗号化キーを作成することもできます。

最新バージョンの SQL Server Management Studio (SSMS) をインストールします

[新しい列マスター キー] ダイアログを使用してエンクレーブ対応の列マスター キーをプロビジョニングする

エンクレーブ対応の列マスター キーをプロビジョニングするには、「[新しい列マスター キー] ダイアログを使用して列マスター キーをプロビジョニングする」の手順に従います。 必ず、[エンクレーブ計算を許可する] を選択します。 次のスクリーンショットをご覧ください。

エンクレーブ計算を許可する

[エンクレーブ計算を許可する] チェック ボックスは、データベース用にセキュリティで保護されたエンクレーブが構成されている場合にのみ表示されます。 SQL Server を使用している場合は、「SQL Server でのセキュリティで保護されたエンクレーブの構成」を参照してください。 Azure SQL Database を使用している場合は、「Azure SQL Database のセキュリティで保護されたエンクレーブを使用して Always Encrypted を有効にする」を参照してください。

ヒント

列マスター キーがエンクレーブ対応かどうかを確認するには、オブジェクト エクスプローラーでキーを右クリックし、[プロパティ] を選択します。 キーがエンクレーブ対応の場合、 Enclave Computations:Allowed が画面に表示され、キーのプロパティを示します。 または、sys.column_master_keys (Transact-SQL) ビューを使用することもできます。

[新しい列の暗号化キー] ダイアログを使用してエンクレーブ対応の列暗号化キーをプロビジョニングする

エンクレーブ対応の列暗号化キーをプロビジョニングするには、「[新しい列の暗号化キー] ダイアログを使用して列の暗号化キーをプロビジョニングする」の手順に従います。 列マスター キーを選択するときは、エンクレーブが有効になっていることを確認します。

ヒント

列暗号化キーがエンクレーブ対応かどうかを確認するには、オブジェクト エクスプローラーでキーを右クリックし、[プロパティ] を選択します。 キーがエンクレーブ対応の場合、 Enclave Computations:Allowed が画面に表示され、キーのプロパティを示します。

PowerShell を使用してエンクレーブ対応キーをプロビジョニングする

PowerShell を使用してエンクレーブ対応キーをプロビジョニングするには、SqlServer PowerShell モジュール バージョン 22 以降が必要です。

Microsoftでは、Always Encrypted PowerShell スクリプトを実行するときに PowerShell 7 以降を使用することをお勧めします。 PowerShell 7 では、多くの Always Encrypted シナリオに必要な、クロスプラットフォームのサポートが強化され、パフォーマンスが向上し、SqlServer モジュール (v22 以降) との最新の互換性が提供されます。

一般に、「PowerShell を使用して Always Encrypted キーをプロビジョニングする」で説明されている、Always Encrypted に対する PowerShell でのキー プロビジョニング ワークフロー (役割の分離の有無に関係なく) が、エンクレーブ対応キーにも適用されます。 このセクションでは、エンクレーブ対応キーに固有の詳細について説明します。

SqlServer PowerShell モジュールは、New-SqlCertificateStoreColumnMasterKeySettings および New-SqlAzureKeyVaultColumnMasterKeySettings コマンドレットが -AllowEnclaveComputations パラメーターで拡張され、プロビジョニング処理中にエンクレーブ対応の列マスター キーを指定できるようになっています。 どちらのコマンドレットでも、(Azure Key Vault または Windows 証明書ストアに格納されている) 列マスター キーのプロパティを含むローカル オブジェクトが作成されます。 -AllowEnclaveComputations プロパティを指定すると、キーがローカル オブジェクトでエンクレーブ対応としてマークされます。 また、コマンドレットでは、参照されている列マスター キー (Azure Key Vault または Windows 証明書ストア内) にアクセスして、キーのプロパティへのデジタル署名が行われます。 新しいエンクレーブ対応列マスター キーの設定オブジェクトを作成すると、以降の SqlColumnMasterKey コマンドレットの呼び出しでそれを使用して、新しいキーを記述するメタデータ オブジェクトをデータベースに作成できます。

エンクレーブ対応の列暗号化キーのプロビジョニングは、エンクレーブ対応ではない列暗号化キーのプロビジョニングと何ら変わりません。 新しい列暗号化キーの暗号化に使用される列マスター キーがエンクレーブ対応であることを確認する必要があるだけです。

現在、SqlServer PowerShell モジュールでは、ハードウェア セキュリティ モジュール (CNG または CAPI を使用) に格納されているエンクレーブ対応キーのプロビジョニングはサポートされていません。

例 - Windows 証明書ストアを使用してエンクレーブ対応キーをプロビジョニングする

次のエンドツーエンドの例では、Windows 証明書ストアに格納されている列マスター キーを格納して、エンクレーブ対応キーをプロビジョニングする方法を示します。 スクリプトは、「Windows 証明書ストアの例 (役割の分離なし)」の例が基になっています。 重要な点は、New-SqlCertificateStoreColumnMasterKeySettings コマンドレットで -AllowEnclaveComputations パラメーターを使用していることです。これが、2 つの例のワークフローにおける唯一の違いです。

[CmdletBinding()]
param(
	[Parameter(Mandatory = $false)]
	[string]$DatabaseName = '<database name>',

	[Parameter(Mandatory = $false)]
	[string]$ServerName = "<server name>",

	[Parameter(Mandatory = $false)]
	[string]$CertificateSubject = "AlwaysEncryptedCert",

	[Parameter(Mandatory = $false)]
	[string]$CmkName = "CMK",

	[Parameter(Mandatory = $false)]
	[string]$CekName = "CEK"
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop

Write-Host "[AE] Locating certificate '$CertificateSubject' in CurrentUser\\My"
$cert = Get-ChildItem -Path Cert:CurrentUser\My |
	Where-Object { $_.Subject -eq "CN=$CertificateSubject" } |
	Sort-Object NotAfter -Descending |
	Select-Object -First 1

if (-not $cert) {
	Write-Host "[AE] Certificate not found. Creating self-signed certificate."
	$cert = New-SelfSignedCertificate `
		-Subject $CertificateSubject `
		-CertStoreLocation Cert:CurrentUser\My `
		-KeyExportPolicy Exportable `
		-Type DocumentEncryptionCert `
		-KeyUsage DataEncipherment `
		-KeySpec KeyExchange
}

Write-Host "[AE] Connecting to SQL Server '$ServerName' / Database '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30"

try {
	$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop
}
catch {
	Write-Error "Failed to connect to '$ServerName' database '$DatabaseName'. Verify instance name SQL2025, database existence, and local permissions."
	throw
}

Write-Host "[AE] Creating CMK settings from certificate thumbprint"
$cmkSettings = New-SqlCertificateStoreColumnMasterKeySettings -CertificateStoreLocation "CurrentUser" -Thumbprint $cert.Thumbprint -AllowEnclaveComputations

Write-Host "[AE] Ensuring CMK '$CmkName' exists"
$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] Ensuring CEK '$CekName' exists"
$existingCek = Get-SqlColumnEncryptionKey -InputObject $database | Where-Object { $_.Name -eq $CekName }
if (-not $existingCek) {
	New-SqlColumnEncryptionKey -Name $CekName -InputObject $database -ColumnMasterKey $CmkName | Out-Null
}

Write-Host "Completed successfully"

例 - Azure Key Vault を使用してエンクレーブ対応キーをプロビジョニングする

以下のエンドツーエンドの例では、Azure Key Vault のキー コンテナーに列マスター キーを格納して、エンクレーブ対応キーをプロビジョニングする方法を示します。 スクリプトは、「Azure Key Vault 例 (役割の分離なし)」の例が基になっています。 エンクレーブ対応のキーとエンクレーブ対応ではないキーのワークフローでの 2 つの違いに注意することが重要です。

  • 次のスクリプトの New-SqlCertificateStoreColumnMasterKeySettings では、-AllowEnclaveComputations パラメーターを使用して、新しい列マスター キーがエンクレーブ対応にされています。
  • 次のスクリプトでは、 Get-AzAccessToken コマンドレットを使用して、キー コンテナーのアクセス トークンを取得します。 これが必要なのは、New-SqlAzureKeyVaultColumnMasterKeySettings が列マスター キーのプロパティへの署名が行われるために、Azure Key Vault にアクセスする必要があるためです。
param(
	[Parameter(Mandatory = $true)] [string]$SubscriptionId,
	[Parameter(Mandatory = $true)] [string]$ResourceGroupName,
	[Parameter(Mandatory = $true)] [string]$AzureLocation,
	[Parameter(Mandatory = $true)] [string]$KeyVaultName,
	[Parameter(Mandatory = $true)] [string]$KeyName,
	[Parameter(Mandatory = $true)] [string]$ServerName,
	[Parameter(Mandatory = $true)] [string]$DatabaseName,
	[string]$CmkName = "CMK",
	[string]$CekName = "CEK",
	[bool]$AssignRbacToCurrentPrincipal = $true
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

Import-Module Az.Accounts -ErrorAction Stop
Import-Module Az.Resources -ErrorAction Stop
Import-Module Az.KeyVault -ErrorAction Stop
Import-Module SqlServer -ErrorAction Stop

function Get-CurrentPrincipalObjectId {
	param([string]$AccountId)

	$userSignedIn = Get-AzADUser -SignedIn -ErrorAction SilentlyContinue
	if ($userSignedIn) { return $userSignedIn.Id }

	$user = Get-AzADUser -UserPrincipalName $AccountId -ErrorAction SilentlyContinue
	if ($user) { return $user.Id }

	$sp = Get-AzADServicePrincipal -DisplayName $AccountId -ErrorAction SilentlyContinue | Select-Object -First 1
	if ($sp) { return $sp.Id }

	throw "Could not resolve Microsoft Entra object id for account '$AccountId'."
}

try {
	Write-Host "[AE] Signing in and selecting subscription"
	Connect-AzAccount | Out-Null
	$ctx = Set-AzContext -SubscriptionId $SubscriptionId

	Write-Host "[AE] Ensuring resource group exists"
	$resourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
	if (-not $resourceGroup) {
		$resourceGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $AzureLocation
	}

	Write-Host "[AE] Ensuring key vault exists (RBAC mode)"
	$vault = Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -ErrorAction SilentlyContinue
	if (-not $vault) {
		$vault = New-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -Location $AzureLocation -EnableRbacAuthorization
	}

	if (-not $vault.EnableRbacAuthorization) {
		throw "Key Vault '$KeyVaultName' is not using RBAC authorization. Enable RBAC authorization on the vault before running this script."
	}

	if ($AssignRbacToCurrentPrincipal) {
		Write-Host "[AE] Ensuring RBAC role assignment"
		$principalSignInName = $ctx.Account.Id
		$roleName = "Key Vault Crypto Officer"
		$existingRole = Get-AzRoleAssignment -SignInName $principalSignInName -Scope $vault.ResourceId -RoleDefinitionName $roleName -ErrorAction SilentlyContinue
		if (-not $existingRole) {
			New-AzRoleAssignment -SignInName $principalSignInName -Scope $vault.ResourceId -RoleDefinitionName $roleName | Out-Null
		}
	}

	Write-Host "[AE] Ensuring column master key material exists in Key Vault"
	$akvKey = Get-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName -ErrorAction SilentlyContinue
	if (-not $akvKey) {
		$akvKey = Add-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName -Destination "Software"
	}

	Write-Host "[AE] Connecting to Azure SQL and creating metadata"
	$keyVaultAccessToken = (Get-AzAccessToken -ResourceUrl "https://vault.azure.net").Token
	$connStr = "Server=tcp:$ServerName.database.windows.net,1433;Database=$DatabaseName;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Authentication=Active Directory Interactive"
	$database = Get-SqlDatabase -ConnectionString $connStr -Encrypt Mandatory
	$cmkSettings = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyUrl $akvKey.Key.Kid -AllowEnclaveComputations

	$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
	}

	Write-Host "Completed successfully"
}
catch {
	Write-Error "Script failed: $($_.Exception.Message)"
	throw
}