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 文件:

  1. 拍摄受影响的 VM 的 OS 磁盘的快照作为备份。 有关详细信息,请参阅拍摄磁盘快照

  2. 将 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。

  3. 附加磁盘后,与恢复 VM 建立远程桌面连接。

  4. 在恢复 VM 中安装 Az PowerShell 模块和 Az.Account 1.9.4

  5. 打开提升的 Azure PowerShell 会话(以管理员身份运行)。 运行以下命令来登录到 Azure 订阅:

    Add-AzAccount -SubscriptionID [SubscriptionID]
    
  6. 运行以下脚本以检查 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 文件以解锁驱动器。

  7. 将 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)
    
  8. 若要使用 BEK 文件解锁附加的磁盘,请运行以下命令。

    manage-bde -unlock F: -RecoveryKey "C:\BEK\EF7B2F5A-50C6-4637-0001-7F599C12F85C.BEK"
    

    在此示例中,附加的 OS 磁盘为驱动器 F。请确保使用正确的驱动器号。

  9. 使用 BEK 密钥成功解锁磁盘后,从恢复 VM 分离磁盘,然后使用此新的 OS 磁盘重新创建 VM。

    注意

    交换 OS 磁盘适用于使用单通道 ADE 版本加密的任何 VM,但双通道不支持。

  10. 如果新 VM 仍无法正常启动,请在解锁驱动器后尝试以下步骤之一:

    • 通过运行以下命令暂停保护以暂时关闭 BitLocker OFF:
    manage-bde -protectors -disable F: -rc 0
    
    • 完全解密驱动器。 为此,请运行以下命令:
    manage-bde -off F:
    

密钥加密密钥方案 (包装 BEK)

对于密钥加密密钥方案,请执行以下步骤:

  1. 请确保登录的用户帐户需要“用户|密钥权限|加密操作|解包密钥”中 Key Vault 访问策略中的“解包”权限。

  2. 将以下脚本保存到 .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
    
  3. 设置参数。 该脚本处理 KEK 机密以创建 BEK 密钥,然后将其保存到恢复 VM 上的本地文件夹中。 如果在运行脚本时收到错误,请参阅 脚本故障排除 部分。

  4. 脚本开始时,将看到以下输出:

    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
    
  5. 若要使用 BEK 文件解锁附加磁盘,请运行以下命令:

    manage-bde -unlock F: -RecoveryKey "C:\BEK\EF7B2F5A-50C6-4637-9F13-7F599C12F85C.BEK
    

    在此示例中,附加的 OS 磁盘为驱动器 F。请确保使用正确的驱动器号。

  6. 使用 BEK 密钥成功解锁磁盘后,请从恢复 VM 中分离磁盘,然后使用 交换 OS 磁盘 功能将原始 VM 的 OS 磁盘替换为此修复的磁盘。

  7. 如果新 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 模块,请执行以下步骤:

  1. 以管理员身份打开 PowerShell 会话,并将当前会话的 HTTP API 安全协议设置为 TLS 1.2。 关闭当前会话后,安全协议将还原为默认值。

    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
  2. 下载最新版本的 Nuget 包:

     Install-PackageProvider -Name "Nuget" -Force
    
    
  3. 安装最新版本的 PowerShellGet 包,然后重启 PowerShell。

    Install-Module -Name PowerShellGet -Force
    
  4. 运行以下命令以安装最新版本的 Azure Az 模块:

    Install-Module -Name Az -Scope AllUsers -Repository PSGallery -Force
    
  5. 安装 Az.Account 1.9.4 包:

    Install-Module -Name Az.Accounts -Scope AllUsers -RequiredVersion "1.9.4" -Repository PSGallery -Force
    

联系我们寻求帮助

如果你有任何疑问或需要帮助,请创建支持请求联系 Azure 社区支持。 你还可以将产品反馈提交到 Azure 反馈社区