使用 Azure Key Vault 进行 MSIX 和 CI/CD 管道签名MSIX and CI/CD Pipeline signing with Azure Key Vault

签名是在使用 MSIX 包时要执行的其中一项关键任务。Signing is one of the critical tasks to perform when you're working with MSIX packages. 如果 MSIX 包未使用受信任的证书进行签名,用户将无法安装应用程序。If a MSIX package isn't signed with a trusted certificate, users won't be able to install the application. 同时,签名也是涉及到安全性时最重要的关键任务之一。At the same time, signing is one of the most critical tasks also when it comes to security. 必须安全存储证书,避免恶意操作者重复使用它们通过我们的标识对其应用程序签名。Certificates must be safely stored, to avoid that malicious actors could reuse them to sign their applications with our identity. Azure Key Vault 是支持这一要求的最佳选项。Azure Key Vault is the best option to support this requirement.

在本文中,我们将了解可如何在 CI/CD 管道中使用 Azure Key Vault,以便可在操作过程中自动对我们的 MSIX 包进行签名。In this article we're going to see how you can leverage Azure Key Vault in a CI/CD pipeline, so that we can automatically sign our MSIX package as part of the process.

重要

本文中描述的过程基于一种名为 Azure SignTool 的开源工具,它既适用于 Azure Pipelines,也适合 GitHub Actions。The process described in this article is based on an open source tool called Azure SignTool, which works both with Azure Pipelines and GitHub Actions. 如果你使用的是 Azure Pipelines,那么还可将 MSIX 扩展Azure Key Vault 任务结合使用。If you're using Azure Pipelines, you can leverage also the MSIX Extensions in combination with the Azure Key Vault task.

先决条件Prerequisites

  • 一个 Azure 帐户。An Azure account. 如果还没有 Azure 帐户,请在此处开始。If you do not already have an Azure account, start here.
  • 一个 Azure Key Vault。An Azure Key Vault. 有关详细信息,请参阅创建 Key VaultFor more info, see Create a Key Vault.
  • 一个导入到 Azure Key Vault 的有效包签名证书。A valid package signing certificate imported into Azure Key Vault. Azure Key Vault 生成的默认证书不能用于代码签名。The default certificate generated by Azure Key Vault will not work for code signing. 要详细了解如何创建包签名证书,请参阅为包签名创建证书For details on how to create a package signing certificate, see Create a certificate for package signing.
  • 一个 CI/CD 管道,它会生成一个托管在 Azure Pipelines 或 GitHub Actions 上的 MSIX 包。A CI/CD pipeline which generates a MSIX package, hosted on Azure Pipelines or GitHub Actions. 有关详细信息,请查看使用 YAML 文件配置 CI/CD 管道For more info, see Configure CI/CD pipeline with YAML file.

在 Azure 上注册应用程序Register an application on Azure

为了将包注册为 CI/CD 管道的一部分,我们将使用一种名为 Azure SignTool 的工具。To sign the package as part of the CI/CD pipeline, we're going to use a tool called Azure SignTool. 它的工作方式与 Windows 10 SDK 中包含的标准 SignTool 实用程序类似,但它会连接到 Azure Key Vault 来使用其中一个可用证书,而不是使用本地证书。It works like the standard SignTool utility included in the Windows 10 SDK but, instead of using a local certificate, it connects to Azure Key Vault to use one of the available certificates. 但若要建立连接,我们首先需要在 Azure 上注册一个应用程序,它将向我们提供启用 Azure SignTool 来针对 Azure Key Vault 服务进行身份验证所需的凭据。To establish the connection, however, we first need to register an application on Azure, which will give us the credentials we need to enable Azure SignTool to authenticate against our Azure Key Vault service.

打开 Azure 门户,然后在可用服务中选择“Azure Active Directory”。Open the Azure Portal and choose Azure Active Directory among the available services. 单击“应用注册”,然后选择“新建注册”来开始该流程 。Click on App Registrations and choose New registration to start the process. 为应用程序指定一个青春(例如,它在下图中是 SignToolForContoso),然后保留默认设置。Give a name to the application (for example, it's SignToolForContoso in the image below) and then leave the default settings.

在 Azure 上注册应用程序

接下来是将应用程序看作是公共客户端,因为我们的方案不需要重定向 URI。The next step is to treat the application as a public client, since we are in a scenario where a redirect URI is not needed. 转到“身份验证”部分,在“高级设置”下,将“将应用程序视为公共客户端”开关更改为“是” 。Move to the Authentication section and, under Advanced settings, change the switch Treat application as a public client to Yes.

设定高级设置

最后一步是创建客户端密码,它是我们在从 Azure SignTool 进行身份验证时需要使用的密码。The last step is to create a client secret, which is the password we'll need to authenticate from the Azure SignTool. 移动到“证书和密码”部分,然后单击“新建客户端密码” 。Move to the Certificates & secrets section, then click on New client secret. 为其指定一个名称,选择过期时间,然后按“添加”按钮。Give it a name, choose an expiration and then press the Add button. 你将被重定向回到主页面,密码将在这里与其值一并列出。You will be redirected back to the main page, where the secret will be listed together with its value. 请一定要复制它并将其存储在安全的位置。Make sure to copy it and to store it somewhere safe. 你将无法再次检索到它。You won't be able to retrieve it again. 刷新页面后,密码将被掩码,没有任何方式来显示它。As soon as you refresh the page, the secret will be masked and there won't be any way to reveal it. 此时你只能生成新的密码。Your only option will be to generate a new secret.

你需要与客户端密码一起保存的最后一项信息是应用程序标识符。There's one last information you need to save together with the client secret: the application identifier. 单击“概述”回到应用程序的主页,然后在上半部分中,查找“应用程序(客户端) ID”值 :Go back to the home of the application (by clicking on Overview) and, in the upper section, look for the value Application (client) ID:

应用程序 ID

启用对 Azure Key Vault 的访问权限Enable access to Azure Key Vault

接下来是配置我们刚才创建的用于访问 Azure Key Vault 服务的 Azure 应用程序。The next step is to configure the Azure application we have just created to access to our Azure Key Vault service. 在 Azure 门户中,转到包含你要用于对 MSIX 包进行签名的证书的 Azure Key Vault 实例。From the Azure portal, move to the Azure Key Vault instance which holds the certificate you want to use to sign your MSIX package. 转到“访问策略”部分,然后单击“添加访问策略” 。Go to the Access policies section and click on Add Access Policy. 该工具支持选择其中一种可用的模板来定义我们想要授予的权限,但在我们的方案中,这些模板都不适合。The tool supports choosing one of the available templates to define the permissions we want to grant but, in our scenario, no one of them is the right fit. 同样,我们将需要使用下拉列表来手动设置以下选项:As such, we'll need to manually set, using the dropdowns, the following options:

  • 在“密钥权限”下,启用“签名”选项 。Under Key permissions, enable the Sign option.
  • 在“证书权限”下,启用“获取”选项 。Under Certificate permissions, enable the Get option.

最后一个重要的步骤是指定哪个应用程序将访问此策略。The last important step is to specify which application is going to access to this policy. 单击“选择主体”,使用 Azure 应用程序的名称搜索你在上一步中创建的应用。Click on Select principal and search for the Azure application you have created in the previous step by using its name. 在本例中,它被命名为“SignToolForContoso”。In the example, it's called SignToolForContoso.

选择主体

找到后,按“选择”。Once you have found it, press Select. 策略应如下所示。This is how the policy should look like.

添加访问策略

完成此过程后,单击“添加”来创建策略。When you have completed the process, click Add to create the policy.

使用 Azure SignTool 在本地对包进行签名Use Azure SignTool to sign the package locally

Azure 配置现已完成,我们可使用 Azure SignTool 来对包进行签名了。Now that the Azure configuration is completed, we can use Azure SignTool to sign the package. 在本部分,我们将在本地使用该工具来熟悉相关用法。In this section, we'll use the tool locally to familiarize with it. 在接下来的部分中,我们会将它用作 CI/CD 管道的一部分。In the next sections, we're going to use it as part of a CI/CD pipeline.

该工具以 .NET 全局工具的形式提供。The tool is available as a .NET global tool. 请确保已安装最新的 .NET SDK,然后打开命令提示符并启动以下命令:Make sure to have the latest .NET SDK installed, then open a command prompt and launch the following command:

dotnet tool install --global AzureSignTool 

现在,你就可使用 AzureSignTool 命令对你的包进行签名,它要求使用以下参数:Now you can sign your package using the AzureSignTool command, which requires the following parameters:

  • kvu 是 Azure Key Vault 的 URL。kvu is the URL of your Azure Key Vault. 你可在 Azure 门户中服务主页中的“DNS 名称”下找到它。You can find it in the main page of the service in the Azure portal, under DNS Name.
  • kvi 是你已注册的 Azure 应用的应用程序 ID,你之前已记下此信息。kvi is the application id of the Azure app you have registered and that you have previously noted.
  • kvs 是你之前生成的客户端密码,你之前已记下此信息。kvs is the client secret you have previously generated and that you have previously noted.
  • kvc 是要使用的证书的易记名称。kvc is the friendly name of the certificate you want to use.
  • tr 是时间戳服务器的 URL。tr is the URL of a timestamp server. 通过使用此选项,当证书过期后,我们也能使我们的包正常工作。By using this option, we can enable our package to work also when the certificate will expire.
  • v 是我们要对其进行签名的 MSIX 包的路径。v is the path of the MSIX package we want to sign.

下面是一个示例命令:This is a sample command:

AzureSignTool sign -kvu "https://contosoexpenses-blog.vault.azure.net/" -kvi "64fae35e-cb84-4b9f-86eb-5170d169316d" -kvs "this-is-the-secret" -kvc "MyCertificate" -tr http://timestamp.digicert.com -v .\MyContosoApp.msix

将 Azure SignTool 与 Azure Pipelines 配合使用Using Azure SignTool with Azure Pipelines

本部分假设你已有一个 CI/CD 管道用于在 Azure Pipelines 上配置有 YAML 文件的 Windows 应用程序,如此处所述。This section assumes you already have a CI/CD pipeline for a Windows application configured with a YAML file on Azure Pipelines, as explained here.

首先,需要创建几个变量来存储 Azure SignTool 连接到 Azure Key Vault 所需的信息。At first, you will need to create a few variables to store the information required by Azure SignTool to connect to Azure Key Vault. 在 Azure DevOps 中,选择你的管道,然后按顶部的“编辑”按钮。In Azure DevOps select your pipeline and press the Edit button at the top. 进入 YAML 编辑器后,单击顶部的“变量”按钮来打开面板。Once you are in the YAML editor, click on the Variables button at the top to open the panel. 单击“+”按钮来添加以下变量:You're going to click on the + button to add the following variables:

  • AzureKeyVaultName,带有保管库的易记名称。AzureKeyVaultName, with the friendly name of your vault.
  • AzureKeyVaultUrl,带有保管库的 URL。AzureKeyVaultUrl, with the URL of your vault.
  • AzureKeyVaultClientId,带有 Azure 应用程序的 ID。AzureKeyVaultClientId, with the application id of your Azure application.
  • AzureKeyVaultClientSecret,带有 Azure 应用程序的客户端密码。AzureKeyVaultClientSecret, with the client secret of your Azure application.

创建每个变量时,都请务必启用“保留此值”密码选项。When you create each variable, make sure to enable the Keep this value secret option. 它将确保有权访问管道的其他人无法查看其值。It will make sure that other people who have access to the pipeline won't be able to see their values.

添加变量

现在,你可添加 .NET Core 任务在代理上安装 Azure SignTool,来自定义现有 YAML 管道。Now you can customize your existing YAML pipeline by adding a .NET Core task to install Azure SignTool on the agent. 这是要添加的 YAML:This is the YAML to add:

- task: DotNetCoreCLI@2
  displayName: 'Install Azure SignTool'
  inputs:
    command: custom
    custom: tool
    arguments: 'install --global AzureSignTool'

下一步是添加 PowerShell 任务来执行将对包进行签名的命令。The next step is to add a PowerShell task to execute the command that will sign the package. 一旦 MSIX 包创建后,就只能在生成过程结束时执行此任务。You must perform this task only at the end of the build process, once the MSIX package has been created.

- powershell: '& AzureSignTool sign -kvu $(AzureKeyVaultUrl) -kvi $(AzureKeyVaultClientId) -kvs $(AzureKeyVaultClientSecret) -kvc $(AzureKeyVaultName) -tr http://timestamp.digicert.com -v "$(System.DefaultWorkingDirectory)\MyPipeline\MyContosoApp\MyContosoApp.msix"'
  displayName: 'Sign the package'

此命令与我们用于在本地对包签名的命令很相似。The command is similar to the one we have used to sign the package locally. 只存在下述区别:The only differences are:

  • 我们会将我们创建的变量与 $(Variable-Name) 语法相结合,而不是对各种参数使用固定值Instead of using fixed values for the various parameters, we're using the variables we have created with the syntax $(Variable-Name)
  • MSIX 包的路径指向在生成结束时创建的 MSIX 包所在的代理上的文件夹。The path of the MSIX package points to the folder on the agent where the MSIX package is created at the end of the build.

将 Azure SignTool 与 GitHub Actions 结合使用Using Azure SignTool with GitHub Actions

本部分假设你已有一个 CI/CD 管道用于在 GitHub Actions 上配置有 YAML 文件的 Windows 应用程序,如此处所述。This section assumes you already have a CI/CD pipeline for a Windows application configured with a YAML file on GitHub Actions, as explained here.

就像在 Azure 管道上操作的一样,首先需要安全地存储凭据。As first step, like we did on Azure Pipeline, we need to safely store the credentials. GitHub 使用密码,它们可添加到存储库的设置中。GitHub uses secrets and they can be added in the settings of your repository. 进入托管你的 Windows 应用程序的 GitHub 存储库后,单击“设置”,然后转到“密码” 。Once you're on the GitHub repository which hosts your Windows application, click on Settings then move to Secrets.

与使用 Azure Pipelines 时的操作类似,你将单击“新建密码”来创建 4 个密码:Similarly to what you did with Azure Pipelines, you're going to click on New secret to create four secrets:

  • AzureKeyVaultName,带有保管库的易记名称。AzureKeyVaultName, with the friendly name of your vault.
  • AzureKeyVaultUrl,带有保管库的 URL。AzureKeyVaultUrl, with the URL of your vault.
  • AzureKeyVaultClientId,带有 Azure 应用程序的 ID。AzureKeyVaultClientId, with the application id of your Azure application.
  • AzureKeyVaultClientSecret,带有 Azure 应用程序的客户端密码。AzureKeyVaultClientSecret, with the client secret of your Azure application.

与 Azure 管道的区别是密码隐式隐藏,因此你不必启用任何选项来保护它们。The difference with Azure Pipeline is that secrets are implicitly hidden, so you won't have to enable any option to protect them.

现在,通过存储库的“操作”选项卡,你可打开现有工作流并添加进行签名所需的任务。Now, through the Actions tab of your repository, you can open your existing workflow and add the tasks you need to perform the signing. 第一项任务将在代理上安装 Azure SignTool:The first one will install AzureSign Tool on the agent:

- name: Install AzureSignTool
  run: dotnet tool install --global AzureSignTool

第二项任务将对包进行签名;因此,必须在 Visual Studio 生成完成且 MSIX 包已生成之后,再执行此任务。The second one will sign the package and, as such, it must be executed after the Visual Studio build has been completed and the MSIX package has been generated.

 - name: Sign package
   run: |
        Get-ChildItem -recurse -Include **.msix | ForEach-Object {
        $msixPath = $_.FullName
        & AzureSignTool sign -kvu "${{ secrets.AzureKeyVaultUrl }}" -kvi "${{ secrets.AzureKeyVaultClientId }}" -kvs "${{ secrets.AzureKeyVaultClientSecret }}" -kvc ${{ secrets.AzureKeyVaultName }} -tr http://timestamp.digicert.com -v $msixPath
        }

与我们在 Pipelines 中采用的任务相比,此任务中有几项区别。There are a few differences in this task compared to the one we used in Azure Pipelines. 第一个是 GitHub 使用不同的语法来访问密码,即使用 ${{ secrets.SECRET_NAME }}。The first one is that GitHub uses a different syntax to access to the secrets, which is ${{ secrets.SECRET_NAME }}. 因此,各种参数填充了我们之前在“密码”部分中创建的值。As such, the various parameters are filled with the values we have previously created in the Secrets section. 另一点区别是你需要使用其他方法来查找用于签名的 MSIX 包。The other one is that you need to use a different approach to find the MSIX packages to sign. 此任务使用 PowerShell 脚本而不是指向特定的 MSIX 包,该脚本会循环访问生成输出中存储的所有文件。The task, instead of pointing to a specific MSIX package, uses a PowerShell script which iterates through all the files stored in the build output. 如果文件具有 MSIX 扩展名,则它将使用 AzureSignTool 命令对其进行签名。If the file has the MSIX extension, then it will use the AzureSignTool command to sign it.

部署包Deploy the package

无论你选择的 CI/CD 平台如何,在流程结束时,你都将有一个用 Azure Key Vault 中存储的证书签名的 MSIX 包。Regardless of the CI/CD platform of your choice, at the end of the flow you will have a MSIX package signed with your certificate stored on Azure Key Vault. 现在,你可使用任何其他可用任务来通过你首选的分发部署包,这些分发包括 Microsoft Store、网站和 Microsoft Store 等。Now you can use any other available task to deploy the package using your preferred choice of distribution: the Microsoft Store, a website, Microsoft Intune, etc.