使用与包含 VM 的 Azure 订阅关联的 Azure 帐户。
若要启用系统分配的托管标识,请将模板加载到编辑器中,在 resources
节中找到所关注的 Microsoft.Compute/virtualMachines
资源,并在与 "type": "Microsoft.Compute/virtualMachines"
属性相同的级别添加 "identity"
属性。 使用以下语法:
"identity": {
"type": "SystemAssigned"
},
最终模板如以下示例所示
"resources": [
{
"apiVersion": "2021-11-01",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"identity": {
"type": "SystemAssigned",
},
//other resource provider properties...
}
]
将访问策略添加到 Azure Key Vault
为 CVM 启用系统分配的托管标识后,必须向其提供对存储密钥对象的 Azure Key Vault 数据平面的访问权限。 为了确保只有机密虚拟机才能执行发放操作,我们只会授予所需的特定权限。
[Bicep 1]
@description('Required. Specifies the object ID of a user, service principal or security group in the Azure Active Directory tenant for the vault. The object ID must be unique for the list of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets.')
param objectId string
resource keyVaultCvmAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = {
parent: keyVault
name: 'add'
properties: {
accessPolicies: [
{
objectId: objectId
tenantId: tenantId
permissions: {
keys: [
'release'
]
}
}
]
}
}
[ARM 模板 2]
{
"type": "Microsoft.KeyVault/vaults/accessPolicies",
"apiVersion": "2022-07-01",
"name": "[format('{0}/{1}', 'mykeyvault', 'add')]",
"properties": {
"accessPolicies": [
{
"objectId": "[parameters('objectId')]",
"tenantId": "[parameters('tenantId')]",
"permissions": {
"keys": [
"release"
]
}
}
]
},
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults', 'mykeyvault')]"
]
}
准备发放策略
Key Vault 安全密钥发放策略的创建效仿 Azure Policy,只是语法略有不同。
其思路是,我们以 JSON Web 令牌 (JWT) 的形式将经过证明的平台报告传递给 Key Vault。 它进而会查看 JWT,并验证经过证明的平台报告声明是否与策略中的声明匹配。
例如,假设我们只希望在经过证明的平台报告具有以下属性时才发放密钥:
- 证明执行方是 Microsoft Azure 证明 (MAA) 服务终结点 "https://sharedweu.weu.attest.azure.net"。
- 策略中的此
authority
值与令牌中的 iss
(证书颁发者)属性进行了比较。
- 并且,它还包含一个名为
x-ms-isolation-tee
的对象,该对象又包含一个名为 x-ms-attestation-type
的属性,其中包含值 sevsnpvm
。
- MAA 作为 Azure 服务已证明 CVM 在 AMD SEV-SNP 正版处理器中运行。
- 并且,它还包含一个名为
x-ms-isolation-tee
的对象,该对象又包含一个名为 x-ms-compliance-status
的属性,其中包含值 azure-compliant-cvm
。
- MAA 作为 Azure 服务能够证明 CVM 是符合要求的 Azure 机密虚拟机。
创建名为 assets
的新文件夹,并将以下 JSON 内容添加到名为 cvm-release-policy.json
的文件:
{
"version": "1.0.0",
"anyOf": [
{
"authority": "https://sharedweu.weu.attest.azure.net",
"allOf": [
{
"claim": "x-ms-isolation-tee.x-ms-attestation-type",
"equals": "sevsnpvm"
},
{
"claim": "x-ms-isolation-tee.x-ms-compliance-status",
"equals": "azure-compliant-cvm"
}
]
}
]
}
发放策略是包含密钥颁发机构数组的 anyOf
条件。 claim
条件是一个 JSON 对象,用于标识声明名称、匹配条件和值。 AnyOf
和 AllOf
条件对象允许对 OR
和 AND
进行建模。 目前,只能对 claim
执行 equals
比较。 条件属性与 authority
属性放置在一起。
重要
环境断言至少包含一个密钥加密密钥以及一个或多个关于目标环境的声明(例如 TEE 类型、发布者、版本),这些声明针对密钥发布策略匹配。 密钥加密密钥是由用于密钥导出的目标执行环境拥有和保护的公共 RSA 密钥。 它必须出现在 TEE 密钥声明 (x-ms-runtime/keys) 中。 此声明是表示 JSON Web 密钥集的 JSON 对象。 在 JWKS 中,其中一个密钥必须满足用作加密密钥的要求(key_use 为“enc”或 key_ops 包含“encrypt”)。 选择第一个合适的密钥。
Key Vault 从“x-ms-runtime
”对象的“keys
”数组属性中选择第一个合适的密钥,它会查找具有 "key_use": ["enc"]
或 "key_ops": ["encrypt"]
的公用 RSA 密钥。 经过证明的平台报告的示例如下所示:
{
//...
"x-ms-runtime": {
"client-payload": {
"nonce": "MTIzNA=="
},
"keys": [
{
"e": "AQAB",
"key_ops": [
"encrypt"
],
"kid": "TpmEphemeralEncryptionKey",
"kty": "RSA",
"n": "9v2XQgAA6y18CxV8dSGnh..."
}
]
},
//...
}
在此示例中,$.x-ms-runtime.keys
路径下只有一个密钥。 Key Vault 使用 TpmEphemeralEncryptionKey
密钥作为密钥加密密钥。
注意
请注意,$.x-ms-isolation-tee.x-ms-runtime.keys
下可能存在密钥,这不是 Key Vault 将使用的密钥。
使用发放策略创建可导出密钥
我们将创建一个 Key Vault 访问策略,该策略允许 Azure 机密虚拟机执行 release
密钥操作。 最后,在创建密钥期间,必须将发放策略作为 base64 编码字符串包含在内。 密钥必须是 HSM 支持的可导出密钥。
注意
HSM 支持的密钥在 Azure Key Vault Premium 和 Azure Key Vault 托管 HSM 中提供。
[Bicep 2]
@description('The type of the key. For valid values, see JsonWebKeyType. Must be backed by HSM, for secure key release.')
@allowed([
'EC-HSM'
'RSA-HSM'
])
param keyType string = 'RSA-HSM'
@description('Not before date in seconds since 1970-01-01T00:00:00Z.')
param keyNotBefore int = -1
@description('Expiry date in seconds since 1970-01-01T00:00:00Z.')
param keyExpiration int = -1
@description('The elliptic curve name. For valid values, see JsonWebKeyCurveName.')
@allowed([
'P-256'
'P-256K'
'P-384'
'P-521'
])
param curveName string
@description('The key size in bits. For example: 2048, 3072, or 4096 for RSA.')
param keySize int = -1
resource exportableKey 'Microsoft.KeyVault/vaults/keys@2022-07-01' = {
parent: keyVault
name: 'mykey'
properties: {
kty: keyType
attributes: {
exportable: true
enabled: true
nbf: keyNotBefore == -1 ? null : keyNotBefore
exp: keyExpiration == -1 ? null : keyExpiration
}
curveName: curveName // applicable when using key type (kty) 'EC'
keySize: keySize == -1 ? null : keySize
keyOps: ['encrypt','decrypt'] // encrypt and decrypt only work with RSA keys, not EC
release_policy: {
contentType: 'application/json; charset=utf-8'
data: loadFileAsBase64('assets/cvm-release-policy.json')
}
}
}
[ARM 模板 2]
{
"type": "Microsoft.KeyVault/vaults/keys",
"apiVersion": "2022-07-01",
"name": "[format('{0}/{1}', 'mykeyvault', 'mykey')]",
"properties": {
"kty": "RSA-HSM",
"attributes": {
"exportable": true,
"enabled": true,
"nbf": "[if(equals(parameters('keyNotBefore'), -1), null(), parameters('keyNotBefore'))]",
"exp": "[if(equals(parameters('keyExpiration'), -1), null(), parameters('keyExpiration'))]"
},
"curveName": "[parameters('curveName')]",
"keySize": "[if(equals(parameters('keySize'), -1), null(), parameters('keySize'))]",
"keyOps": [
"encrypt",
"decrypt"
],
"release_policy": {
"contentType": "application/json; charset=utf-8",
"data": "[variables('cvmReleasePolicyBase64EncodedString')]"
}
},
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults', 'mykeyvault')]"
]
}
我们可以通过导航到 Azure 门户并选择密钥来验证 Key Vault 是否已创建新的 HSM 支持的密钥,以及它是否包含安全密钥发放策略。 预期密钥将标记为“可导出”。
来宾证明客户端
证明有助于我们以加密方式评估某个事物是否在预期的操作状态下运行。 它是一方(验证者)评估可能不受信任的对等方(证明者)的可信度的过程。 借助远程来宾证明,受信任执行环境提供了一个平台,使你能够在其中运行整个操作系统。
重要
Microsoft 为 Windows 和 Linux 提供了一个 C/C++ 库,可助力开发工作。 通过该库,可以轻松地从硬件获取 SEV-SNP 平台报告,并轻松让其通过 Azure 证明服务的实例进行证明。 Azure 证明服务可以是由 Microsoft(共享)托管的服务,也可以是你自己的专用实例。
可以选择利用来宾证明库的开源 Windows 和 Linux 客户端二进制文件,以便简化 VCM 的来宾证明过程。 客户端二进制文件会将已证明的平台报告作为 JSON Web 令牌返回,这是 Key Vault 的 release
密钥操作所需的。
注意
Azure 证明服务中的令牌有效期为 8 小时。
[Linux]
登录 VM。
克隆示例 Linux 应用程序。
安装 build-essential
包。 此包安装编译示例应用程序所需的所有内容。
sudo apt-get install build-essential
安装 libcurl4-openssl-dev
和 libjsoncpp-dev
包。
sudo apt-get install libcurl4-openssl-dev
sudo apt-get install libjsoncpp-dev
下载证明包。
安装证明包。 确保将 <version>
替换为已下载的版本。
sudo dpkg -i azguestattestation1_<latest-version>_amd64.deb
若要运行示例客户端,请在解压缩的文件夹内导航并运行以下命令:
sudo ./AttestationClient -a <attestation-url> -n <nonce-value> -o token
注意
如果 -o
未指定为 token
,则 exe 将输出二进制结果 true 或 false,具体取决于证明结果和运行 sevsnp
的平台。
登录 VM。
克隆示例 Windows 应用程序。
在解压缩的文件夹内导航并运行 VC_redist.x64.exe
。 VC_redist 将在计算机上安装 Microsoft C 和 C++ (MSVC) 运行时库。
若要运行示例客户端,请在解压缩的文件夹内导航并运行以下命令:
sudo ./AttestationClient -a <attestation-url> -n <nonce-value> -o token
注意
如果 -o
未指定为 token
,则 exe 将输出二进制结果 true 或 false,具体取决于证明结果和运行 sevsnp
的平台。
来宾证明结果
来宾证明客户端的结果只是一个 base64 编码的字符串。 此编码的字符串值是包含标头、正文和签名的有符号 JSON Web 令牌 (JWT)。 可以按 .
(点)值拆分字符串,并对结果进行 base64 解码。
eyJhbGciO...
标头包含一个 jku
,也称为 JWK 集 URI,它会链接到一组 JSON 编码的公钥。 其中一个对应于用于对 JWS 进行数字签名的密钥。 kid
指示使用哪个密钥对 JWS 进行签名。
{
"alg": "RS256",
"jku": "https://sharedweu.weu.attest.azure.net/certs",
"kid": "dRKh+hBcWUfQimSl3Iv6ZhStW3TSOt0ThwiTgUUqZAo=",
"typ": "JWT"
}
来宾证明响应的正文将由 Azure Key Vault 作为测试密钥发放策略的输入进行验证。 如前所述,Azure Key Vault 使用“TpmEphemeralEncryptionKey
”作为密钥加密密钥。
{
"exp": 1671865218,
"iat": 1671836418,
"iss": "https://sharedweu.weu.attest.azure.net",
"jti": "ce395e5de9c638d384cd3bd06041e674edee820305596bba3029175af2018da0",
"nbf": 1671836418,
"secureboot": true,
"x-ms-attestation-type": "azurevm",
"x-ms-azurevm-attestation-protocol-ver": "2.0",
"x-ms-azurevm-attested-pcrs": [
0,
1,
2,
3,
4,
5,
6,
7
],
"x-ms-azurevm-bootdebug-enabled": false,
"x-ms-azurevm-dbvalidated": true,
"x-ms-azurevm-dbxvalidated": true,
"x-ms-azurevm-debuggersdisabled": true,
"x-ms-azurevm-default-securebootkeysvalidated": true,
"x-ms-azurevm-elam-enabled": false,
"x-ms-azurevm-flightsigning-enabled": false,
"x-ms-azurevm-hvci-policy": 0,
"x-ms-azurevm-hypervisordebug-enabled": false,
"x-ms-azurevm-is-windows": false,
"x-ms-azurevm-kerneldebug-enabled": false,
"x-ms-azurevm-osbuild": "NotApplication",
"x-ms-azurevm-osdistro": "Ubuntu",
"x-ms-azurevm-ostype": "Linux",
"x-ms-azurevm-osversion-major": 20,
"x-ms-azurevm-osversion-minor": 4,
"x-ms-azurevm-signingdisabled": true,
"x-ms-azurevm-testsigning-enabled": false,
"x-ms-azurevm-vmid": "6506B531-1634-431E-99D2-42B7D3414AD0",
"x-ms-isolation-tee": {
"x-ms-attestation-type": "sevsnpvm",
"x-ms-compliance-status": "azure-compliant-cvm",
"x-ms-runtime": {
"keys": [
{
"e": "AQAB",
"key_ops": [
"encrypt"
],
"kid": "HCLAkPub",
"kty": "RSA",
"n": "tXkRLAABQ7vgX96..1OQ"
}
],
"vm-configuration": {
"console-enabled": true,
"current-time": 1671835548,
"secure-boot": true,
"tpm-enabled": true,
"vmUniqueId": "6506B531-1634-431E-99D2-42B7D3414AD0"
}
},
"x-ms-sevsnpvm-authorkeydigest": "0000000000000..00",
"x-ms-sevsnpvm-bootloader-svn": 3,
"x-ms-sevsnpvm-familyId": "01000000000000000000000000000000",
"x-ms-sevsnpvm-guestsvn": 2,
"x-ms-sevsnpvm-hostdata": "0000000000000000000000000000000000000000000000000000000000000000",
"x-ms-sevsnpvm-idkeydigest": "57486a44..96",
"x-ms-sevsnpvm-imageId": "02000000000000000000000000000000",
"x-ms-sevsnpvm-is-debuggable": false,
"x-ms-sevsnpvm-launchmeasurement": "ad6de16..23",
"x-ms-sevsnpvm-microcode-svn": 115,
"x-ms-sevsnpvm-migration-allowed": false,
"x-ms-sevsnpvm-reportdata": "c6500..0000000",
"x-ms-sevsnpvm-reportid": "cf5ea742f08cb45240e8ad4..7eb7c6c86da6493",
"x-ms-sevsnpvm-smt-allowed": true,
"x-ms-sevsnpvm-snpfw-svn": 8,
"x-ms-sevsnpvm-tee-svn": 0,
"x-ms-sevsnpvm-vmpl": 0
},
"x-ms-policy-hash": "wm9mHlvTU82e8UqoOy1..RSNkfe99-69IYDq9eWs",
"x-ms-runtime": {
"client-payload": {
"nonce": ""
},
"keys": [
{
"e": "AQAB",
"key_ops": [
"encrypt"
],
"kid": "TpmEphemeralEncryptionKey", // key-encryption key candidate!
"kty": "RSA",
"n": "kVTLSwAAQpg..Q"
}
]
},
"x-ms-ver": "1.0"
}
Microsoft Azure 证明服务的文档有一个包含所有这些 SEV-SNP 相关声明的说明的详尽列表。
可以使用任何脚本语言或编程语言通过 AttestationClient 二进制文件接收经过证明的平台报告。 由于我们在上一步骤中部署的虚拟机已启用托管标识,因此我们应该从实例元数据服务 (IMDS) 获取 Key Vault 的 Azure AD 令牌。
通过将经证明的平台报告配置为正文有效负载,并在授权标头中配置 Microsoft Entra 令牌,你就拥有了执行 release
密钥发放操作所需的一切。
#Requires -Version 7
#Requires -RunAsAdministrator
#Requires -PSEdition Core
<#
.SYNOPSIS
Perform Secure Key Release operation in Azure Key Vault, provided this script is running inside an Azure Confidential Virtual Machine.
.DESCRIPTION
Perform Secure Key Release operation in Azure Key Vault, provided this script is running inside an Azure Confidential Virtual Machine.
The release key operation is applicable to all key types. The target key must be marked exportable. This operation requires the keys/release permission.
.PARAMETER -AttestationTenant
Provide the attestation instance base URI, for example https://mytenant.attest.azure.net.
.PARAMETER -VaultBaseUrl
Provide the vault name, for example https://myvault.vault.azure.net.
.PARAMETER -KeyName
Provide the name of the key to get.
.PARAMETER -KeyName
Provide the version parameter to retrieve a specific version of a key.
.INPUTS
None.
.OUTPUTS
System.Management.Automation.PSObject
.EXAMPLE
PS C:\> .\Invoke-SecureKeyRelease.ps1 -AttestationTenant "https://sharedweu.weu.attest.azure.net" -VaultBaseUrl "https://mykeyvault.vault.azure.net/" -KeyName "mykey" -KeyVersion "e473cd4c66224d16870bbe2eb4c58078"
#>
param (
[Parameter(Mandatory = $true)]
[string]
$AttestationTenant,
[Parameter(Mandatory = $true)]
[string]
$VaultBaseUrl,
[Parameter(Mandatory = $true)]
[string]
$KeyName,
[Parameter(Mandatory = $false)]
[string]
$KeyVersion
)
# Check if AttestationClient* exists.
$fileExists = Test-Path -Path "AttestationClient*"
if (!$fileExists) {
throw "AttestationClient binary not found. Please download it from 'https://github.com/Azure/confidential-computing-cvm-guest-attestation'."
}
$cmd = $null
if ($isLinux) {
$cmd = "sudo ./AttestationClient -a $attestationTenant -o token"
}
elseif ($isWindows) {
$cmd = "./AttestationClientApp.exe -a $attestationTenant -o token"
}
$attestedPlatformReportJwt = Invoke-Expression -Command $cmd
if (!$attestedPlatformReportJwt.StartsWith("eyJ")) {
throw "AttestationClient failed to get an attested platform report."
}
## Get access token from IMDS for Key Vault
$imdsUrl = 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net'
$kvTokenResponse = Invoke-WebRequest -Uri $imdsUrl -Headers @{Metadata = "true" }
if ($kvTokenResponse.StatusCode -ne 200) {
throw "Unable to get access token. Ensure Azure Managed Identity is enabled."
}
$kvAccessToken = ($kvTokenResponse.Content | ConvertFrom-Json).access_token
# Perform release key operation
if ([string]::IsNullOrEmpty($keyVersion)) {
$kvReleaseKeyUrl = "{0}/keys/{1}/release?api-version=7.3" -f $vaultBaseUrl, $keyName
}
else {
$kvReleaseKeyUrl = "{0}/keys/{1}/{2}/release?api-version=7.3" -f $vaultBaseUrl, $keyName, $keyVersion
}
$kvReleaseKeyHeaders = @{
Authorization = "Bearer $kvAccessToken"
'Content-Type' = 'application/json'
}
$kvReleaseKeyBody = @{
target = $attestedPlatformReportJwt
}
$kvReleaseKeyResponse = Invoke-WebRequest -Method POST -Uri $kvReleaseKeyUrl -Headers $kvReleaseKeyHeaders -Body ($kvReleaseKeyBody | ConvertTo-Json)
if ($kvReleaseKeyResponse.StatusCode -ne 200) {
Write-Error -Message "Unable to perform release key operation."
Write-Error -Message $kvReleaseKeyResponse.Content
}
else {
$kvReleaseKeyResponse.Content | ConvertFrom-Json
}
密钥发放响应
安全密钥发放操作仅返回其 JSON 有效负载中的单个属性。 但是,这些内容也进行了 base64 编码。
{
"value": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg4RUFDM.."
}
在这里,我们有另一个标头,尽管此标头有 X.509 证书链作为属性。
{
"alg": "RS256",
"kid": "88EAC2DB6BE4E051B0E05AEAF6CB79E675296121",
"x5t": "iOrC22vk4FGw4Frq9st55nUpYSE",
"typ": "JWT",
"x5t#S256": "BO7jbeU3BG0FEjetF8rSisRbkMfcdy0olhcnmYEwApA",
"x5c": [
"MIIIfDCCBmSgA..XQ==",
"MII..8ZZ8m",
"MII..lMrY="
]
}
可以从 PowerShell 中的“x5c
”数组中读取,这可以帮助你验证此证书是否有效。 下面是一个示例:
$certBase64 = "MIIIfDCCBmSgA..XQ=="
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]([System.Convert]::FromBase64String($certBase64))
$cert | Format-List *
# NotAfter : 9/18/2023 6:14:06 PM
# NotBefore : 9/23/2022 6:14:06 PM
# ...
# Issuer : CN=Microsoft Azure TLS Issuing CA 06, O=Microsoft Corporation, C=US
# Subject : CN=vault.azure.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US
响应的 JWT 令牌正文看起来与调用 get
密钥操作时得到的响应非常相似。 但是,除了其他内容,该 release
操作还包括 key_hsm
属性。
{
"request": {
"api-version": "7.3",
"enc": "CKM_RSA_AES_KEY_WRAP",
"kid": "https://mykeyvault.vault.azure.net/keys/mykey"
},
"response": {
"key": {
"key": {
"kid": "https://mykeyvault.vault.azure.net/keys/mykey/e473cd4c66224d16870bbe2eb4c58078",
"kty": "RSA-HSM",
"key_ops": [
"encrypt",
"decrypt"
],
"n": "nwFQ8p..20M",
"e": "AQAB",
"key_hsm": "eyJzY2hlbW..GIifQ"
},
"attributes": {
"enabled": true,
"nbf": 1671577355,
"exp": 1703113355,
"created": 1671577377,
"updated": 1671827011,
"recoveryLevel": "Recoverable+Purgeable",
"recoverableDays": 90,
"exportable": true
},
"tags": {},
"release_policy": {
"data": "eyJ2ZXJzaW9uIjoiMS4wLjAiLCJhbnlPZiI6W3siYXV0aG9yaXR5IjoiaHR0cHM6Ly9zaGFyZWR3ZXUud2V1LmF0dGVzdC5henVyZS5uZXQiLCJhbGxPZiI6W3siY2xhaW0iOiJ4LW1zLWlzb2xhdGlvbi10ZWUueC1tcy1hdHRlc3RhdGlvbi10eXBlIiwiZXF1YWxzIjoic2V2c25wdm0ifSx7ImNsYWltIjoieC1tcy1pc29sYXRpb24tdGVlLngtbXMtY29tcGxpYW5jZS1zdGF0dXMiLCJlcXVhbHMiOiJhenVyZS1jb21wbGlhbnQtY3ZtIn1dfV19",
"immutable": false
}
}
}
}
如果对 $.response.key.release_policy.data
下面的值进行 base64 解码,你将获得我们在前面步骤中定义的 Key Vault 密钥发放策略的 JSON 表示形式。
key_hsm
属性 base64 解码的值如下所示:
{
"schema_version": "1.0",
"header": {
"kid": "TpmEphemeralEncryptionKey", // (key identifier of KEK)
"alg": "dir", // Direct mode, i.e. the referenced 'kid' is used to directly protect the ciphertext
"enc": "CKM_RSA_AES_KEY_WRAP"
},
"ciphertext": "Rftxvr..lb"
}
后续步骤
SKR 策略示例了解如何使用 Microsoft Defender for Cloud 与已安装来宾证明的机密 VM 的集成详细了解来宾证明功能了解 Azure 机密 VM