SQL Server / Azure SQL Database では、Always Encrypted という暗号化の機能があります。
この機能は、列マスターキー (CMK) と列暗号化キー (CEK) という 2 種類の暗号化のためのキーを使用して、データの暗号化を行います。
データは常に暗号化された状態で取り扱われ、暗号化を指定した列についてはディスク / メモリ / ネットワーク上で常に暗号化された状態となり、適切な暗号化キーにアクセスができない状態では、SQL Server の管理者であったとして、暗号化されたデータを複合化することができないという特徴があります。
本ブログでもこの機能について触れていたことはありますが、改めて思い出す必要がありましたので情報を残しておきたいと思います。
Contents
Always Encrypted のチュートリアル
Always Encrypted がどのような機能であるかを確認する際にはチュートリアルが公開されています。
CMK としてローカルの証明書ストアを使用したチュートリアルとなりますが機能を使用するためには、この内容を一度実施してみるとよいかと思います。
CMK と CEK
Always Encrypted を久しぶりに触ると CMK と CEK の役割がよくわからなくなってきます…。
これらのキーについては、次の情報で内容を確認することができます。
CEK は実際にデータを暗号化する際に使用するキーとなり、SQL Server 内のデータの暗号化はこのキーを使用して実行されており、複合化する際にも CEK が必要となります。CEK については SQL Server のユーザーデータベース内に保存が行われます。
CMK は CEK の暗号化に使用される CEK を保護するために使用するキーとなり、CEK を使用するためには CMK にアクセスができる必要があります。CMK のエントリは SQL Server 内に保存されますが、実体については SQL Server 外に保存され、証明書ストアや Key Vault を使用することができます。(PC のローカルの証明書ストアを使用するのではなく、Yubikey に証明書を保存して使用するということも構成としては可能です)
Azure Key Vault で列マスター キーを作成する の方法で、Key Vault に Always Encrypted の CMK を格納することができ、Key Vault にアクセスができないユーザーでは CEK によって暗号化されたデータにアクセスができないという制限を行うことができます。
Key Vault を使用したアプリケーションの実装例
アプリケーションで Key Vault を使用した CMK へのアクセスを行う場合の情報となりまます。
Always Encrypted の Key Vault サポートは、カスタムキーストアプロバイダーを使用して実装されており、次の情報でアナウンスが行われました。
- Using the Azure Key Vault Key Store Provider for Always Encrypted
- Creating Custom Key Store Providers for Always Encrypted (Azure Key Vault Example)
もう少し踏み込んだ実装については、次の情報から確認することができます。
- Always Encrypted での Azure Key Vault プロバイダーの使用を示す例
- Configuring Always Encrypted on Azure SQL by using Azure Key Vault and Entity Framework Core
- AzureKeyVaultProviderExample
- Azure.Identity 名前空間
SqlColumnEncryptionAzureKeyVaultProvider を SqlConnection.RegisterColumnEncryptionKeyStoreProviders で登録することで、Key Vault に格納された CMK を使用することができるようになります。
ENCRYPTED_VALUE の生成
前述のとおり、Always Encrypted は CMK と CEK の二つの暗号化キーが使用されます。
CMK については CREATE COLUMN MASTER KEY で作成することができ、個の構文は CMK のプロバイダー / パスを指定するシンプルなものとなります。
CEK の作成については、CREATE COLUMN ENCRYPTION KEY で作成することができますが、CMK より作成の方法が複雑です。
CEK を作成する際には、CMK を指定する必要がありますが、CMK の指定だけでなく「ENCRYPTED_VALUE」として列暗号化キーのバイナリを指定する必要があります。
SSMS から設定を実施する場合は、ENCRYPTED_VALUE の生成は SSMS 内で実行されるため意識する必要が無いのですが、SSMS が使用できない環境やクエリ / コマンドベースで CEK を作成する場合には自分で生成する必要があります。
生成の方法ですが次の情報が参考となります。
- Always On Encrypted ? Generating Certificates and Column Encryption Key ENCRYPTED_VALUE
- New-SqlColumnEncryptionKeyEncryptedValue
機能がリリースされた当初は、コマンドレットが提供されていなかったと思うのですが、現在は New-SqlColumnEncryptionKeyEncryptedValue というコマンドレットが提供されていますので、このコマンドレットを使用して生成を行うのが良いのではないでしょうか。
CMK を作成したら、次のコマンドを実行することで、ENCRYPTED_VALUE を生成することができます。
Connect-AzAccount -UseDeviceAuthentication $keyVaulUrl="https://xxxxxx.vault.azure.net/keys/Always-Encrypted-Auto1/xxxxxxx" $cmkKeyVaultAccessToken?=?(Get-AzAccessToken?-ResourceUrl?https://vault.azure.net).Token $cmkSetting = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyUrl $keyVaulUrl -KeyVaultAccessToken $cmkKeyVaultAccessToken? New-SqlColumnEncryptionKeyEncryptedValue ` -TargetColumnMasterKeySettings $cmkSetting -KeyVaultAccessToken $cmkKeyVaultAccessToken | Set-Clipboard
生成された値を CREATE CLOMUN ENCRYPTION KEY の ENCRYPTED_VALUE に指定することで、SSMS GUI の CEK の生成機能を使用せずに CEK を作成することが可能となります。
CMK のローテーション
CMK にはローテーション (回転) という仕組みがあります。これは新しい CMK を使用するように、CEK の情報を更新する処理となります。
前述のとおり、実際のデータの暗号化は CEK を使用して実行されていますので、CMK を変更しても既存のデータに対して再暗号化は発生しないという認識でいます。
CMK のローテーション操作については次のドキュメントで確認することができます。
- SQL Server Management Studio を使用して Always Encrypted キーを交換する
- PowerShell を使用して Always Encrypted キーをローテーションする
CEK には、最大 二つの CMK を設定することができるようになっており、CMK のローテーションとは ALTER COLUMN ENCRYPTION KEY を使用して、CEK に新しい CMK を追加するという処理となります。
具体的には次のクエリのように既存の CEK に対して新しい CMK と ENCRYPTED_VALUE を追加する T-SQL の実行が必要となります。
ALTER COLUMN ENCRYPTION KEY [CEK] ADD VALUE ( COLUMN_MASTER_KEY = [CMK-KeyVault-02], ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0xXXXXXXXXXXXXXXXX )
SSMS の GUI でローテーション (回転) を実行する場合には、作成時と同様に GUI が自動的に ENCRYPTED_VALUE が生成されます。
手動で生成する場合には、PowerShell を使用して Always Encrypted キーをローテーションする の方法を参考にしながら ENCRYPTED_VALUE を生成する必要があります。
新しく追加する CMK 用の ENCRYPTED_VALUE は作成時に指定した ENCRYPTED_VALUE を使用して生成する必要があります。
作成した際の ENCRYPTED_VALUE が残っていればよいのですが、この値はプレーンテキストで残しておくことは推奨されていないため、次のようなスクリプトで CEK から情報を確認するのがよさそうでした
Connect-AzAccount -UseDeviceAuthentication $connStr = "Server=xxxxxx; Database = AEDB; User=xxxxxx; Password=xxxxxx;TrustServerCertificate=True" $database = Get-SqlDatabase -ConnectionString $connStr $cekName = "CEK" $cek = Get-SqlColumnEncryptionKey -InputObject $database -Name $cekName $encryptedValue = $cek.ColumnEncryptionKeyValues[0].EncryptedValueAsSqlBinaryString $oldKeyVaulUrl="https://xxxxxxxx.vault.azure.net/keys/Always-Encrypted-Auto1/xxxxxxxxxxxx" $oldCmkKeyVaultAccessToken?=?(Get-AzAccessToken?-ResourceUrl?https://vault.azure.net).Token $oldCmkSetting = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyUrl $oldKeyVaulUrl -KeyVaultAccessToken $oldCmkKeyVaultAccessToken? $newKeyVaulUrl="https://xxxxxxxx.vault.azure.net/keys/Always-Encrypted-Auto1/xxxxxxxxxxxx" $newCmkKeyVaultAccessToken?=?(Get-AzAccessToken?-ResourceUrl?https://vault.azure.net).Token $newCmkSetting = New-SqlAzureKeyVaultColumnMasterKeySettings -KeyUrl $newKeyVaulUrl -KeyVaultAccessToken $newCmkKeyVaultAccessToken? New-SqlColumnEncryptionKeyEncryptedValue ` -TargetColumnMasterKeySettings $newCmkSetting ` -ColumnMasterKeySettings $oldCmkSetting ` -KeyVaultAccessToken $newCmkKeyVaultAccessToken ` -EncryptedValue $encryptedValue | Set-Clipboard
これで新しい CMK を追加するための ENCRYPTED_VALUE を生成できますので、生成した値を ALTER COLUMN ENCRIPTION KEY に指定することでローテーションのための CMK を追加することができます。
この状態では、CEK に CMK は二つ登録された状態となり、動作を見ていた限りはどちらか一方の CMK にアクセスができれば CEK の利用はできるようでした。ローテーションにより新しくキーを追加した際には、新しいキーに対してアクセスが行われていることを確認したほうがよさそうですね。
ローテーションが終わったら「クリーンアップの実行を検討する必要があります。これは、ALTER COLUMN ENCRYPTION KEY を DROP で使用して CEK から CMK を削除する対応となります。