使用与包含 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 密钥库
为 CVM 启用系统分配的托管标识后,必须向其提供对存储密钥对象的 Azure 密钥库数据平面的访问权限。 为了确保只有机密虚拟机才能执行发布操作,我们只会授予所需的特定权限。
注意
可以在Azure 门户的虚拟机标识选项中找到托管标识对象 ID。 或者,可以使用 PowerShell、Azure CLI、Bicep 或 ARM 模板检索它。
[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')]"
]
}
准备发布策略
密钥库安全密钥发布策略在之后建模Azure Policy,语法略有不同。
我们的想法是,我们将经过证明的平台报表(采用 JSON Web 令牌(JWT)的形式传递给密钥库。 反过来,它将查看 JWT 并检查证明的平台报告声明是否与策略中的声明匹配。
例如,假设仅当经过证明的平台报表具有如下所示的属性时,才想要释放密钥:
- 由 Microsoft Azure 证明 (MAA) 服务终结点 “https://sharedweu.weu.attest.azure.net" 证明。
- 策略
authority
中的此值与 iss
令牌中的 (issuer) 属性进行比较。
- 它还包含一个调用的属性调用
x-ms-isolation-tee
x-ms-attestation-type
的对象,该属性包含值sevsnpvm
。
- MAA 即 Azure 服务已证明 CVM 在 AMD 标准版V-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
属性一起放置。
重要
环境断言至少包含一个密钥加密密钥,以及一个或多个针对目标环境(例如 T企业版 类型、发布者、版本)的声明,这些声明与密钥发布策略匹配。 密钥加密密钥是由用于密钥导出的目标执行环境拥有和保护的公共 RSA 密钥。 它必须出现在 TEE 密钥声明 (x-ms-runtime/keys) 中。 此声明是表示 JSON Web 密钥集的 JSON 对象。 在 JWKS 中,其中一个密钥必须满足用作加密密钥的要求(key_use 为“enc”或 key_ops 包含“encrypt”)。 选择第一个合适的密钥。
密钥库从“”对象中的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
只有一个键。 密钥库使用TpmEphemeralEncryptionKey
密钥作为密钥加密密钥。
注意
请注意,可能存在一个密钥,$.x-ms-isolation-tee.x-ms-runtime.keys
这不是密钥库将使用的密钥。
使用发布策略创建可导出密钥
我们创建一个密钥库访问策略,让 Azure 机密虚拟机执行release
密钥操作。 最后,必须在创建密钥期间将发布策略作为 base64 编码字符串包含在内。 密钥必须是 HSM 支持的可 导出密钥。
注意
Azure 密钥库 高级版和 Azure 密钥库 托管 HSM 中提供了 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')]"
]
}
我们可以验证密钥库是否已创建一个新的 HSM 支持的密钥,以及它是否包含我们的安全密钥发布策略,方法是导航到Azure 门户并选择密钥。 预期密钥将标记为“可导出”。
来宾证明客户端
证明有助于我们 以加密方式评估 某个内容是否在预期运行状态。 它是一方(验证程序)评估可能不受信任的对等方(证明者)的可信度的过程。 借助远程来宾证明,受信任的执行环境提供了一个平台,使你能够在其中运行整个操作系统。
重要
Microsoft 为 Windows 和 Linux 提供了一个 C/C++ 库,可帮助进行开发工作。 该库可以轻松地从硬件获取 标准版V-SNP 平台报表,并使其由Azure 证明服务的实例证明。 Azure 证明服务可以是由 Microsoft 托管的服务(共享)或你自己的专用实例。
可以选择利用来宾证明库的开放源代码 Windows 和 Linux 客户端二进制文件,以便使用 CVM 轻松完成来宾证明过程。 客户端二进制文件将经过证明的平台报表作为 JSON Web 令牌返回,这是密钥库密钥release
操作所需的。
[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
指定为<a0/>,则 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
指定为<a0/>,则 exe 将输出二进制结果 true 或 false,具体取决于证明结果和平台。sevsnp
来宾证明结果
来宾证明客户端的结果只是 base64 编码的字符串。 此编码的字符串值是带头、正文和签名的有符号 JSON Web 令牌 (JWT)。 可以按 .
值拆分字符串,并按 base64 解码结果。
eyJhbGciO...
标头包含一个jku
也称为 JWK Set URI,该 URI 链接到一组 JSON 编码的公钥。 其中一个对应于用于对 JWS 进行数字签名的密钥。 指示 kid
使用哪个密钥对 JWS 进行签名。
{
"alg": "RS256",
"jku": "https://sharedweu.weu.attest.azure.net/certs",
"kid": "dRKh+hBcWUfQimSl3Iv6ZhStW3TSOt0ThwiTgUUqZAo=",
"typ": "JWT"
}
Azure 密钥库将验证来宾证明响应的正文作为针对密钥发布策略进行测试的输入。 如前所述,Azure 密钥库使用“”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 证明 服务的文档包含所有这些标准版V-SNP 相关声明的说明。
可以使用任何脚本或编程语言通过 AttestationClient 二进制文件接收经过证明的平台报表。 由于我们在上一步中部署的虚拟机已启用托管标识,因此应从实例元数据服务(IMDS)获取密钥库的 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
}
}
}
}
如果 base64 解码下面的$.response.key.release_policy.data
值,你将获得我们在前面步骤中定义的密钥库密钥发布策略的 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