Azure VM 上的 BitLocker 启动错误
适用于:✔️ Windows VM
本文介绍在 Microsoft Azure 中启动 Windows 虚拟机 (VM) 时可能遇到的 BitLocker 错误。
症状
Windows VM 不启动。 检查启动诊断窗口中的屏幕截图时,看到以下错误消息之一:
插入含 BitLocker 密钥的 USB 驱动程序
你被锁定! 输入恢复密钥,以便再次开始操作(键盘布局:美式键盘)错误登录信息输入次数过多,因此,你的 PC 被锁定以保护你的隐私。 若要检索恢复密钥,请从另一电脑或移动设备转到 https://windows.microsoft.com/recoverykeyfaq。 如果需要,密钥 ID 为 XXXXXXX。 或者,可以重置电脑。
输入密码以解锁此驱动器 [ ] 按 Insert 键在键入时查看密码。
输入恢复密钥 从 USB 设备加载恢复密钥。
原因
如果 VM 找不到用于解密加密磁盘的 BitLocker 恢复密钥 (BEK) 文件,则可能会出现此问题。
解密加密 OS 磁盘
提示
如果 VM 有最近的备份,可以尝试 从备份 还原 VM 以修复启动问题。
若要解决此问题,请停止并解除分配 VM,然后启动它。 此操作将强制 VM 从 Azure Key Vault 中检索 BEK 文件,然后将其放在加密磁盘上。
如果此方法未能解决此问题,请执行以下步骤,手动还原 BEK 文件:
拍摄受影响的 VM 的 OS 磁盘的快照作为备份。 有关详细信息,请参阅拍摄磁盘快照。
将 OS 磁盘附加到恢复 VM。 附加托管磁盘时,可能会收到“包含加密设置,因此不能用作数据磁盘”错误消息。 在此情况下,运行以下脚本,重试附加磁盘:
$rgName = "myResourceGroup" $osDiskName = "ProblemOsDisk" # Set the EncryptionSettingsEnabled property to false, so you can attach the disk to the recovery VM. New-AzDiskUpdateConfig -EncryptionSettingsEnabled $false |Update-AzDisk -diskName $osDiskName -ResourceGroupName $rgName $recoveryVMName = "myRecoveryVM" $recoveryVMRG = "RecoveryVMRG" $OSDisk = Get-AzDisk -ResourceGroupName $rgName -DiskName $osDiskName; $vm = get-AzVM -ResourceGroupName $recoveryVMRG -Name $recoveryVMName Add-AzVMDataDisk -VM $vm -Name $osDiskName -ManagedDiskId $osDisk.Id -Caching None -Lun 3 -CreateOption Attach Update-AzVM -VM $vm -ResourceGroupName $recoveryVMRG
不能将托管磁盘附加到从 Blob 映像还原的 VM。
附加磁盘后,与恢复 VM 建立远程桌面连接。
打开提升的 Azure PowerShell 会话(以管理员身份运行)。 运行以下命令来登录到 Azure 订阅:
Add-AzAccount -SubscriptionID [SubscriptionID]
运行以下脚本以检查 BEK 文件的名称(机密名称):
$vmName = "myVM" $vault = "myKeyVault" Get-AzKeyVaultSecret -VaultName $vault | where {($_.Tags.MachineName -eq $vmName) -and ($_.ContentType -match 'BEK')} ` | Sort-Object -Property Created ` | ft Created, ` @{Label="Content Type";Expression={$_.ContentType}}, ` @{Label ="MachineName"; Expression = {$_.Tags.MachineName}}, ` @{Label ="Volume"; Expression = {$_.Tags.VolumeLetter}}, ` @{Label ="DiskEncryptionKeyFileName"; Expression = {$_.Tags.DiskEncryptionKeyFileName}}
下面是输出的示例。 在本例中,我们假定文件名为 EF7B2F5A-50C6-4637-0001-7F599C12F85C。BEK。
Created Content Type Volume MachineName DiskEncryptionKeyFileName ------- ------------ ------ ----------- ------------------------- 11/20/2020 7:41:56 AM BEK C:\ myVM EF7B2F5A-50C6-4637-0001-7F599C12F85C.BEK
如果看到两个重复的卷,具有较新时间戳的卷为恢复 VM 使用的当前 BEK 文件。
如果“内容类型”值为“包装的 BEK”,请转到密钥加密密钥 (KEK) 方案。
获取驱动器的 BEK 文件名称后,须创建 secret-file-name.BEK 文件以解锁驱动器。
将 BEK 文件下载到恢复磁盘。 以下示例将 BEK 文件保存到 C:\BEK 文件夹。 运行脚本前,请确保
C:\BEK\
路径存在。$vault = "myKeyVault" $bek = "EF7B2F5A-50C6-4637-0001-7F599C12F85C" $keyVaultSecret = Get-AzKeyVaultSecret -VaultName $vault -Name $bek $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($keyVaultSecret.SecretValue) $bekSecretBase64 = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) $bekFileBytes = [Convert]::FromBase64String($bekSecretbase64) $path = "C:\BEK\DiskEncryptionKeyFileName.BEK" [System.IO.File]::WriteAllBytes($path,$bekFileBytes)
若要使用 BEK 文件解锁附加的磁盘,请运行以下命令。
manage-bde -unlock F: -RecoveryKey "C:\BEK\EF7B2F5A-50C6-4637-0001-7F599C12F85C.BEK"
在此示例中,附加的 OS 磁盘为驱动器 F。请确保使用正确的驱动器号。
使用 BEK 密钥成功解锁磁盘后,从恢复 VM 分离磁盘,然后使用此新的 OS 磁盘重新创建 VM。
注意
交换 OS 磁盘适用于使用单通道 ADE 版本加密的任何 VM,但双通道不支持。
如果新 VM 仍无法正常启动,请在解锁驱动器后尝试以下步骤之一:
- 通过运行以下命令暂停保护以暂时关闭 BitLocker OFF:
manage-bde -protectors -disable F: -rc 0
- 完全解密驱动器。 为此,请运行以下命令:
manage-bde -off F:
密钥加密密钥方案 (包装 BEK)
对于密钥加密密钥方案,请执行以下步骤:
请确保登录的用户帐户需要“用户|密钥权限|加密操作|解包密钥”中 Key Vault 访问策略中的“解包”权限。
将以下脚本保存到 .PS1 文件:
注意
此脚本中使用的 ADAL 程序集(dll 文件)仅适用于 Az.Account 1.9.4 和早期版本。 若要安装 Az.Account 模块,请参阅 “安装 Az PowerShell 模块”。
#Set the Parameters for the script. If you have question about the Parameters, see the "KEK script parameters" section. param ( [Parameter(Mandatory=$true)] [string] $keyVaultName, [Parameter(Mandatory=$true)] [string] $kekName, [Parameter(Mandatory=$true)] [string] $secretName, [Parameter(Mandatory=$true)] [string] $bekFilePath, [Parameter(Mandatory=$true)] [string] $adTenant ) # Load ADAL Assemblies. If the ADAL Assemblies cannot be found, please see the "Install Az PowerShell module" section. $adal = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\1.9.4\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" If ((Test-Path -Path $adal) -and (Test-Path -Path $adalforms)) { [System.Reflection.Assembly]::LoadFrom($adal) [System.Reflection.Assembly]::LoadFrom($adalforms) } else { Write-output "ADAL Assemblies files cannot be found. Please set the correct path for `$adal` and `$adalforms`, then run the script again." exit } # Set well-known client ID for AzurePowerShell $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" # Set redirect URI for Azure PowerShell $redirectUri = "urn:ietf:wg:oauth:2.0:oob" # Set Resource URI to Azure Service Management API $resourceAppIdURI = "https://vault.azure.net" # Set Authority to Azure AD Tenant $authority = "https://login.windows.net/$adtenant" # Create Authentication Context tied to Azure AD Tenant $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority # Acquire token $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto" $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters).result # Generate auth header $authHeader = $authResult.CreateAuthorizationHeader() # Set HTTP request headers to include Authorization header $headers = @{'x-ms-version'='2014-08-01';"Authorization" = $authHeader} ######################################################################################################################## # 1. Retrieve wrapped BEK # 2. Make KeyVault REST API call to unwrap the BEK # 3. Convert the Base64Url string returned by KeyVault unwrap to Base64 string # 4. Convert Base64 string to bytes and write to the BEK file ######################################################################################################################## #Get wrapped BEK and place it in JSON object to send to KeyVault REST API $keyVaultSecret = Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($keyVaultSecret.SecretValue) $wrappedBekSecretBase64 = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) $jsonObject = @" { "alg": "RSA-OAEP", "value" : "$wrappedBekSecretBase64" } "@ #Get KEK Url $kekUrl = (Get-AzKeyVaultKey -VaultName $keyVaultName -Name $kekName).Key.Kid; $unwrapKeyRequestUrl = $kekUrl+ "/unwrapkey?api-version=2015-06-01"; #Call KeyVault REST API to Unwrap $result = Invoke-RestMethod -Method POST -Uri $unwrapKeyRequestUrl -Headers $headers -Body $jsonObject -ContentType "application/json" -Debug #Convert Base64Url string returned by KeyVault unwrap to Base64 string $base64UrlBek = $result.value; $base64Bek = $base64UrlBek.Replace('-', '+'); $base64Bek = $base64Bek.Replace('_', '/'); if($base64Bek.Length %4 -eq 2) { $base64Bek+= '=='; } elseif($base64Bek.Length %4 -eq 3) { $base64Bek+= '='; } #Convert base64 string to bytes and write to BEK file $bekFileBytes = [System.Convert]::FromBase64String($base64Bek); [System.IO.File]::WriteAllBytes($bekFilePath,$bekFileBytes) #Delete the key from the memory [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) clear-variable -name wrappedBekSecretBase64
设置参数。 该脚本处理 KEK 机密以创建 BEK 密钥,然后将其保存到恢复 VM 上的本地文件夹中。 如果在运行脚本时收到错误,请参阅 脚本故障排除 部分。
脚本开始时,将看到以下输出:
GAC 版本位置
False v4.0.30319 C:\Program Files\WindowsPowerShell\Modules\Az.Accounts...False v4.0.30319 C:\Program Files\WindowsPowerShell\Modules\Az.Accounts...
脚本完成后,将看到以下输出:
VERBOSE: POST https://myvault.vault.azure.net/keys/rondomkey/<KEY-ID>/unwrapkey?api- version=2015-06-01 with -1-byte payload VERBOSE: received 360-byte response of content type application/json; charset=utf-8
若要使用 BEK 文件解锁附加磁盘,请运行以下命令:
manage-bde -unlock F: -RecoveryKey "C:\BEK\EF7B2F5A-50C6-4637-9F13-7F599C12F85C.BEK
在此示例中,附加的 OS 磁盘为驱动器 F。请确保使用正确的驱动器号。
使用 BEK 密钥成功解锁磁盘后,请从恢复 VM 中分离磁盘,然后使用 交换 OS 磁盘 功能将原始 VM 的 OS 磁盘替换为此修复的磁盘。
如果新 VM 仍无法正常启动,请在解锁驱动器后尝试以下步骤之一:
- 通过运行以下命令暂停保护以暂时关闭 BitLocker:
manage-bde -protectors -disable F: -rc 0
- 完全解密驱动器。 为此,请运行以下命令:
manage-bde -off F:
脚本故障排除
错误:无法加载文件或程序集
发生此错误的原因是 ADAL 程序集的路径不正确。 可以搜索 Az.Accounts
文件夹以查找正确的路径。
错误:Get-AzKeyVaultSecret 或 Get-AzKeyVaultSecret 无法识别为 cmdlet 的名称
如果使用旧的 Az PowerShell 模块,则必须将两个 Get-AzureKeyVaultSecret
命令更改为和 Get-AzureKeyVaultSecret
。
KEK 脚本参数
参数 | 示例 | 如何检查 |
---|---|---|
$keyVaultName | myKeyVault2707 | 在输出中运行 Get-AzVM -ResourceGroupName $rgName -Name $vmName -DisplayHint Expand 并检查 “设置” 和 “KeyEncryptionKeyURL ”。 下面是一个示例:“KeyEncryptionKeyURL”: https://myKeyVault2707.vault.azure.net/keys/mykey/000072b987145a3b79b0ed415f0000 |
$kekName | mykey | 在输出中运行 Get-AzVM -ResourceGroupName $rgName -Name $vmName -DisplayHint expand 并检查 “设置” 和 “KeyEncryptionKeyURL ”。 下面是一个示例:“KeyEncryptionKeyURL”: https://myKeyVault2707.vault.azure.net/keys/mykey/000072b987145a3b79b0ed415f0000 |
$secretName | 7EB4F531-5FBA-4970-8E2D-C11FD6B0C69D | VM 密钥的机密的名称。 若要查找正确的机密名称,请查看“解密加密的 OS 磁盘”部分中的步骤 6。 |
$bekFilePath | c:\bek\7EB4F531-5FBA-4970-8E2D-C11FD6B0C69D。BEK | 要在其中保存 BEK 文件的本地路径。 在此示例中,需要在运行脚本之前创建“bek”文件夹,否则将出错。 |
$adTenant | contoso.onmicrosoft.com | 托管密钥保管库的 Microsoft Entra ID 的 FQDN 或 GUID |
安装 Az PowerShell 模块
若要为恢复 VM 安装 Az PowerShell 模块,请执行以下步骤:
以管理员身份打开 PowerShell 会话,并将当前会话的 HTTP API 安全协议设置为 TLS 1.2。 关闭当前会话后,安全协议将还原为默认值。
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
下载最新版本的 Nuget 包:
Install-PackageProvider -Name "Nuget" -Force
安装最新版本的 PowerShellGet 包,然后重启 PowerShell。
Install-Module -Name PowerShellGet -Force
运行以下命令以安装最新版本的 Azure Az 模块:
Install-Module -Name Az -Scope AllUsers -Repository PSGallery -Force
安装 Az.Account 1.9.4 包:
Install-Module -Name Az.Accounts -Scope AllUsers -RequiredVersion "1.9.4" -Repository PSGallery -Force
联系我们寻求帮助
如果你有任何疑问或需要帮助,请创建支持请求或联系 Azure 社区支持。 你还可以将产品反馈提交到 Azure 反馈社区。