Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Si applica a:SQL Server
Database SQL di
AzureIstanza gestita di SQL di Azure
In questo articolo sono descritti i passaggi per ruotare le chiavi per Always Encrypted usando il modulo PowerShell SqlServer. Per informazioni su come iniziare a usare il modulo SqlServer PowerShell per Always Encrypted, vedere Configurare Always Encrypted con PowerShell.
Nota
Microsoft consiglia di usare PowerShell 7 o versione successiva quando si eseguono script di PowerShell Always Encrypted. PowerShell 7 offre un supporto multipiattaforma migliorato, prestazioni migliori e la compatibilità più recente con il modulo SqlServer (v22+), necessario per molti scenari Always Encrypted.
La rotazione delle chiavi Always Encrypted è il processo di sostituzione di una chiave esistente con una nuova. Potrebbe essere necessario ruotare una chiave se è compromessa o rispettare i criteri o le normative di conformità dell'organizzazione che impongono una rotazione regolare delle chiavi crittografiche.
Always Encrypted usa due tipi di chiavi, quindi ci sono due flussi di lavoro principali per la rotazione delle chiavi: rotazione delle chiavi master della colonna e rotazione delle chiavi di crittografia della colonna.
- Rotazione della chiave di crittografia della colonna: comporta la decrittografia dei dati crittografati con la chiave corrente e la nuova crittografia dei dati usando la nuova chiave di crittografia della colonna. Dal momento che la rotazione di una chiave di crittografia della colonna richiede l'accesso alle chiavi e al database, questa può solo essere eseguita senza la separazione dei ruoli.
- Rotazione della chiave master della colonna: comporta la decrittografia delle chiavi di crittografia della colonna protette con la chiave master della colonna corrente, la nuova crittografia tramite la nuova chiave master della colonna e l'aggiornamento dei metadati per entrambi i tipi di chiavi. La rotazione delle chiavi master della colonna può essere completata con o senza la separazione dei ruoli (quando si usa il modulo PowerShell SqlServer).
Rotazione delle chiavi master di colonna senza separazione dei ruoli
Il metodo descritto in questa sezione per la rotazione di una chiave master della colonna non supporta la separazione dei ruoli tra un amministratore della sicurezza e un amministratore di database. Alcuni dei passaggi seguenti combinano operazioni sulle chiavi fisiche con operazioni sui metadati delle chiavi. Usare questo flusso di lavoro se l'organizzazione usa il modello DevOps o quando il database è ospitato nel cloud e l'obiettivo principale è quello di limitare gli amministratori cloud (ma non gli amministratori di database locali) ad accedere ai dati sensibili. Non usare questo metodo se tra i potenziali avversari rientrano i DBA o se i DBA non devono avere accesso ai dati sensibili.
| Attività | Articolo | Accede alle chiavi di testo non crittografato o all'archivio chiavi | Accede al database |
|---|---|---|---|
| Passaggio 1: Creare una nuova chiave master della colonna in un archivio chiavi. Nota: Il modulo SqlServer PowerShell non supporta questo passaggio. Per eseguire questa operazione dalla riga di comando, è necessario usare gli strumenti specifici per l'archivio chiavi. Quando si utilizza Azure Key Vault come archivio di chiavi, la rotazione delle chiavi gestite dai clienti multi-tenant non è supportata. Assicurarsi che la nuova chiave gestita dal cliente si trova nello stesso tenant di quello esistente. |
Creare e archiviare chiavi master delle colonne per Always Encrypted | Sì | NO |
| Passaggio 2. Avviare un ambiente PowerShell e importare il modulo SqlServer | Importare il modulo SqlServer | NO | NO |
| Passaggio 3. Connettersi al server e al database. | Connessione a un database | NO | Sì |
| Passaggio 4. Creare un oggetto SqlColumnMasterKeySettings che includa informazioni sul percorso della nuova chiave master della colonna. In PowerShell SqlColumnMasterKeySettings è un oggetto presente in memoria. Per crearlo, devi utilizzare il cmdlet specifico per il tuo archivio chiavi. |
Impostazioni della chiave master di colonna del Key Vault di SQL Azure Nuovo-SqlCertificateStoreColumnMasterKeyImpostazioni New-SqlCngColumnMasterKeySettings (Imposta nuove configurazioni per la chiave principale della colonna in SQL) New-SqlCspColumnMasterKeySettings |
NO | NO |
| Passaggio 5. Creare i metadati relativi alla nuova chiave master della colonna nel database. |
New-SqlColumnMasterKey Nota: questo cmdlet rilascia l'istruzione CREATE COLUMN MASTER KEY (Transact-SQL) per creare i metadati della chiave. |
NO | Sì |
| Passaggio 6. Autenticati in Azure, se la tua chiave master di colonna corrente o la nuova chiave master di colonna è archiviata in una cassaforte delle chiavi o in un HSM gestito all'interno di Azure Key Vault. | Connect-AzAccount | Sì | NO |
| Passaggio 7. Ottenere un token di accesso per Azure Key Vaults, se la chiave master della colonna è archiviata in Azure Key Vault. | Get-AzAccessToken | NO | NO |
| Passaggio 8. Inizia la rotazione, crittografando ciascuna delle chiavi di crittografia delle colonne, attualmente protette con la vecchia chiave master delle colonne, utilizzando la nuova chiave master delle colonne. Dopo questo passaggio ciascuna chiave di crittografia della colonna interessata (associata con la chiave master precedente della colonna durante la rotazione) è crittografata con la chiave master precedente e nuova della colonna e ha due valori crittografati nei metadati del database. | Invoke-SqlColumnMasterKeyRotation | Sì | Sì |
| Passaggio 9. Chiedere agli amministratori di tutte le applicazioni di eseguire query sulle colonne crittografate del database (protette con la chiave master precedente della colonna) per assicurarsi che possano accedere alla nuova chiave master della colonna. | Creare e archiviare chiavi master della colonna (Always Encrypted) | Sì | NO |
| Passaggio 10. Completare la rotazione Nota: prima di eseguire questo passaggio, assicurarsi che tutte le applicazioni che eseguono query su colonne crittografate protette con la chiave master precedente della colonna siano state configurate per l'uso della nuova chiave master della colonna. Se non si esegue questo passaggio in maniera completa, alcune applicazioni potrebbero non essere in grado di decrittografare i dati. Completare la rotazione rimuovendo i valori crittografati dal database creati con la precedente chiave master della colonna. Questa operazione rimuove l'associazione tra la vecchia chiave master della colonna e le chiavi di crittografia della colonna da essa protette. |
Complete-SqlColumnMasterKeyRotation | NO | Sì |
| Passaggio 11. Rimuovere i metadati dalla vecchia chiave master della colonna. | Remove-SqlColumnMasterKey | NO | Sì |
Nota
Si raccomanda vivamente di non eliminare definitivamente la chiave master della colonna precedente dopo la rotazione. Dovresti invece conservare la vecchia chiave master della colonna nel suo attuale archivio chiavi o archiviarla in un altro posto sicuro. Se si ripristina il database da un file di backup a un punto nel tempo prima della configurazione della nuova chiave master della colonna, sarà necessaria la chiave precedente per accedere ai dati.
Rotazione di una Chiave Master di Colonna senza la Separazione dei Ruoli (Esempio di Certificato Windows)
Lo script seguente è un esempio end-to-end che sostituisce una chiave master esistente della colonna (CMK1) con una nuova chiave master della colonna (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.'
Rotazione delle chiavi master della colonna con la separazione dei ruoli
Il flusso di lavoro della rotazione delle chiavi master della colonna descritto in questa sezione assicura la separazione dei ruoli tra un amministratore della sicurezza e un amministratore di database.
Importante
Prima di eseguire tutti i passaggi in cui accede alle chiavi di testo noncrittografato/archivio chiavi =Sì nella tabella seguente (passaggi che accedono alle chiavi di testo non crittografato o all'archivio chiavi), assicurarsi che l'ambiente Di PowerShell venga eseguito in un computer protetto diverso da un computer che ospita il database. Per altre informazioni, vedere Considerazioni sulla sicurezza per la gestione delle chiavi.
Parte 1: Amministratore di database
Un amministratore di database recupera metadati sulla chiave master della colonna da ruotare e sulle relative chiavi di crittografia associate alla chiave master della colonna corrente. L'amministratore di database condivide tutte queste informazioni con un amministratore della sicurezza.
| Attività | Articolo | Accede alle chiavi di testo non crittografato o all'archivio chiavi | Accede al database |
|---|---|---|---|
| Passaggio 1: Avviare un ambiente PowerShell e importare il modulo SqlServer. | Importare il modulo SqlServer | NO | Nessuno |
| Passaggio 2. Connettiti al tuo server e a un database. | Connettersi a un database | NO | Sì |
| Passaggio 3. Recupera i metadati sulla vecchia chiave master della colonna. | Get-SqlColumnMasterKey | NO | Sì |
| Passaggio 4. Recuperare i metadati sulle chiavi di crittografia di colonna, protetti dalla chiave master precedente della colonna, inclusi i relativi valori crittografati. | Get-SqlColumnEncryptionKey | NO | Sì |
| Passaggio 5. Condividere il percorso della chiave master della colonna (nome del provider e percorso della chiave master della colonna) e i valori crittografati delle chiavi di crittografia della colonna corrispondenti, protetti con la chiave master della colonna precedente. | Vedere gli esempi seguenti. | NO | NO |
Parte 2: Amministratore della sicurezza
L'amministratore della sicurezza genera una nuova chiave master della colonna, riesegue la crittografia delle chiavi di crittografia delle colonne interessate e condivide con l'amministratore di database le informazioni sulla nuova chiave master della colonna, nonché il set dei valori nuovi crittografati per le chiavi di crittografia delle colonne interessate.
| Attività | Articolo | Accede alle chiavi di testo non crittografato o all'archivio chiavi | Accede al database |
|---|---|---|---|
| Passaggio 1: Ottenere dall'amministratore del database il percorso della chiave master precedente della colonna e i valori crittografati delle chiavi di crittografia corrispondenti della colonna, protette con la chiave master precedente della colonna. | N/D Vedere gli esempi seguenti. |
NO | NO |
| Passaggio 2. Creare una nuova chiave master della colonna in un archivio chiavi. Nota: Il modulo SqlServer non supporta questo passaggio. Per eseguire questa attività da una riga di comando, è necessario usare gli strumenti specifici del tipo di archivio chiavi. Quando si utilizza Azure Key Vault come archivio di chiavi, la rotazione delle chiavi gestite dai clienti multi-tenant non è supportata. Assicurarsi che la nuova chiave gestita dal cliente si trova nello stesso tenant di quello esistente. |
Creare e archiviare chiavi master delle colonne per Always Encrypted | Sì | NO |
| Passaggio 3. Avviare un ambiente PowerShell e importare il modulo SqlServer. | Importare il modulo SqlServer | NO | NO |
| Passaggio 4. Creare un oggetto SqlColumnMasterKeySettings che contiene informazioni sulla posizione della chiave master della colonna precedente . In PowerShell SqlColumnMasterKeySettings è un oggetto presente in memoria. | New-SqlColumnMasterKeySettings | NO | NO |
| Passaggio 5. Creare un oggetto SqlColumnMasterKeySettings che contiene informazioni sulla posizione della nuova chiave master della colonna. In PowerShell SqlColumnMasterKeySettings è un oggetto presente in memoria. Per crearlo, devi utilizzare il cmdlet specifico per il tuo archivio chiavi. |
Impostazioni della chiave master di colonna del Key Vault di SQL Azure Nuovo-SqlCertificateStoreColumnMasterKeyImpostazioni New-SqlCngColumnMasterKeySettings (Imposta nuove configurazioni per la chiave principale della colonna in SQL) New-SqlCspColumnMasterKeySettings |
NO | NO |
| Passaggio 6. Autenticarsi su Azure, se la vecchia chiave master (corrente) o la nuova chiave master della colonna è archiviata in un archivio di chiavi o in un Modulo di sicurezza hardware (HSM) gestito in Azure Key Vault. | Connect-AzAccount | Sì | NO |
| Passaggio 7. Ottenere un token di accesso per Azure Key Vaults, se la chiave master della colonna è archiviata in Azure Key Vault. | Get-AzAccessToken | NO | NO |
| Passaggio 8. Crittografare nuovamente ogni valore della chiave di crittografia della colonna, attualmente protetta con la chiave master precedente della colonna, usando la nuova chiave master della colonna. |
New-SqlColumnEncryptionKeyEncryptedValue Nota: Quando si chiama questo cmdlet, passare gli oggetti SqlColumnMasterKeySettings sia per la chiave master precedente che per la nuova chiave master della colonna, insieme a un valore della chiave di crittografia della colonna, da crittografare nuovamente. |
Sì | NO |
| Passaggio 9. Condividere il percorso della nuova chiave master della colonna (nome del provider e percorso della chiave master della colonna) e il set di nuovi valori crittografati delle chiavi di crittografia della colonna con l'amministratore del database. | Vedere gli esempi seguenti. | NO | NO |
Nota
Si raccomanda vivamente di non eliminare definitivamente la chiave master della colonna precedente dopo la rotazione. Dovresti invece conservare la vecchia chiave master della colonna nel suo attuale archivio chiavi o archiviarla in un altro posto sicuro. Se si ripristina il database da un file di backup a un punto nel tempo prima della configurazione della nuova chiave master della colonna, sarà necessaria la chiave precedente per accedere ai dati.
Parte 3: Amministratore di database
L'amministratore di database crea metadati per la nuova chiave master della colonna e aggiorna i metadati delle chiavi di crittografia delle colonne interessate per aggiungere il set nuovo di valori crittografati. In questo passaggio, l'amministratore di database interagisce anche con gli amministratori delle applicazioni che eseguono query sulle colonne di crittografia, i quali assicurano che l'applicazione può accedere alla nuova chiave master della colonna. Dopo che tutte le applicazioni sono configurate per usare la nuova chiave master della colonna, l'amministratore di database rimuove il set precedente di valori crittografati e i metadati della chiave master precedente della colonna.
| Attività | Articolo | Accede alle chiavi di testo non crittografato o all'archivio chiavi | Accede al database |
|---|---|---|---|
| Passaggio 1: Ottenere dall'amministratore del database il percorso della nuova chiave master della colonna e il set nuovo di valori crittografati delle corrispondenti chiavi di crittografia della colonna, protette con la chiave master precedente della colonna. | Vedere gli esempi seguenti. | NO | NO |
| Passaggio 2. Avviare un ambiente PowerShell e importare il modulo SqlServer. | Importare il modulo SqlServer | NO | NO |
| Passaggio 3. Connettiti al tuo server e a un database. | Connessione a un database | NO | Sì |
| Passaggio 4. Creare un oggetto SqlColumnMasterKeySettings che includa informazioni sul percorso della nuova chiave master della colonna. In PowerShell SqlColumnMasterKeySettings è un oggetto presente in memoria. | New-SqlColumnMasterKeySettings | NO | NO |
| Passaggio 5. Creare i metadati relativi alla nuova chiave master della colonna nel database. |
New-SqlColumnMasterKey Nota: Dietro le quinte, questo cmdlet esegue l'istruzione CREATE COLUMN MASTER KEY (Transact-SQL) per creare i metadati della chiave. |
NO | Sì |
| Passaggio 6. Recuperare i metadati sulle chiavi di crittografia della colonna, protetti dalla chiave master precedente della colonna. | Get-SqlColumnEncryptionKey | NO | Sì |
| Passaggio 7. Aggiungere un valore crittografato nuovo (creato tramite la nuova chiave master della colonna) ai metadati di ogni chiave di crittografia della colonna interessata. | Aggiungi-SqlColumnEncryptionKeyValue | NO | Sì |
| Passaggio 8. Chiedere agli amministratori di tutte le applicazioni di eseguire query sulle colonne crittografate del database (protette con la chiave master precedente della colonna) per assicurarsi che possano accedere alla nuova chiave master della colonna. | Creazione e archiviazione di chiavi master della colonna (Always Encrypted) | NO | NO |
| Passaggio 9. Completare la rotazione rimuovendo dal database i valori crittografati associati con la chiave master precedente della colonna. Nota: Prima di eseguire questo passaggio, assicurarsi che tutte le applicazioni che eseguono query su colonne crittografate protette con la chiave master precedente della colonna siano state configurate per l'uso della nuova chiave master della colonna. Se non si esegue questo passaggio in maniera completa, alcune applicazioni potrebbero non essere in grado di decrittografare i dati. Questo passaggio rimuove l'associazione tra la chiave master precedente della colonna e le chiavi di crittografia della colonna da essa protetti. |
Complete-SqlColumnMasterKeyRotation In alternativa, è possibile usare Remove-SqlColumnEncryptionKeyValue |
NO | Sì |
| Passaggio 10. Rimuovere i metadati della vecchia chiave master della colonna dal database | Remove-SqlColumnMasterKey | NO | Sì |
Rotazione di una chiave master di colonna con separazione dei ruoli (esempio di certificato Windows)
Lo script seguente è un esempio end-to-end per la generazione di una nuova chiave master della colonna, certificata nell'archivio certificati di Windows, e per ruotare la chiave master attuale della colonna, sostituendola con la nuova chiave master della colonna. Questo script presuppone che il database di destinazione contenga la chiave master della colonna, denominata CMK1 (da ruotare), che crittografa alcune chiavi di crittografia della colonna.
Parte 1: Amministratore di database
[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"
Parte 2: Amministratore della sicurezza
[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"
Parte 3: Amministratore di database
[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'
Rotazione di una chiave di crittografia della colonna
La rotazione di una chiave di crittografia della colonna comporta la decrittografia dei dati in tutte le colonne crittografate con la chiave da ruotare e la successiva ri-crittografia dei dati utilizzando la nuova chiave di crittografia della colonna. Questo flusso di lavoro di rotazione richiede accesso alle chiavi e al database e quindi non può essere eseguito con la separazione dei ruoli. La rotazione di una chiave di crittografia della colonna può richiedere molto tempo, se le tabelle contenenti le colonne crittografate con la chiave da ruotare sono di grandi dimensioni. Pertanto, l'organizzazione deve pianificare con attenzione una rotazione della chiave di crittografia della colonna.
È possibile ruotare una chiave di crittografia della colonna usando un approccio online o offline. Il primo metodo è probabilmente il più veloce, ma le applicazioni non possono scrivere nelle tabelle interessate. L'ultimo approccio richiederà probabilmente più tempo, ma è possibile limitare l'intervallo di tempo durante il quale le tabelle interessate non sono disponibili per le applicazioni. Per altre informazioni, vedere Configurare la crittografia delle colonne usando Always Encrypted con PowerShell e Set-SqlColumnEncryption.
| Attività | Articolo | Accede alle chiavi di testo non crittografato o all'archivio chiavi | Accede al database |
|---|---|---|---|
| Passaggio 1: Avviare un ambiente PowerShell e importare il modulo SqlServer. | Importare il modulo SqlServer | NO | NO |
| Passaggio 2. Connettiti al tuo server e a un database. | Connessione a un database | NO | Sì |
| Passaggio 3. Eseguire l'autenticazione in Azure, se la chiave master della colonna (che protegge la chiave di crittografia della colonna da ruotare) è archiviata in un Key Vault o in un HSM gestito in Azure Key Vault. | Connect-AzAccount | Sì | NO |
| Passaggio 4. Ottenere un token di accesso per Azure Key Vaults, se la chiave master della colonna è archiviata in Azure Key Vault. | Get-AzAccessToken | NO | NO |
| Passaggio 5. Generare una nuova chiave di crittografia di colonna, crittografarla con la chiave master della colonna e creare i metadati della chiave di crittografia della colonna nel database. |
New-SqlColumnEncryptionKey Nota: Usare una variante del cmdlet che genera internamente e crittografa una chiave di crittografia della colonna. Dietro le quinte, questo cmdlet esegue l'istruzione CREATE COLUMN ENCRYPTION KEY (Transact-SQL) per creare i metadati della chiave. |
Sì | Sì |
| Passaggio 6. Trovare tutte le colonne crittografate con la vecchia chiave di crittografia della colonna. | Guida alla programmazione di SQL Server Management Objects (SMO) | NO | Sì |
| Passaggio 7. Creare un oggetto SqlColumnEncryptionSettings per ogni colonna interessata. SqlColumnEncryptionSettings è un oggetto esistente in memoria (in PowerShell). che specifica lo schema di crittografia di destinazione per una colonna. In questo caso, l'oggetto deve specificare che la colonna interessata deve essere crittografata con la nuova chiave di crittografia della colonna. | Impostazioni di Crittografia per Nuova Colonna SQL | NO | NO |
| Passaggio 8. Crittografare nuovamente le colonne, identificate nel passaggio 5, usando la nuova chiave di crittografia della colonna. |
Set-SqlColumnEncryption Nota: Questo passaggio può richiedere molto tempo. Le applicazioni non potranno accedere alle tabelle durante l'intera operazione o una parte di essa, a seconda dell'approccio (online o offline) selezionato. |
Sì | Sì |
| Passaggio 9. Rimuovere i metadati per la chiave di crittografia precedente della colonna. | Remove-SqlColumnEncryptionKey | NO | Sì |
Esempio: rotazione di una chiave di crittografia della colonna
Lo script seguente illustra la rotazione di una chiave di crittografia della colonna. Questo script presuppone che il database di destinazione contenga alcune colonne crittografate con una chiave di crittografia della colonna denominata CEK1 (da ruotare), che è protetta da una chiave master della colonna, denominata CMK1 (la chiave master della colonna non è archiviata in Azure Key Vault).
[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'
Passaggi successivi
- Esegui query sulle colonne utilizzando Always Encrypted con SQL Server Management Studio
- Sviluppare applicazioni con Always Encrypted
Vedi anche
- Always Encrypted
- Panoramica della gestione delle chiavi per Always Encrypted
- Configurare Always Encrypted con PowerShell
- Ruotare le chiavi Always Encrypted utilizzando SQL Server Management Studio
- 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.column_encryption_keys (Transact-SQL)