Azure AI 搜索使用 Azure 管理的密钥自动加密静态数据。 如果需要另一层加密,或者能够撤销密钥并关闭对内容的访问权限,则可以使用在Azure 密钥保管库中创建和管理的密钥。 本文介绍如何设置客户管理的密钥(CMK)加密。
可以使用以下任一方法存储密钥:
重要
CMK 为静态数据提供加密。 如果需要保护正在使用的数据,请考虑使用 机密计算。
CMK 加密不可逆。 可以轮换密钥并更改 CMK 配置,但索引加密在索引的生存期内持续。 CMK 加密后,仅当搜索服务有权访问密钥时,才能访问索引。 如果通过删除或更改角色分配来撤销对密钥的访问权限,则索引不可用,且在删除索引或还原对密钥的访问权限之前,服务将无法缩放。 如果删除或轮换密钥,则最新密钥最多缓存 60 分钟。
CMK 加密的对象
CMK 加密适用于创建单个对象时。 这意味着无法加密已经存在的对象。 每次将对象保存到磁盘时,就会进行 CMK 加密,无论是用于静态数据(长期存储),还是用于临时缓存的数据(短期存储)。 使用 CMK 时,磁盘永远不会看到未加密的数据。
可加密的对象包括索引、同义词列表、索引器、数据源和技能组。 加密的计算开销高于解密,因此只会加密敏感内容。
以下内容将被加密:
如果需要跨搜索服务使用 CMK,请设置强制策略。
尽管无法将加密添加到现有对象,但一旦对象配置为加密,可以更改其加密定义的所有部分,包括切换到其他密钥保管库或 HMS 存储,前提是资源位于同一租户中。
先决条件
Azure AI 搜索在可计费的层级(基本层或更高级别,适用于任何区域)。
Azure 密钥保管库 和启用了 软删除 和 清除保护 的密钥保管库。 或者,Azure 密钥保管库托管 HSM。 此资源可以位于任何订阅和不同的租户中。
能够为密钥访问设置权限并分配角色。 若要创建密钥,必须是 Azure 密钥保管库 中的密钥保管库 Crypto Officer或 Azure 密钥保管库 托管 HSM 中的Managed HSM Crypto Officer。
若要分配角色,必须是订阅 Owner、User Access Administrator、角色基于访问控制管理员,或者分配到具有 Microsoft.Authorization/roleAssignments/write 权限的自定义角色。
步骤 1:创建加密密钥
使用Azure 密钥保管库或Azure 密钥保管库托管 HSM 创建密钥。 Azure AI 搜索加密支持大小为 2048、3072 和 4096 的 RSA 密钥。 有关支持的密钥类型详细信息,请参阅关于密钥。
建议在开始之前查看 这些提示 。
必需的作是 Wrap、 Unwrap、 Encrypt 和 Decrypt。
可以使用 Azure 门户、Azure CLI或 Azure PowerShell 创建密钥保管库。
登录到 Azure 门户并打开密钥保管库概述页。
在左侧选择“对象”“密钥”,然后选择“生成/导入”。>
在“创建密钥”窗格的“选项”列表中,选择“生成”以创建新密钥。
输入密钥的“名称”,并接受其他密钥属性的默认值。
可以选择设置密钥轮换策略以启用自动轮换。
选择“创建”以开始部署。
创建密钥后,获取其密钥标识符。 选择密钥,选择当前版本,然后复制密钥标识符。 它由键值 URI、密钥名称和密钥版本组成。 你需要标识符来在Azure AI 搜索中定义加密索引。 回想一下,所需的作是 Wrap、 Unwrap、 Encrypt 和 Decrypt。
步骤 2:创建安全主体
创建一个安全主体供搜索服务用于访问加密密钥。 可以使用托管标识和角色分配,也可以注册应用程序并让搜索服务针对请求提供应用程序 ID。
建议使用托管标识和角色。 可以使用系统托管标识或用户托管标识。 托管标识使搜索服务能够通过Microsoft Entra ID进行身份验证,而无需在代码中存储凭据(ApplicationID 或 ApplicationSecret)。 此类托管标识的生命周期与只能具有一个系统分配托管标识的搜索服务的生命周期相关联。 有关托管标识工作原理的详细信息,请参阅 什么是Azure资源的托管标识。
为搜索服务启用系统分配的托管标识。 这是一项两次点击操作:点击启用并保存。
可以使用Azure门户或搜索管理 REST API 创建用户分配的托管标识并将标识分配给搜索服务。 有关详细信息,请参阅创建用户分配的托管标识。
如果您无法使用角色分配来为搜索服务访问加密密钥,请按照以下说明进行操作。
在 Azure 门户中,找到与您订阅相关的 Microsoft Entra 资源。
在左侧的 Manage 下,选择 应用注册,然后选择 New registration。
为注册提供一个名称,该名称可以是与搜索应用程序名称类似的名称。 选择“注册”。
创建应用注册后,复制应用程序 ID。 需要将此字符串提供给应用程序。
如果要单步执行 DotNetHowToEncryptionUsingCMK,请将此值粘贴到 appsettings.json 文件中。
接下来,选择 “证书和机密”。
选择“新建客户端机密”。 为机密指定显示名称并选择“添加”。
复制应用程序密钥。 如果要逐步执行此示例,请将以下值粘贴到 appsettings.json 文件中。
步骤 3:授予权限
如果将搜索服务配置为使用托管标识,请分配授予其对加密密钥访问权限的角色。
建议选择基于角色的访问控制,而不是访问策略权限模型。 有关详细信息或迁移步骤,请从 Azure 基于角色的访问控制(Azure RBAC)与访问策略(旧版)。
登录到 Azure 门户并找到密钥保管库。
选择“访问控制 (IAM)”,然后选择“添加角色分配”。
选择一个角色:
- 在Azure 密钥保管库上,选择密钥保管库加密服务加密用户。
- 在托管 HSM 上,选择“托管 HSM 加密服务加密用户”。
选择“托管标识”,再选择“成员”,然后选择搜索服务的托管标识。 如果是在本地进行测试,请同时将此角色分配给自己。
选择“审核与分配”。
等待几分钟,以便角色分配正常运行。
步骤 4:加密内容
创建或更新对象时发生加密。 可以使用 Azure 门户来处理特定对象。 对于所有对象,请使用 Search Service REST API 或Azure SDK。
在 Azure 门户中创建新对象时,可以在密钥保管库中指定预定义的客户管理的密钥。 Azure门户允许为以下项启用 CMK 加密:
使用 Azure 门户的要求是密钥保管库和密钥必须存在,并且你已完成先前的步骤,以便获得对密钥的授权访问。
在Azure门户中,技能集在 JSON 视图中定义。 使用 REST API 示例中提供的 JSON 为技能集配置一个客户管理密钥。
登录到 Azure 门户并打开搜索服务页。
在 “搜索管理”下,选择 “索引”、“ 索引器”或 “数据源”。
添加新对象。 在对象定义中,选择Azure托管加密。
选择 客户管理的密钥 ,然后选择订阅、保管库、密钥和版本。
<博览会>Azure 门户中的加密密钥页面的屏幕截图。
调用创建 API 以指定 encryptionKey 属性:
将 encryptionKey 构造插入到对象定义中。 此属性是一级属性,其级别与名称和说明相同。 如果使用的是相同的保管库、密钥和版本,则可以将相同的 encryptionKey 构造粘贴到每个对象定义中。
如果密钥标识符为 https://contoso-keyvault.vault.azure.cn/keys/contoso-cmk/aaaaaaaa-0b0b-1c1c-2d2d-333333333333,则 URI 为 https://contoso-keyvault.vault.azure.cn,密钥名称为 contoso-cmk,版本为 aaaaaaaa-0b0b-1c1c-2d2d-333333333333。
{
"encryptionKey": {
"keyVaultUri": "<YOUR-KEY-VAULT-URI>",
"keyVaultKeyName": "<YOUR-ENCRYPTION-KEY-NAME>",
"keyVaultKeyVersion": "<YOUR-ENCRYPTION-KEY-VERSION>",
"identity" : {
"@odata.type": "#Microsoft.Azure.Search.DataUserAssignedIdentity",
"userAssignedIdentity" : "/subscriptions/<your-subscription-ID>/resourceGroups/<your-resource-group-name>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<your-managed-identity-name>"
}
}
}
第一个示例展示了通过托管身份进行连接的搜索服务的加密密钥:
{
"encryptionKey": {
"keyVaultUri": "<YOUR-KEY-VAULT-URI>",
"keyVaultKeyName": "<YOUR-ENCRYPTION-KEY-NAME>",
"keyVaultKeyVersion": "<YOUR-ENCRYPTION-KEY-VERSION>"
}
}
第二个示例包括 accessCredentials,在Microsoft Entra ID中注册应用程序时是必需的:
{
"encryptionKey": {
"keyVaultUri": "<YOUR-KEY-VAULT-URI>",
"keyVaultKeyName": "<YOUR-ENCRYPTION-KEY-NAME>",
"keyVaultKeyVersion": "<YOUR-ENCRYPTION-KEY-VERSION>",
"accessCredentials": {
"applicationId": "<YOUR-APPLICATION-ID>",
"applicationSecret": "<YOUR-APPLICATION-SECRET>"
}
}
}
通过在对象上发出 GET 来验证加密密钥是否存在。
通过执行任务(例如查询已加密的索引)来验证对象是否正常运行。
在搜索服务上创建加密对象后,可以像使用其类型的任何其他对象一样使用它。 加密对于用户和开发人员是透明的。
这些密钥保管库详细信息都不被视为机密,可以通过浏览到Azure门户中的相关Azure 密钥保管库页轻松检索这些密钥保管库详细信息。
此示例显示对象定义中encryptionKey的Python表示形式。 相同的定义适用于索引、数据源、技能组、索引器和同义词映射。 若要在搜索服务和密钥保管库上试用此示例,请从 azure-search-python-samples 下载笔记本。
安装一些包。
! pip install python-dotenv
! pip install azure-core
! pip install azure-search-documents==11.5.1
! pip install azure-identity
创建具有加密密钥的索引。
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents.indexes.models import (
SimpleField,
SearchFieldDataType,
SearchableField,
SearchIndex,
SearchResourceEncryptionKey
)
from azure.identity import DefaultAzureCredential
endpoint="<PUT YOUR AZURE SEARCH SERVICE ENDPOINT HERE>"
credential = DefaultAzureCredential()
index_name = "test-cmk-index"
index_client = SearchIndexClient(endpoint=endpoint, credential=credential)
fields = [
SimpleField(name="Id", type=SearchFieldDataType.String, key=True),
SearchableField(name="Description", type=SearchFieldDataType.String)
]
scoring_profiles = []
suggester = []
encryption_key = SearchResourceEncryptionKey(
key_name="<PUT YOUR KEY VAULT NAME HERE>",
key_version="<PUT YOUR ALPHANUMERIC KEY VERSION HERE>",
vault_uri
)
index = SearchIndex(name=index_name, fields=fields, encryption_key=encryption_key)
result = index_client.create_or_update_index(index)
print(f' {result.name} created')
获取索引定义以验证加密密钥配置是否存在。
index_name = "test-cmk-index-qs"
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
result = index_client.get_index(index_name)
print(f"{result}")
用几个文档来加载索引。 所有字段内容将被视为敏感内容,并使用客户管理的密钥在磁盘上加密。
from azure.search.documents import SearchClient
# Create a documents payload
documents = [
{
"@search.action": "upload",
"Id": "1",
"Description": "The hotel is ideally located on the main commercial artery of the city in the heart of Beijing. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make Beijing one of America's most attractive and cosmopolitan cities."
},
{
"@search.action": "upload",
"Id": "2",
"Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts."
},
{
"@search.action": "upload",
"Id": "3",
"Description": "The hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services."
},
{
"@search.action": "upload",
"Id": "4",
"Description": "The hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace."
}
]
search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, index_name=index_name, credential=credential)
try:
result = search_client.upload_documents(documents=documents)
print("Upload of new document succeeded: {}".format(result[0].succeeded))
except Exception as ex:
print (ex.message)
index_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
运行查询以确认索引是否正常运行。
from azure.search.documents import SearchClient
query = "historic"
search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential, index_name=index_name)
results = search_client.search(
query_type='simple',
search_text=query,
select=["Id", "Description"],
include_total_count=True
)
for result in results:
print(f"Score: {result['@search.score']}")
print(f"Id: {result['Id']}")
print(f"Description: {result['Description']}")
查询的输出应生成类似于以下示例的结果。
Score: 0.6130029
Id: 4
Description: The hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.
Score: 0.26286605
Id: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of Beijing. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make Beijing one of America's most attractive and cosmopolitan cities.
由于加密内容是在数据刷新或查询之前解密的,因此不会看到加密的可视证据。 要验证加密是否正常工作,请检查资源日志。
重要
Azure AI 搜索中的加密内容配置为使用具有特定 version 的特定密钥。 如果更改了密钥或版本,则必须先更新对象以使用它,然后才能删除上一个密钥或版本。 否则,将会导致该对象不可用。 如果密钥丢失,你将无法解密内容。
步骤 5:测试加密
要验证加密是否正常工作,请撤销加密密钥,查询索引(应处于不可用状态),然后恢复加密密钥。
为此任务使用Azure门户。 请确保分配了允许读取密钥的角色。
在Azure 密钥保管库页上,选择Objects>Keys。
选择创建的密钥,然后选择“ 删除”。
在“Azure AI 搜索”页上,选择Search management>Indexes。
选择索引并使用搜索资源管理器运行查询。 你应该出现一个错误。
返回到 Azure 密钥保管库 Objects>Keys 页。
选择“管理已删除的密钥”。
选择密钥,然后选择“恢复”。
返回到Azure AI 搜索中的索引并重新运行查询。 你会看到搜索结果。 如果未看到即时结果,请等待一分钟,然后重试。
设置策略以强制执行 CMK 合规性
Azure策略有助于强制实施组织标准并大规模评估合规性。 Azure AI 搜索有两个与 CMK 相关的可选内置策略。 这些策略适用于新的和现有的搜索服务。
| 影响 |
启用时的效果 |
|
AuditIfNotExists |
检查策略符合性:对象是否定义了客户管理的密钥,并且内容是否已加密。 此效果适用于包含内容的现有服务。 每次创建或更新对象时会对其进行评估,或按评估计划进行评估。
了解详细信息... |
|
否认 |
检查策略强制执行:搜索服务是否将 SearchEncryptionWithCmk 设置为 Enabled。 此效果仅适用于必须启用加密的新服务。 现有服务仍可正常运行,但除非修补服务,否则无法更新它们。 用于预配服务的工具都未公开此属性,因此请注意设置策略会限制你 以编程方式进行设置。 |
分配策略
在 Azure 门户中,导航到内置策略,然后选择 Assign。
下面是 Azure 门户中 AuditIfExists 策略的示例:
通过选择订阅和资源组来设置 策略范围 。 排除策略不应应用的任何搜索服务。
接受或修改默认值。 选择 审阅 +创建,然后选择 创建。
启用 CMK 策略的强制执行
分配给订阅中的资源组的策略立即生效。 审核策略会标记不符合资源,但拒绝策略会阻止创建和更新不合规的搜索服务。 本部分介绍如何创建合规的搜索服务或更新服务以使其合规。 若要使对象符合规定,请从本文第一步开始。
创建合规的搜索服务
对于新的搜索服务,请使用 SearchEncryptionWithCmk 设置为 Enabled 创建它们。
Azure门户和命令行工具(Azure CLI和Azure PowerShell)均未本机提供此属性,但可以使用 Management REST API 使用 CMK 策略定义预配搜索服务。
该示例改编自使用 REST API 管理 Azure AI 搜索服务,并包含 SearchEncryptionWithCmk 属性。
### Create a search service (provide an existing resource group)
@resource-group = my-rg
@search-service-name = my-search
PUT https://management.chinacloudapi.cn/subscriptions/{{subscriptionId}}/resourceGroups/{{resource-group}}/providers/Microsoft.Search/searchServices/{{search-service-name}}?api-version=2025-05-01 HTTP/1.1
Content-type: application/json
Authorization: Bearer {{token}}
{
"location": "China North",
"sku": {
"name": "basic"
},
"properties": {
"replicaCount": 1,
"partitionCount": 1,
"hostingMode": "default",
"encryptionWithCmk": {
"enforcement": "Enabled"
}
}
}
更新现有搜索服务
对于现在不合规的现有搜索服务,请使用 Services - Update API 或 Azure CLI az 资源更新命令对其进行修补。 修补服务可恢复更新搜索服务属性的功能。
PATCH https://management.chinacloudapi.cn/subscriptions/<your-subscription-Id>/resourceGroups/<your-resource-group-name>/providers/Microsoft.Search/searchServices/<your-search-service-name>?api-version=2025-05-01
{
"properties": {
"encryptionWithCmk": {
"enforcement": "Enabled"
}
}
}
运行以下命令,替换搜索服务和资源组的有效值。
az resource update --name SEARCH-SERVICE-PLACEHOLDER --resource-group RESOURCE-GROUP-PLACEHOLDER --resource-type searchServices --namespace Microsoft.Search --set properties.encryptionWithCmk.enforcement=Enabled
响应应包含以下语句:
"encryptionWithCmk": {
"encryptionComplianceStatus": "NonCompliant",
"enforcement": "Enabled"
}
...
“不符合”意味着搜索服务具有未加密 CMK 的现有对象。 若要实现合规性,请重新创建每个对象,并指定加密密钥。
轮换或更新加密密钥
使用以下说明轮换密钥或从Azure 密钥保管库迁移到硬件安全模型(HSM)。
对于密钥轮换,建议使用 Azure 密钥保管库 的自动轮换功能。 如果使用自动轮换,请省略对象定义中的关键版本。 使用最新的密钥,而不是特定版本。
更改密钥或其版本时,必须先更新使用该密钥的任何对象以使用新值,然后再删除旧值。 否则,由于无法解密,对象将会处于不可用状态。
回想一下,密钥缓存了 60 分钟。 在测试和轮换密钥时,请记住这一点。
确定索引或同义词映射所使用的密钥。
在密钥保管库中创建新密钥,但保持原始密钥可用。 在此步骤中,可以将密钥保管库切换到 HSM。
更新索引或同义词映射上的 encryptionKey 属性以使用新值。 只有最初创建时带有此属性的对象才能更新为使用其他值。
禁用或删除密钥保管库中的上一个密钥。 监视密钥访问以验证是否正在使用新密钥。
出于性能方面的原因,搜索服务最多会缓存此密钥几个小时。 如果在不提供新密钥的情况下禁用或删除该密钥,则查询将暂时性地继续工作,直到缓存过期。 但是,一旦搜索服务无法再解密内容,你就会收到以下消息: "Access forbidden. The query key used might have been revoked - please retry."
密钥保管库提示
如果你不熟悉Azure 密钥保管库,请查看本快速入门,了解基本任务:使用 PowerShell 从 Azure 密钥保管库 中检索机密。
您可以根据需要使用任意数量的密钥保管库。 管理的密钥可以位于不同的密钥保管库中。 一个搜索服务可以有多个已加密的对象,每个对象通过不同密钥保管库中存储的不同客户管理的加密密钥进行加密。
使用相同的 Azure 租户,以便可以通过角色分配以及通过系统或用户托管标识进行连接来检索托管密钥。 有关创建租户的详细信息,请参阅设置新租户。
在密钥保管库上启用清除保护和软删除。 由于使用客户管理的密钥进行加密的性质,如果删除了Azure 密钥保管库密钥,则任何人都无法检索数据。 若要防止意外密钥保管库密钥删除导致的数据丢失,必须在key vault上启用软删除和清除保护。 默认情况下,软删除处于启用状态,因此,只有在你特意禁用了此功能时才会遇到问题。 默认情况下,不会启用清除保护,但在Azure AI 搜索中需要 CMK 加密。
在密钥保管库上启用日志记录,以便可以监视密钥使用情况。
请启用密钥自动轮换,或在例行轮换过程中,遵循严格的流程以管理密钥保管库密钥、应用程序机密和注册。 在删除旧机密和密钥之前,始终更新所有已加密内容以使用新机密和密钥。 如果缺少此步骤,你的内容将无法解密。
与加密内容一起工作
使用 CMK 加密,你可能会注意到由于额外的加密/解密工作而导致索引编制和查询的延迟。 Azure AI 搜索不会记录加密活动,但可以通过密钥保管库日志记录监视密钥访问。
建议在配置密钥保管库的过程中启用日志记录。
创建 Log Analytics 工作区。
在密钥保管库中添加一个诊断设置,该设置使用工作区来进行数据保留。
为该类别选择审核或allLogs,并为诊断配置命名,然后保存。
后续步骤
如果不熟悉Azure安全体系结构,请查看 Azure 安全文档,特别是本文: