Azure VM 上的 BitLocker 启动错误BitLocker boot errors on an Azure VM

本文介绍在 Azure 中启动 Windows 虚拟机 (VM) 时可能遇到的 BitLocker 错误。This article describes BitLocker errors that you may experience when you start a Windows virtual machine (VM) in Azure.

症状Symptom

Windows VM 不启动。A Windows VM doesn't start. 检查启动诊断窗口中的屏幕截图时,看到以下错误消息之一:When you check the screenshots in the Boot diagnostics window, you see one of the following error messages:

  • 插入含 BitLocker 密钥的 USB 驱动程序Plug in the USB driver that has the BitLocker key

  • 你被锁定!You're locked out! 输入恢复密钥,以便再次开始操作(键盘布局:美式键盘)错误登录信息输入次数过多,因此,你的 PC 被锁定以保护你的隐私。Enter the recovery key to get going again (Keyboard Layout: US) The wrong sign-in info has been entered too many times, so your PC was locked to protect your privacy. 若要检索恢复密钥,请从另一电脑或移动设备转到 https://windows.microsoft.com/recoverykeyfaqTo retrieve the recovery key, go to https://windows.microsoft.com/recoverykeyfaq from another PC or mobile device. 如果需要,密钥 ID 为 XXXXXXX。In case you need it, the key ID is XXXXXXX. 或者,可以重置电脑。Or, you can reset your PC.

  • 输入密码以解锁此驱动器 [ ] 按 Insert 键在键入时查看密码。Enter the password to unlock this drive [ ] Press the Insert Key to see the password as you type.

  • 输入恢复密钥 从 USB 设备加载恢复密钥。Enter your recovery key Load your recovery key from a USB device.

原因Cause

如果 VM 找不到用于解密加密磁盘的 BitLocker 恢复密钥 (BEK) 文件,则可能会出现此问题。This problem may occur if the VM cannot locate the BitLocker Recovery Key (BEK) file to decrypt the encrypted disk.

解决方案Solution

若要解决此问题,请停止 VM 并将它解除分配,然后再启动它。To resolve this problem, stop and deallocate the VM, and then start it. 此操作将强制 VM 从 Azure Key Vault 中检索 BEK 文件,然后将其放在加密磁盘上。This operation forces the VM to retrieve the BEK file from the Azure Key Vault, and then put it on the encrypted disk.

如果此方法未能解决此问题,请执行以下步骤,手动还原 BEK 文件:If this method does not the resolve the problem, follow these steps to restore the BEK file manually:

  1. 拍摄受影响的 VM 的系统磁盘的快照作为备份。Take a snapshot of the system disk of the affected VM as a backup. 有关详细信息,请参阅拍摄磁盘快照For more information, see Snapshot a disk.

  2. 将系统磁盘附加到恢复 VMAttach the system disk to a recovery VM. 若要在步骤 7 中运行 manage-bde 命令,必须在恢复 VM 中启用“BitLocker 驱动器加密”功能。To run the manage-bde command in the step 7, the BitLocker Drive Encryption feature must be enabled in the recovery VM.

    附加托管磁盘时,可能会收到“包含加密设置,因此不能用作数据磁盘”错误消息。When you attach a managed disk, you might receive a "contains encryption settings and therefore cannot be used as a data disk" error message. 在此情况下,运行以下脚本,重试附加磁盘:In this situation, run the following script to try again to attach the disk:

    $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。You cannot attach a managed disk to a VM that was restored from a blob image.

  3. 附加磁盘后,对恢复 VM 进行远程桌面连接,以便可以运行某些 Azure PowerShell 脚本。After the disk is attached, make a remote desktop connection to the recovery VM so that you can run some Azure PowerShell scripts. 确保已在恢复 VM 上安装最新版本的 Azure PowerShellMake sure that you have the latest version of Azure PowerShell installed on the recovery VM.

  4. 打开提升的 Azure PowerShell 会话(以管理员身份运行)。Open an elevated Azure PowerShell session (Run as administrator). 运行以下命令来登录到 Azure 订阅:Run the following commands to sign in to Azure subscription:

    Add-AzAccount -Environment AzureChinaCloud -SubscriptionID [SubscriptionID]
    
  5. 运行以下脚本来检查 BEK 文件的名称:Run the following script to check the name of the BEK file:

    $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}}
    

    下面是输出的示例。The following is sample of the output. 在这种情况下,我们假设文件名为 EF7B2F5A-50C6-4637-0001-7F599C12F85C.BEK。In this case, we assume that the file name is 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 文件。If you see two duplicated volumes, the volume that has the newer timestamp is the current BEK file that is used by the recovery VM.

    如果“内容类型”值为“包装的 BEK”,请转到密钥加密密钥 (KEK) 方案If the Content Type value is Wrapped BEK, go to the Key Encryption Key (KEK) scenarios.

    获取驱动器的 BEK 文件名称后,须创建 secret-file-name.BEK 文件以解锁驱动器。Now that you have the name of the BEK file for the drive, you have to create the secret-file-name.BEK file to unlock the drive.

  6. 将 BEK 文件下载到恢复磁盘。Download the BEK file to the recovery disk. 以下示例将 BEK 文件保存到 C:\BEK 文件夹。The following sample saves the BEK file to the C:\BEK folder. 运行脚本前,请确保 C:\BEK\ 路径存在。Make sure that the C:\BEK\ path exists before you run the scripts.

    $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)
    
  7. 若要使用 BEK 文件解锁附加磁盘,请运行以下命令。To unlock the attached disk by using the BEK file, run the following command.

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

    在此示例中,附加的 OS 磁盘为驱动器 F。请确保使用正确的驱动器号。In this sample, the attached OS disk is drive F. Make sure that you use the correct drive letter.

  8. 使用 BEK 密钥成功解锁磁盘以后,从恢复 VM 分离该磁盘,然后使用该新的 OS 磁盘重新创建 VM。After the disk was successfully unlocked by using the BEK key, detach the disk from the recovery VM, and then recreate the VM by using this new OS disk.

    备注

    对于使用磁盘加密的 VM,不支持交换 OS 磁盘。Swapping OS Disk is not supported for VMs using disk encryption.

  9. 如果新的 VM 仍然不能正常启动,请在解锁设备后尝试下述步骤之一:If the new VM still cannot boot normally, try one of following steps after you unlock the drive:

    • 暂停保护,以便运行以下命令,暂时关闭 BitLocker:Suspend protection to temporarily turn BitLocker OFF by running the following:
    manage-bde -protectors -disable F: -rc 0
    
    • 完全解密该驱动器。Fully decrypt the drive. 为此,请运行以下命令:To do this, run the following command:
    manage-bde -off F:
    

密钥加密密钥方案Key Encryption Key scenario

对于密钥加密密钥方案,请执行以下步骤:For a Key Encryption Key scenario, follow these steps:

  1. 请确保登录的用户帐户需要“用户|密钥权限|加密操作|解包密钥”中 Key Vault 访问策略中的“解包”权限。Make sure that the logged-in user account requires the "unwrapped" permission in the Key Vault Access policies in the USER|Key permissions|Cryptographic Operations|Unwrap Key.

  2. 将以下脚本保存到 .PS1 文件:Save the following script to a .PS1 file:

    #Set the Parameters for the script
    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
    $adal = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\$(((dir ${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts).name) | select -last 1)\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
    $adalforms = "${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts\$(((dir ${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Accounts).name) | select -last 1)\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
     {
    $adal="${env:userprofile}\Documents\WindowsPowerShell\Modules\Az.Accounts\$(((dir ${env:userprofile}\Documents\WindowsPowerShell\Modules\Az.Accounts).name) | select -last 1)\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
    $adalforms ="${env:userprofile}\Documents\WindowsPowerShell\Modules\Az.Accounts\$(((dir ${env:userprofile}\Documents\WindowsPowerShell\Modules\Az.Agit pgit ccounts).name) | select -last 1)\PreloadAssemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
    [System.Reflection.Assembly]::LoadFrom($adal)
    [System.Reflection.Assembly]::LoadFrom($adalforms)
     }  
    
    # 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.cn"
    # Set Authority to Azure AD Tenant
    $authority = "https://login.chinacloudapi.cn/$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. 设置参数。Set the parameters. 该脚本处理 KEK 机密以创建 BEK 密钥,然后将其保存到恢复 VM 上的本地文件夹中。The script will process the KEK secret to create the BEK key, and then save it to a local folder on the recovery VM. 如果在运行脚本时收到错误,请参阅脚本故障排除部分。If you receive errors when you run the script, see the script troubleshooting section.

  4. 脚本开始时,将看到以下输出:You see the following output when the script begins:

    GAC 版本 位置GAC Version Location


    False v4.0.30319 C:\Program Files\WindowsPowerShell\Modules\Az.Accounts...False v4.0.30319 C:\Program Files\WindowsPowerShell\Modules\Az.Accounts...False v4.0.30319 C:\Program Files\WindowsPowerShell\Modules\Az.Accounts... False v4.0.30319 C:\Program Files\WindowsPowerShell\Modules\Az.Accounts...

    脚本完成后,将看到以下输出:When the script finishes, you see the following output:

    VERBOSE: POST https://myvault.vault.azure.cn/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 文件解锁附加磁盘,请运行以下命令:To unlock the attached disk by using the BEK file, run the following command:

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

    在此示例中,附加的 OS 磁盘为驱动器 F。请确保使用正确的驱动器号。In this sample, the attached OS disk is drive F. Make sure that you use the correct drive letter.

  6. 使用 BEK 密钥成功解锁磁盘以后,从恢复 VM 分离该磁盘,然后使用该新的 OS 磁盘重新创建 VM。After the disk was successfully unlocked by using the BEK key, detach the disk from the recovery VM, and then recreate the VM by using this new OS disk.

    备注

    对于使用磁盘加密的 VM,不支持交换 OS 磁盘。Swapping OS Disk is not supported for VMs using disk encryption.

  7. 如果新的 VM 仍然不能正常启动,请在解锁设备后尝试下述步骤之一:If the new VM still cannot boot normally, try one of following steps after you unlock the drive:

    • 暂停保护,以便运行以下命令,暂时关闭 BitLocker:Suspend protection to temporarily turn BitLocker OFF by running the following command:
    manage-bde -protectors -disable F: -rc 0
    
    • 完全解密该驱动器。Fully decrypt the drive. 为此,请运行以下命令:To do this, run the following command:
    manage-bde -off F:
    

脚本故障排除Script troubleshooting

错误:无法加载文件或程序集Error: Could not load file or assembly

发出此错误是因为 ADAL 程序集的路径错误。This error occurs because the paths of the ADAL Assemblies are wrong. 你可搜索 Az.Accounts 文件夹来查找正确的路径。You can search for Az.Accounts folder to find the correct path.

错误:Get-AzKeyVaultSecret 或 Get-AzKeyVaultSecret 无法识别为 cmdlet 的名称Error: Get-AzKeyVaultSecret or Get-AzKeyVaultSecret is not recognized as the name of a cmdlet

如果使用旧的 AZ PowerShell 模块,则必须将这两个命令更改为 Get-AzureKeyVaultSecretGet-AzureKeyVaultSecretIf you are using the old AZ PowerShell module, you must change the two commands to Get-AzureKeyVaultSecret and Get-AzureKeyVaultSecret.

参数示例Parameters samples

parametersParameters 值示例Value sample 注释Comments
$keyVaultName$keyVaultName myKeyVault2112852926myKeyVault2112852926 用于存储此密钥的密钥保管库的名称The name of the key Vault that stores the key
$kekName$kekName mykeymykey 用于加密 VM 的密钥的名称The name of the key that is used to encrypt the VM
$secretName$secretName 7EB4F531-5FBA-4970-8E2D-C11FD6B0C69D7EB4F531-5FBA-4970-8E2D-C11FD6B0C69D VM 密钥的机密的名称The name of the secret of the VM key
$bekFilePath$bekFilePath c:\bek\7EB4F531-5FBA-4970-8E2D-C11FD6B0C69D.BEKc:\bek\7EB4F531-5FBA-4970-8E2D-C11FD6B0C69D.BEK 用于写入 BEK 文件的路径。The path for writing BEK file.
$adTenant$adTenant contoso.partner.onmschina.cncontoso.partner.onmschina.cn 用于托管密钥保管库的 Azure Active Directory 的 FQDN 或 GUIDFQDN or GUID of your Azure Active Directory that hosts the key vault