Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A következőkre vonatkozik:SQL Server
Azure SQL Database
Felügyelt Azure SQL-példány
Ez a cikk az Always Encrypted kulcsainak az SqlServer PowerShell-modullal történő forgatásának lépéseit ismerteti. Az Always Encryptedhez készült SqlServer PowerShell-modul használatának megkezdéséről további információt az Always Encrypted konfigurálása a PowerShell használatával című témakörben talál.
Jegyzet
Microsoft a PowerShell 7 vagy újabb verzióját javasolja Always Encrypted PowerShell-szkriptek futtatásakor. A PowerShell 7 továbbfejlesztett platformfüggetlen támogatást, jobb teljesítményt és a legújabb kompatibilitást biztosít az SqlServer modullal (v22+), amely számos Always Encrypted-forgatókönyvhöz szükséges.
Az Always Encrypted kulcsok elforgatása a meglévő kulcsok újra cserélésének folyamata. Előfordulhat, hogy el kell forgatnia egy kulcsot, ha az sérült, vagy meg kell felelnie a szervezet szabályzatainak vagy megfelelőségi előírásainak, amelyek rendszeres titkosítási kulcsváltást tesznek szükségessé.
Az Always Encrypted kétféle kulcstípust használ, ezért két magas szintű kulcsforgatási munkafolyamat létezik; az oszlop főkulcsának elforgatása és az oszloptitkosítási kulcsok elforgatása.
- Oszloptitkosítási kulcs rotálása – az aktuális kulccsal titkosított adatok visszafejtésével és az új oszloptitkosítási kulccsal történő újratitkosításával jár. Mivel az oszloptitkosítási kulcsok elforgatásához mind a kulcsokhoz, mind az adatbázishoz hozzá kell férni, az oszloptitkosítási kulcs elforgatása csak szerepkör-elkülönítés nélkül végezhető el.
- Oszlop főkulcsának elforgatása – az aktuális oszlop főkulcsával védett oszloptitkosítási kulcsok visszafejtésével, az új oszlop főkulcsával történő újratitkosításával, valamint a metaadatok mindkét típusú kulcs esetében történő frissítésével jár. Az oszlop főkulcs-elforgatása szerepkör-elkülönítéssel vagy anélkül is elvégezhető (az SqlServer PowerShell-modul használatakor).
Oszlop főkulcsának elforgatása szerepkör-elkülönítés nélkül
Az oszlop főkulcsának elforgatásához ebben a szakaszban ismertetett módszer nem támogatja a biztonsági rendszergazda és a DBA közötti szerepkör-elkülönítést. Az alábbi lépések némelyike egyesíti a fizikai kulcsok műveleteit a kulcs metaadatain végzett műveletekkel. Ezt a munkafolyamatot akkor használja, ha a szervezet a DevOps-modellt használja, vagy ha az adatbázis a felhőben van üzemeltetve, és az elsődleges cél az, hogy korlátozza a felhőgazdák (de nem a helyszíni adatbázis-üzemeltetők) hozzáférését a bizalmas adatokhoz. Ne használja ezt a módszert, ha a lehetséges támadók adatbázis-kezelőket is tartalmazhatnak, vagy ha a dbA-k nem férhetnek hozzá bizalmas adatokhoz.
| Feladat | Cikk | Szövegformátumú kulcsok/keystore-ok elérése | Adatbázis elérése |
|---|---|---|---|
| 1. lépés. Hozzon létre egy új oszlop főkulcsát egy kulcstárolóban. Jegyzet: Az SqlServer PowerShell-modul nem támogatja ezt a lépést. A feladat parancssorból történő végrehajtásához a kulcstárolóhoz tartozó eszközöket kell használnia. Ha az Azure Key Vaultot használja kulcstárolóként, a több-bérlős ügyfél által felügyelt kulcsváltás nem támogatott. Győződjön meg arról, hogy az új ügyfél által kezelt kulcs ugyanabban a bérlőben van, mint a meglévő. |
Oszlop főkulcsainak létrehozása és tárolása az Always Encryptedhez | Igen | Nem |
| 2. lépés. PowerShell-környezet indítása és az SqlServer modul importálása | Az SqlServer modul importálása | Nem | Nem |
| 3. lépés. Csatlakozzon a kiszolgálóhoz és az adatbázishoz. | Csatlakozás adatbázishoz | Nem | Igen |
| 4. lépés. Hozzon létre egy SqlColumnMasterKeySettings objektumot, amely információkat tartalmaz az új oszlop főkulcsának helyéről. Az SqlColumnMasterKeySettings egy olyan objektum, amely a memóriában (a PowerShellben) létezik. A létrehozásához a kulcstárolóra jellemző parancsmagot kell használnia. |
Új-SqlAzureKeyVaultOszlopFőKulcsBeállítások Új-SqlTanúsítványTárolóOszlopFőkulcsBeállítások Új-SqlCngOszlopMesterKulcsBeállítások Új-SqlCspOszlopMesterKulcsBeállítások |
Nem | Nem |
| 5. lépés. Hozza létre az új oszlop főkulcsának metaadatait az adatbázisban. |
New-SqlColumnMasterKey Megjegyzés: A borítók alatt ez a parancsmag kiadja a CREATE COLUMN MASTER KEY (Transact-SQL) utasítást a kulcs metaadatainak létrehozásához. |
Nem | Igen |
| 6. lépés. Hitelesítés az Azure-ban, ha az aktuális oszlop főkulcsa vagy az új oszlop főkulcsa kulcstartóban vagy felügyelt HSM-ben van tárolva az Azure Key Vaultban | Connect-AzAccount | Igen | Nem |
| 7. lépés. Szerezze be az Azure Key Vaultok hozzáférési jogkivonatát, ha az oszlop főkulcsa az Azure Key Vaultban van tárolva. | Get-AzAccessToken | Nem | Nem |
| 8. lépés. Indítsa el a forgatási folyamatot azzal, hogy az egyes oszloptitkosítási kulcsokat titkosítja, amelyek a régi oszlop főkulccsal vannak védve, az új oszlop főkulcs használatával. A lépés után az egyes érintett oszloptitkosítási kulcsok (amelyek a forgatás alatt álló régi oszlop főkulcshoz tartoznak) mind a régi, mind az új oszlop főkulccsal titkosítva vannak, és két titkosított értékkel rendelkeznek az adatbázis metaadataiban. | Invoke-SqlColumnMasterKeyRotation | Igen | Igen |
| 9. lépés. Az adatbázis titkosított oszlopait lekérdező (és a régi oszlop főkulcsával védett) alkalmazások rendszergazdáival együttműködve biztosíthatják, hogy az alkalmazások hozzáférhessenek az új oszlop főkulcsához. | Oszlop főkulcsának létrehozása és tárolása (Mindig titkosított) | Igen | Nem |
| 10. lépés. A forgatás befejezése Megjegyzés: A lépés végrehajtása előtt győződjön meg arról, hogy a régi oszlop főkulcsával védett titkosított oszlopokat lekérdező összes alkalmazás konfigurálva lett az új oszlopmintakulcs használatára. Ha ezt a lépést idő előtt hajtja végre, előfordulhat, hogy néhány alkalmazás nem tudja visszafejteni az adatokat. Végezze el a forgatást úgy, hogy eltávolítja a titkosított értékeket a régi oszlop főkulcsával létrehozott adatbázisból. Ez eltávolítja a régi oszlop főkulcsa és az általa védett oszloptitkosítási kulcsok közötti társításokat. |
Complete-SqlColumnMasterKeyRotation | Nem | Igen |
| 11. lépés Távolítsa el a metaadatokat a régi oszlop főkulcsából. | Távolítsa el az SqlColumnMasterKey kulcsát | Nem | Igen |
Jegyzet
Erősen ajánlott, hogy a forgatás után ne törölje véglegesen a régi oszlop főkulcsát. Ehelyett a régi oszlop főkulcsát a jelenlegi kulcstárolójában kell tartania, vagy egy másik biztonságos helyen kell archiválnia. Ha egy biztonságimásolat-fájlból visszaállítja az adatbázist az új oszlop főkulcsának konfigurálása előtti időpontra, az adatok eléréséhez szüksége lesz a régi kulcsra.
Egy oszlop főkulcsának forgatása szerepkör különválasztása nélkül (Windows-tanúsítvány példa)
Az alábbi szkript egy teljes példa, amely lecserél egy meglévő oszlop mesterkulcsot (CMK1) egy új oszlop mesterkulcsra (CMK2).
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$ServerName = '<server name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$DatabaseName = '<database name>',
[Parameter(Mandatory = $false)]
[string]$CertificateSubject = 'AlwaysEncryptedCertNew',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$OldCmkName = 'CMK1',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$NewCmkName = 'CMK2'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop
Write-Host '[AE] Step 1: Creating a new self-signed certificate for the new CMK'
$cert = New-SelfSignedCertificate `
-Subject $CertificateSubject `
-CertStoreLocation 'Cert:CurrentUser\My' `
-KeyExportPolicy Exportable `
-Type DocumentEncryptionCert `
-KeyUsage KeyEncipherment `
-KeySpec KeyExchange `
-KeyLength 2048
Write-Host "[AE] Certificate created with thumbprint: $($cert.Thumbprint)"
Write-Host "[AE] Step 2: 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' / '$DatabaseName'. Verify instance, database, and local permissions."
throw
}
Write-Host "[AE] Step 3: Validating that old CMK '$OldCmkName' exists"
$oldCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $OldCmkName }
if (-not $oldCmk) {
throw "Old CMK '$OldCmkName' does not exist. Cannot rotate."
}
Write-Host "[AE] Old CMK '$OldCmkName' found."
Write-Host "[AE] Step 4: Creating CMK settings for new certificate"
$newCmkSettings = New-SqlCertificateStoreColumnMasterKeySettings -CertificateStoreLocation 'CurrentUser' -Thumbprint $cert.Thumbprint
Write-Host "[AE] Step 5: Registering new CMK '$NewCmkName' in the database"
$newCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $NewCmkName }
if ($newCmk) {
Write-Host "[AE] New CMK '$NewCmkName' already exists. Skipping creation."
}
else {
New-SqlColumnMasterKey -Name $NewCmkName -InputObject $database -ColumnMasterKeySettings $newCmkSettings | Out-Null
Write-Host "[AE] New CMK '$NewCmkName' registered."
}
Write-Host "[AE] Step 6: Initiating CMK rotation from '$OldCmkName' to '$NewCmkName'"
Write-Host "[AE] (This re-encrypts all associated CEKs under the new CMK...)"
Invoke-SqlColumnMasterKeyRotation `
-SourceColumnMasterKeyName $OldCmkName `
-TargetColumnMasterKeyName $NewCmkName `
-InputObject $database
Write-Host "[AE] Rotation initiated."
Write-Host "[AE] Step 7: Completing the CMK rotation"
Complete-SqlColumnMasterKeyRotation `
-SourceColumnMasterKeyName $OldCmkName `
-InputObject $database
Write-Host "[AE] Rotation completed."
Write-Host "[AE] Step 8: Verifying CEKs are now under '$NewCmkName'"
$query = "SELECT name FROM sys.column_encryption_keys WHERE name = N'$($NewCmkName)'"
$rotatedCeks = Invoke-SqlCmd -ServerInstance $ServerName -Database $DatabaseName -Query $query -TrustServerCertificate -ErrorAction SilentlyContinue
if ($rotatedCeks) {
$cekCount = @($rotatedCeks).Count
if ($cekCount -eq 0) { $cekCount = 1 }
Write-Host "[AE] Verified: $cekCount CEK(s) now under '$NewCmkName'"
@($rotatedCeks) | ForEach-Object { Write-Host " - $($_.name)" }
}
Write-Host "[AE] Step 9: Removing old CMK metadata '$OldCmkName'"
Remove-SqlColumnMasterKey -Name $OldCmkName -InputObject $database
Write-Host "[AE] Old CMK '$OldCmkName' removed."
Write-Host '[AE] ========== Rotation Complete =========='
Write-Host "[AE] Old CMK: $OldCmkName (deleted)"
Write-Host "[AE] New CMK: $NewCmkName (active)"
Write-Host '[AE] All CEKs have been re-encrypted under the new CMK.'
Oszlop főkulcsának cseréje szerepkör-elkülönítéssel
Az ebben a szakaszban ismertetett főkulcs-rotációs munkafolyamat biztosítja a biztonsági rendszergazda és a DBA elkülönítését.
Fontos
Mielőtt végrehajtaná az alábbi táblázatban szereplő azon lépéseket, amelyek hozzáférnek a szövegesen tárolt kulcsokhoz vagy a kulcstárolóhoz=Yes értéket kap, győződjön meg róla, hogy a PowerShell környezet egy olyan biztonságos gépen fut, amely különbözik az adatbázist futtató számítógéptől. További információ: A kulcskezelés biztonsági szempontjai.
1. rész: DBA
A DBA lekéri az elforgatni kívánt oszlop főkulcsának metaadatait, valamint az aktuális oszlop főkulcsához társított érintett oszloptitkosítási kulcsokat. A DBA ezeket az információkat egy biztonsági rendszergazdával osztja meg.
| Feladat | Cikk | Szövegformátumú kulcsok/keystore-ok elérése | Adatbázis elérése |
|---|---|---|---|
| 1. lépés. Indítsa el a PowerShell-környezetet, és importálja az SqlServer modult. | Az SqlServer modul importálása | Nem | Egyik sem |
| 2. lépés. Csatlakozzon a kiszolgálóhoz és egy adatbázishoz. | Csatlakozás adatbázishoz | Nem | Igen |
| 3. lépés. Kérje le a régi oszlop főkulcsának metaadatait. | Get-SqlColumnMasterKey | Nem | Igen |
| 4. lépés. Kérje le az oszloptitkosítási kulcsok metaadatait, amelyeket a régi oszlop főkulcsa véd, beleértve azok titkosított értékeit is. | Get-SqlColumnEncryptionKey | Nem | Igen |
| 5. lépés. Ossza meg az oszlop főkulcsának helyét (az oszlop főkulcsának szolgáltatónevét és kulcsútvonalát) és a megfelelő oszloptitkosítási kulcsok titkosított értékeit, amelyek a régi oszlop főkulcsával védettek. | Lásd az alábbi példákat. | Nem | Nem |
2. rész: Biztonsági rendszergazda
A biztonsági rendszergazda létrehoz egy új oszlop-főkulcsot, újra titkosítja az érintett oszloptitkosítási kulcsokat az új oszloptitkosítási kulccsal, és megosztja az új oszlop főkulcsával, valamint az érintett oszloptitkosítási kulcsok új titkosított értékeinek készletével kapcsolatos információkat a DBA-val.
| Feladat | Cikk | Egyszerű szöveges kulcsok/keystore elérése | Adatbázis elérése |
|---|---|---|---|
| 1. lépés. Szerezze be a régi oszlop főkulcsának helyét és a megfelelő oszloptitkosítási kulcsok titkosított értékeit, amelyek a régi oszlop főkulcsával védettek a DBA-ból. | Nincs adat. Lásd az alábbi példákat. |
Nem | Nem |
| 2. lépés. Hozzon létre egy új oszlop főkulcsát egy kulcstárolóban. Jegyzet: Az SqlServer modul nem támogatja ezt a lépést. Ha parancssorból szeretné elvégezni ezt a feladatot, a kulcstároló típusának megfelelő eszközöket kell használnia. Ha az Azure Key Vaultot használja kulcstárolóként, a több-bérlős ügyfél által felügyelt kulcsváltás nem támogatott. Győződjön meg arról, hogy az új ügyfél által kezelt kulcs ugyanabban a bérlőben van, mint a meglévő. |
Oszlop főkulcsainak létrehozása és tárolása az Always Encryptedhez | Igen | Nem |
| 3. lépés. Indítsa el a PowerShell-környezetet, és importálja az SqlServer modult. | Az SqlServer modul importálása | Nem | Nem |
| 4. lépés. Hozzon létre egy SqlColumnMasterKeySettings objektumot, amely információkat tartalmaz a régi oszlop főkulcsának helyéről. Az SqlColumnMasterKeySettings egy olyan objektum, amely a memóriában (a PowerShellben) létezik. | Új-SqlOszlopfőkulcsBeállítások | Nem | Nem |
| 5. lépés. Hozzon létre egy SqlColumnMasterKeySettings objektumot, amely információkat tartalmaz az új oszlop főkulcsának helyéről. Az SqlColumnMasterKeySettings egy olyan objektum, amely a memóriában (a PowerShellben) létezik. A létrehozásához a kulcstárolóra jellemző parancsmagot kell használnia. |
Új-SqlAzureKeyVaultOszlopFőKulcsBeállítások Új-SqlTanúsítványTárolóOszlopFőkulcsBeállítások Új-SqlCngOszlopMesterKulcsBeállítások Új-SqlCspOszlopMesterKulcsBeállítások |
Nem | Nem |
| 6. lépés. Hitelesítés az Azure-ban, ha a régi (jelenlegi) oszlop főkulcsa vagy az új oszlop főkulcsa kulcstartóban vagy felügyelt HSM-ben van tárolva az Azure Key Vaultban. | Connect-AzAccount | Igen | Nem |
| 7. lépés. Szerezze be az Azure Key Vaultok hozzáférési jogkivonatát, ha az oszlop főkulcsa az Azure Key Vaultban van tárolva. | Get-AzAccessToken | Nem | Nem |
| 8. lépés. Az új oszloptitkosítási kulccsal újratitkosíthatja az oszloptitkosítási kulcs minden értékét, amely jelenleg a régi oszlop főkulcsával van védve. |
New-SqlColumnEncryptionKeyEncryptedValue Jegyzet: A parancsmag meghívásakor adja át az SqlColumnMasterKeySettings objektumokat a régi és az új oszlop főkulcsához, valamint az oszloptitkosítási kulcs értékét, hogy újra titkosítva legyen. |
Igen | Nem |
| 9. lépés. Ossza meg az új oszlop főkulcsának helyét (az oszlop főkulcsának szolgáltatónevét és kulcsútvonalát) és az oszloptitkosítási kulcsok új titkosított értékeinek készletét a DBA-val. | Lásd az alábbi példákat. | Nem | Nem |
Jegyzet
Erősen ajánlott, hogy a forgatás után ne törölje véglegesen a régi oszlop főkulcsát. Ehelyett a régi oszlop főkulcsát a jelenlegi kulcstárolójában kell tartania, vagy egy másik biztonságos helyen kell archiválnia. Ha egy biztonságimásolat-fájlból visszaállítja az adatbázist az új oszlop főkulcsának konfigurálása előtti időpontra, az adatok eléréséhez szüksége lesz a régi kulcsra.
3. rész: DBA
A DBA metaadatokat hoz létre az új oszlop főkulcsához, és frissíti az érintett oszloptitkosítási kulcsok metaadatait az új titkosított értékek hozzáadásához. Ebben a lépésben a DBA egyeztet a titkosítási oszlopokat lekérdező alkalmazások rendszergazdáival is, akik biztosítják, hogy az alkalmazás hozzáférhessen az új oszlop főkulcsához. Miután minden alkalmazás be van állítva az új oszlop főkulcsának használatára, a DBA eltávolítja a régi titkosított értékeket és a régi oszlop főkulcs-metaadatait.
| Feladat | Cikk | Egyszerű szöveges kulcsok/keystore elérése | Adatbázis elérése |
|---|---|---|---|
| 1. lépés. Szerezze be az új oszlop főkulcsának helyét és a megfelelő oszloptitkosítási kulcsok új, a régi oszlop főkulcsával védett titkosított értékeit a biztonsági rendszergazdától. | Lásd az alábbi példákat. | Nem | Nem |
| 2. lépés. Indítsa el a PowerShell-környezetet, és importálja az SqlServer modult. | Az SqlServer modul importálása | Nem | Nem |
| 3. lépés. Csatlakozzon a kiszolgálóhoz és egy adatbázishoz. | Csatlakozás adatbázishoz | Nem | Igen |
| 4. lépés. Hozzon létre egy SqlColumnMasterKeySettings objektumot, amely információkat tartalmaz az új oszlop főkulcsának helyéről. Az SqlColumnMasterKeySettings egy olyan objektum, amely a memóriában (a PowerShellben) létezik. | Új-SqlOszlopfőkulcsBeállítások | Nem | Nem |
| 5. lépés. Hozza létre az új oszlop főkulcsának metaadatait az adatbázisban. |
New-SqlColumnMasterKey Megjegyzés: A háttérben ez a parancsmag a kulcsmetaadatok létrehozásához kiadja a CREATE COLUMN MASTER KEY (Transact-SQL) utasítást. |
Nem | Igen |
| 6. lépés. Kérje le a metaadatokat az oszloptitkosítási kulcsokról, amelyeket a régi oszlop főkulcsa véd. | Get-SqlColumnEncryptionKey | Nem | Igen |
| 7. lépés. Adjon hozzá egy új (az új oszlop főkulcsával előállított) titkosított értéket az egyes érintett oszloptitkosítási kulcsok metaadataihoz. | Add-SqlOszlopTitkosításKulcsÉrték | Nem | Igen |
| 8. lépés. Az adatbázis titkosított oszlopait lekérdező (és a régi oszlop főkulcsával védett) alkalmazások rendszergazdáival együttműködve biztosíthatják, hogy az alkalmazások hozzáférhessenek az új oszlop főkulcsához. | Oszlop főkulcsainak létrehozása és tárolása (Mindig titkosított) | Nem | Nem |
| 9. lépés. A forgatás befejezéséhez távolítsa el a régi oszlop főkulcsához társított titkosított értékeket az adatbázisból. Jegyzet: A lépés végrehajtása előtt győződjön meg arról, hogy minden olyan alkalmazás, amely a régi oszlop főkulcsával védett titkosított oszlopokat kérdez le, konfigurálva legyen az új oszlopmintakulcs használatára. Ha ezt a lépést idő előtt hajtja végre, előfordulhat, hogy néhány alkalmazás nem tudja visszafejteni az adatokat. Ez a lépés eltávolítja a régi oszlop főkulcsa és az általa védett oszloptitkosítási kulcsok közötti társításokat. |
Complete-SqlColumnMasterKeyRotation Másik lehetőségként a Remove-SqlColumnEncryptionKeyValue parancsot is használhatja |
Nem | Igen |
| 10. lépés. A régi oszlop főkulcs-metaadatainak eltávolítása az adatbázisból | Távolítsa el az SqlColumnMasterKey kulcsát | Nem | Igen |
Oszlop főkulcsa forgatása szerepkörök szétválasztásával (a Windows-tanúsítvány példáján keresztül)
Az alábbi szkript egy átfogó példa arra, hogyan lehet egy új oszlop főkulcsot generálni, amely tanúsítványként szerepel a Windows Tanúsítványtárolóban. Ez magában foglalja egy meglévő (jelenlegi) oszlop főkulcsának kicserélését az új oszlop főkulcsra. A szkript feltételezi, hogy a céladatbázis tartalmazza a CMK1 (forgatandó) nevű oszlop főkulcsát, amely titkosít néhány oszloptitkosítási kulcsot.
1. rész: DBA
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$ServerName = '<server name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$DatabaseName = '<database name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$OldCmkName = 'CMK2',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$OutputFolder = 'C:\temp'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop
Write-Host "[CEK Export] Starting CMK and CEK data export"
# Validate output folder
if (-not (Test-Path -Path $OutputFolder -PathType Container)) {
Write-Host "[CEK Export] Creating output folder: $OutputFolder"
New-Item -Path $OutputFolder -ItemType Directory | Out-Null
}
# Connect to database
Write-Host "[CEK Export] Connecting to '$ServerName' / '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30"
try {
$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop
}
catch {
Write-Error "Failed to connect to '$ServerName' / '$DatabaseName'."
throw
}
# Retrieve old CMK
Write-Host "[CEK Export] Retrieving CMK '$OldCmkName'"
$oldCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $OldCmkName }
if (-not $oldCmk) {
throw "CMK '$OldCmkName' not found in database '$DatabaseName'."
}
# Export CMK metadata using fixed text file name
$cmkFile = Join-Path $OutputFolder "oldcmkdata.txt"
Write-Host "[CEK Export] Exporting CMK metadata to: $cmkFile"
"CMKName|KeyStoreProviderName|KeyPath" | Set-Content -Path $cmkFile -Encoding UTF8
"$OldCmkName|$($oldCmk.KeyStoreProviderName)|$($oldCmk.KeyPath)" | Add-Content -Path $cmkFile -Encoding UTF8
Write-Host "[CEK Export] ✓ CMK metadata exported"
# Discover and export CEKs using fixed text file name
Write-Host "[CEK Export] Discovering CEKs associated with '$OldCmkName'"
$ceks = Get-SqlColumnEncryptionKey -InputObject $database
$cekFile = Join-Path $OutputFolder "oldcekvalues.txt"
"CEKName|CEKEncryptedValue|HasMultipleEncryptedValues" | Set-Content -Path $cekFile -Encoding UTF8
$exportedCount = 0
$multiValueCount = 0
foreach ($cek in $ceks) {
if (-not $cek.ColumnEncryptionKeyValues) {
continue
}
# Check if this CEK has multiple encrypted values
if ($cek.ColumnEncryptionKeyValues.Count -gt 1) {
# CEK has multiple encrypted values - check if any reference the old CMK
$refersToOldCmk = $cek.ColumnEncryptionKeyValues | Where-Object { $_.ColumnMasterKeyName -eq $OldCmkName }
if ($refersToOldCmk) {
Write-Warning "CEK '$($cek.Name)' has $($cek.ColumnEncryptionKeyValues.Count) encrypted values. One references '$OldCmkName'. This CEK cannot be rotated automatically."
"$($cek.Name)|MULTIPLE_ENCRYPTED_VALUES|True" | Add-Content -Path $cekFile -Encoding UTF8
$multiValueCount++
}
}
else {
# CEK has single encrypted value - check if it references the old CMK
if ($cek.ColumnEncryptionKeyValues[0].ColumnMasterKeyName -eq $OldCmkName) {
$encryptedValueHex = "0x" + -join ($cek.ColumnEncryptionKeyValues[0].EncryptedValue | ForEach-Object { $_.ToString("X2") })
"$($cek.Name)|$encryptedValueHex|False" | Add-Content -Path $cekFile -Encoding UTF8
$exportedCount++
}
}
}
Write-Host "[CEK Export] ✓ CEK encrypted values exported"
Write-Host "[CEK Export] - Exported: $exportedCount CEK(s)"
if ($multiValueCount -gt 0) {
Write-Warning " - Multi-valued CEKs (manual review needed): $multiValueCount"
}
Write-Host "[CEK Export] ===== Export Complete ====="
Write-Host "[CEK Export] CMK Metadata: $cmkFile"
Write-Host "[CEK Export] CEK Values: $cekFile"
2. rész: Biztonsági rendszergazda
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$ShareFolder = 'C:\Temp\',
[Parameter(Mandatory = $false)]
[ValidateSet('CurrentUser', 'LocalMachine')]
[string]$StoreLocation = 'CurrentUser',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$CertificateSubject = 'AlwaysEncryptedCert'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop
function Import-DelimitedTextFile {
param(
[Parameter(Mandatory = $true)] [string]$Path,
[Parameter(Mandatory = $true)] [string[]]$RequiredColumns
)
if (-not (Test-Path -Path $Path -PathType Leaf)) {
throw "Required file not found: $Path"
}
$raw = Get-Content -Path $Path -Raw
if ([string]::IsNullOrWhiteSpace($raw)) {
throw "File is empty: $Path"
}
$delimiter = if ($raw -match '\|') { '|' } else { ',' }
$rows = @(Import-Csv -Path $Path -Delimiter $delimiter)
if ($rows.Count -eq 0) {
throw "No data rows found in file: $Path"
}
$first = $rows[0]
$RequiredColumns | ForEach-Object {
if (-not $first.PSObject.Properties[$_]) {
throw "Missing required column '$_' in file: $Path"
}
}
return $rows
}
if (-not (Test-Path -Path $ShareFolder -PathType Container)) {
throw "Share folder does not exist: $ShareFolder"
}
$oldCmkDataFile = Join-Path $ShareFolder 'oldcmkdata.txt'
$oldCekValuesFile = Join-Path $ShareFolder 'oldcekvalues.txt'
$newCmkDataFile = Join-Path $ShareFolder 'newcmkdata.txt'
$newCekValuesFile = Join-Path $ShareFolder 'newcekvalues.txt'
Write-Host "[AE] Reading old CMK data from '$oldCmkDataFile'"
$oldCmkDataRows = Import-DelimitedTextFile -Path $oldCmkDataFile -RequiredColumns @('KeyStoreProviderName', 'KeyPath')
$oldCmkData = $oldCmkDataRows[0]
Write-Host "[AE] Reading old CEK values from '$oldCekValuesFile'"
$oldCekValues = Import-DelimitedTextFile -Path $oldCekValuesFile -RequiredColumns @('CEKName', 'CEKEncryptedValue')
Write-Host "[AE] Finding or creating certificate '$CertificateSubject' in $StoreLocation\\My"
$certPath = "Cert:$StoreLocation\My"
$cert = Get-ChildItem -Path $certPath |
Where-Object { $_.Subject -eq "CN=$CertificateSubject" } |
Sort-Object NotAfter -Descending |
Select-Object -First 1
if (-not $cert) {
$cert = New-SelfSignedCertificate `
-Subject $CertificateSubject `
-CertStoreLocation $certPath `
-KeyExportPolicy Exportable `
-Type DocumentEncryptionCert `
-KeyUsage DataEncipherment `
-KeySpec KeyExchange
}
Write-Host '[AE] Building CMK settings'
$oldCmkSettings = New-SqlColumnMasterKeySettings `
-KeyStoreProviderName $oldCmkData.KeyStoreProviderName `
-KeyPath $oldCmkData.KeyPath
$newCmkSettings = New-SqlCertificateStoreColumnMasterKeySettings `
-CertificateStoreLocation $StoreLocation `
-Thumbprint $cert.Thumbprint
Write-Host "[AE] Re-encrypting CEK values and writing '$newCekValuesFile'"
"CEKName|CEKEncryptedValue" | Set-Content -Path $newCekValuesFile -Encoding UTF8
$oldCekValues | ForEach-Object {
$newValue = New-SqlColumnEncryptionKeyEncryptedValue `
-TargetColumnMasterKeySettings $newCmkSettings `
-ColumnMasterKeySettings $oldCmkSettings `
-EncryptedValue $_.CEKEncryptedValue
"$($_.CEKName)|$newValue" | Add-Content -Path $newCekValuesFile -Encoding UTF8
}
Write-Host "[AE] Writing new CMK data to '$newCmkDataFile'"
"KeyStoreProviderName|KeyPath" | Set-Content -Path $newCmkDataFile -Encoding UTF8
"$($newCmkSettings.KeyStoreProviderName)|$($newCmkSettings.KeyPath)" | Add-Content -Path $newCmkDataFile -Encoding UTF8
Write-Host '[AE] Completed successfully'
Write-Host "[AE] Output files: $newCmkDataFile , $newCekValuesFile"
3. rész: DBA
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$ServerName = '<server name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$DatabaseName = '<database name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$OldCmkName = 'CMK1',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$NewCmkName = 'CMK2',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$InputFolder = 'C:\temp'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop
function Import-DelimitedTextFile {
param(
[Parameter(Mandatory = $true)] [string]$Path,
[Parameter(Mandatory = $true)] [string[]]$RequiredColumns
)
if (-not (Test-Path -Path $Path -PathType Leaf)) {
throw "Required file not found: $Path"
}
$raw = Get-Content -Path $Path -Raw
if ([string]::IsNullOrWhiteSpace($raw)) {
throw "File is empty: $Path"
}
$delimiter = if ($raw -match '\|') { '|' } else { ',' }
$rows = @(Import-Csv -Path $Path -Delimiter $delimiter)
if ($rows.Count -eq 0) {
throw "No data rows found in file: $Path"
}
$first = $rows[0]
$RequiredColumns | ForEach-Object {
if (-not $first.PSObject.Properties[$_]) {
throw "Missing required column '$_' in file: $Path"
}
}
return $rows
}
if (-not (Test-Path -Path $InputFolder -PathType Container)) {
throw "Input folder not found: $InputFolder"
}
$newCmkDataFile = Join-Path $InputFolder 'newcmkdata.txt'
$newCekValuesFile = Join-Path $InputFolder 'newcekvalues.txt'
Write-Host "[AE] Reading new CMK data from '$newCmkDataFile'"
$newCmkRows = Import-DelimitedTextFile -Path $newCmkDataFile -RequiredColumns @('KeyStoreProviderName', 'KeyPath')
$newCmkData = $newCmkRows[0]
Write-Host "[AE] Reading new CEK values from '$newCekValuesFile'"
$newCekValues = Import-DelimitedTextFile -Path $newCekValuesFile -RequiredColumns @('CEKName', 'CEKEncryptedValue')
Write-Host "[AE] Connecting to '$ServerName' / '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30"
$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop
Write-Host "[AE] Ensuring target CMK '$NewCmkName' exists"
$newCmkSettings = New-SqlColumnMasterKeySettings -KeyStoreProviderName $newCmkData.KeyStoreProviderName -KeyPath $newCmkData.KeyPath
$existingNewCmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $NewCmkName }
if (-not $existingNewCmk) {
New-SqlColumnMasterKey -Name $NewCmkName -InputObject $database -ColumnMasterKeySettings $newCmkSettings | Out-Null
}
Write-Host "[AE] Adding new encrypted CEK values under '$NewCmkName'"
$ceks = Get-SqlColumnEncryptionKey -InputObject $database
$ceksToRotate = @(
$ceks | Where-Object {
$_.ColumnEncryptionKeyValues -and
@($_.ColumnEncryptionKeyValues | Where-Object { $_.ColumnMasterKeyName -eq $OldCmkName }).Count -gt 0
}
)
$ceksToRotate | ForEach-Object {
$cek = $_
if (@($cek.ColumnEncryptionKeyValues).Count -gt 1) {
throw "CEK '$($cek.Name)' already has multiple encrypted values and still references '$OldCmkName'."
}
$newValueRow = @($newCekValues | Where-Object { $_.CEKName -eq $cek.Name }) | Select-Object -First 1
if (-not $newValueRow) {
throw "No new encrypted value found for CEK '$($cek.Name)' in file '$newCekValuesFile'."
}
Add-SqlColumnEncryptionKeyValue `
-ColumnMasterKeyName $NewCmkName `
-Name $cek.Name `
-EncryptedValue $newValueRow.CEKEncryptedValue `
-InputObject $database | Out-Null
}
Write-Host "[AE] Completing rotation for source CMK '$OldCmkName'"
Complete-SqlColumnMasterKeyRotation -SourceColumnMasterKeyName $OldCmkName -InputObject $database
Write-Host "[AE] Removing source CMK '$OldCmkName' metadata"
Remove-SqlColumnMasterKey -Name $OldCmkName -InputObject $database
Write-Host '[AE] Completed successfully'
Oszloptitkosítási kulcs elforgatása
Az oszloptitkosítási kulcs elforgatása során az összes oszlop adatainak visszafejtése történik az elforgatandó kulccsal, majd az adatok újratitkosítása az új oszloptitkosítási kulccsal. Ez a rotációs munkafolyamat a kulcsokhoz és az adatbázishoz is hozzáférést igényel, ezért szerepkör-elkülönítéssel nem hajtható végre. Az oszloptitkosítási kulcsok elforgatása hosszú időt vehet igénybe, ha a kulccsal titkosított oszlopokat tartalmazó táblák nagyok. Ezért a szervezetnek körültekintően kell megterveznie az oszloptitkosítási kulcs rotálását.
Az oszloptitkosítási kulcsot offline vagy online módon is elforgathatja. A korábbi módszer valószínűleg gyorsabb lesz, de az alkalmazások nem tudnak írni az érintett táblákba. Az utóbbi megközelítés valószínűleg hosszabb időt vesz igénybe, de korlátozhatja az időintervallumot, amely alatt az érintett táblák nem érhetők el az alkalmazások számára. További információért lásd: Oszloptitkosítás konfigurálása a PowerShell Always Encrypted használatával és a Set-SqlColumnEncryption használatával.
| Feladat | Cikk | Szövegformátumú kulcsok/keystore-ok elérése | Adatbázis elérése |
|---|---|---|---|
| 1. lépés. Indítsa el a PowerShell-környezetet, és importálja az SqlServer modult. | Az SqlServer modul importálása | Nem | Nem |
| 2. lépés. Csatlakozzon a kiszolgálóhoz és egy adatbázishoz. | Csatlakozás adatbázishoz | Nem | Igen |
| 3. lépés. Jelentkezzen be az Azure-ba, ha az oszlop főkulcsa, amely az elforgatandó oszloptitkosítási kulcsot védi, az Azure Key Vault kulcstartójában vagy egy felügyelt HSM-ben van tárolva. | Connect-AzAccount | Igen | Nem |
| 4. lépés. Szerezze be az Azure Key Vaultok hozzáférési jogkivonatát, ha az oszlop főkulcsa az Azure Key Vaultban van tárolva. | Get-AzAccessToken | Nem | Nem |
| 5. lépés. Hozzon létre egy új oszloptitkosítási kulcsot, titkosítsa az oszlop főkulcsával, és hozzon létre oszloptitkosítási kulcs metaadatait az adatbázisban. |
New-SqlColumnEncryptionKey Jegyzet: Használja a parancsmag egy olyan változatát, amely belsőleg létrehoz és titkosít egy oszloptitkosítási kulcsot. A háttérben ez a parancsmag a CREATE COLUMN ENCRYPTION KEY (Transact-SQL) utasítást hajtja végre a kulcs metaadatainak létrehozásához. |
Igen | Igen |
| 6. lépés. Keresse meg a régi oszloptitkosítási kulccsal titkosított összes oszlopot. | SQL Server Management Objects (SMO) programozási útmutató | Nem | Igen |
| 7. lépés. Hozzon létre egy SqlColumnEncryptionSettings objektumot minden érintett oszlophoz. Az SqlColumnEncryptionSettings egy olyan objektum, amely a memóriában (a PowerShellben) létezik. Egy oszlop céltitkosítási sémájának megadása. Ebben az esetben az objektumnak meg kell adnia, hogy az érintett oszlopot az új oszloptitkosítási kulcs használatával kell titkosítani. | New-SqlColumnEncryptionSettings | Nem | Nem |
| 8. lépés. Az 5. lépésben azonosított oszlopok újratitkosítása az új oszloptitkosítási kulccsal. |
Set-SqlColumnEncryption Jegyzet: Ez a lépés hosszú időt vehet igénybe. Az alkalmazások a választott megközelítéstől (online vagy offline) függően nem tudják elérni a táblákat a teljes művelettel vagy annak egy részével. |
Igen | Igen |
| 9. lépés. Távolítsa el a régi oszloptitkosítási kulcs metaadatait. | Remove-SqlColumnEncryptionKey | Nem | Igen |
Példa – Oszloptitkosítási kulcs elforgatása
Az alábbi szkript egy oszloptitkosítási kulcs elforgatását mutatja be. A szkript feltételezi, hogy a céladatbázis tartalmaz néhány oszloptitkosítási kulccsal titkosított, CEK1 (forgatandó) nevű oszloptitkosítási kulcsot, amelyet cmK1 nevű oszlop-főkulcs véd (az oszlop főkulcsa nem az Azure Key Vaultban van tárolva).
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$ServerName = '<server name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$DatabaseName = '<database name>',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$OldCekName = 'CEK1',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$NewCekName = 'CEK2',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$CmkName = 'CMK2',
[Parameter(Mandatory = $false)]
[ValidateRange(0, 3600)]
[int]$MaxDowntimeInSeconds = 120,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$LogFileDirectory = '.'
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Import-Module SqlServer -MinimumVersion 22.0.50 -ErrorAction Stop
if ($OldCekName -eq $NewCekName) {
throw 'OldCekName and NewCekName must be different.'
}
if (-not (Test-Path -Path $LogFileDirectory -PathType Container)) {
New-Item -Path $LogFileDirectory -ItemType Directory | Out-Null
}
Write-Host "[AE] Connecting to '$ServerName' / '$DatabaseName'"
$connStr = "Server=$ServerName;Database=$DatabaseName;Integrated Security=True;TrustServerCertificate=True;Connection Timeout=30"
$database = Get-SqlDatabase -ConnectionString $connStr -ErrorAction Stop
Write-Host "[AE] Ensuring CMK '$CmkName' exists"
$cmk = Get-SqlColumnMasterKey -InputObject $database | Where-Object { $_.Name -eq $CmkName }
if (-not $cmk) {
throw "Column master key '$CmkName' was not found."
}
Write-Host "[AE] Ensuring target CEK '$NewCekName' exists"
$existingNewCek = Get-SqlColumnEncryptionKey -InputObject $database | Where-Object { $_.Name -eq $NewCekName }
if (-not $existingNewCek) {
New-SqlColumnEncryptionKey -Name $NewCekName -InputObject $database -ColumnMasterKey $CmkName | Out-Null
}
Write-Host "[AE] Discovering encrypted columns using '$OldCekName'"
$settings = @()
$tables = @($database.Tables)
$tables | ForEach-Object {
$table = $_
@($table.Columns) | ForEach-Object {
$column = $_
if ($column.IsEncrypted -and $column.ColumnEncryptionKeyName -eq $OldCekName) {
$columnName = "{0}.{1}.{2}" -f $table.Schema, $table.Name, $column.Name
$settings += New-SqlColumnEncryptionSettings -ColumnName $columnName -EncryptionType $column.EncryptionType -EncryptionKey $NewCekName
}
}
}
if ($settings.Count -eq 0) {
Write-Warning "No encrypted columns found that reference '$OldCekName'. Nothing to rotate."
return
}
Write-Host "[AE] Re-encrypting $($settings.Count) column(s) to '$NewCekName'"
Set-SqlColumnEncryption `
-ColumnEncryptionSettings $settings `
-InputObject $database `
-UseOnlineApproach `
-MaxDowntimeInSeconds $MaxDowntimeInSeconds `
-LogFileDirectory $LogFileDirectory
Write-Host "[AE] Validating no columns still reference '$OldCekName'"
$stillUsingOld = $false
@($database.Tables) | ForEach-Object {
@($_.Columns) | ForEach-Object {
if ($_.IsEncrypted -and $_.ColumnEncryptionKeyName -eq $OldCekName) {
$stillUsingOld = $true
}
}
}
if ($stillUsingOld) {
throw "At least one encrypted column still references '$OldCekName'. Aborting CEK removal."
}
Write-Host "[AE] Removing old CEK '$OldCekName'"
Remove-SqlColumnEncryptionKey -Name $OldCekName -InputObject $database
Write-Host '[AE] Completed successfully'
Következő lépések
- Oszlopok lekérdezése az Always Encrypted használatával az SQL Server Management Studióval
- Alkalmazások fejlesztése az Always Encrypted használatával
Lásd még:
- Mindig titkosítva
- Az Always Encrypted kulcskezelésének áttekintése
- Always Encrypted konfigurálása a PowerShell használatával
- Always Encrypted kulcsok elforgatása az SQL Server Management Studióval
- CREATE COLUMN MASTER KEY (Transact-SQL)
- DROP COLUMN MASTER KEY (Transact-SQL)
- CREATE COLUMN ENCRYPTION KEY (Transact-SQL)
- ALTER COLUMN ENCRYPTION KEY (Transact-SQL)
- DROP COLUMN ENCRYPTION KEY (Transact-SQL)
- sys.column_master_keys (Transact-SQL)
- sys.oszlop_titkosítási_kulcsok (Transact-SQL)