Безопасная подпись приложений ClickOnce в конвейере Azure DevOps

Я пытаюсь сделать CI/CD в DevOps Azure с помощью приложения ClickOnce. Как безопасно сделать свой сертификат подписи кода доступным во время сборки при использовании размещенного агента?

Примечание. Мне известно, что вы можете использовать сценарий, как указано в разделе Ошибка службы развертывания / сборки команды службы Visual Studio. Однако такой подход небезопасен. Сертификат будет загружен в хранилище сертификатов учетной записи, под которой работает размещенный агент. Это позволит агенту и, следовательно, другим учетным записям DevOps Azure потенциально получать доступ и использовать сертификат.

0 ответов

Решение проблемы заключается в переопределении встроенной задачи SignFile. Интересно, что задача SignFile использует встроенную функцию в Microsoft.Build.Tasks.Deployment.ManifestUtilities.SecurityUtilities.SignFile которая имеет две перегрузки, одна, которая берет отпечаток большого пальца, и другая, которая принимает файл и пароль.

Решение состоит в том, чтобы создать новую задачу, которая может ссылаться на другую перегрузку. Поскольку мы не можем изменить вызывающий SignFile, нам нужно поддерживать ту же сигнатуру и помещать соответствующие переменные в переменные окружения. В этом случае "CertificateFile" и "CertificatePassword".

Затем обратитесь к этим двум в перезаписанном SignFile. Я создал новый целевой файл (filesign.targets) и поместил туда код. Проверял это в моем репозитории и ссылался на него из основного файла (ов) проекта.<Import Project="filesign.targets" />

Таким образом, мы также можем хранить наши ключевые файлы в хранилище ключей Azure, загружать их во встроенную и назначать им уникальный пароль только для этой сборки.

Файл целей содержит новую задачу FileSign:

<?xml version="1.0" encoding="Windows-1252"?>
<!--
***********************************************************************************************
Microsoft.VisualStudio.Tools.Office.targets

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy.  Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.

This file defines the steps in the standard build process specific for Visual Studio Tools for 
Office projects.

Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <UsingTask TaskName="SignFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">

    <ParameterGroup>
      <SigningTarget Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem" />
      <CertificateThumbprint ParameterType="System.String" />
      <TargetFrameworkVersion ParameterType="System.String" />
      <TimestampUrl ParameterType="System.String" />
      <CertificateFile ParameterType="System.String" />
      <CertificatePassword ParameterType="System.String" />
    </ParameterGroup>
    <Task>
      <Reference Include="mscorlib" />
      <Reference Include="Microsoft.Build.Tasks.Core" />
      <Using Namespace="System" />
      <Code Type="Fragment" Language="cs">
        <![CDATA[
                var EnvCertFile = System.Environment.GetEnvironmentVariable("CertificateFile");

                Log.LogMessage("CertFile:!!" + EnvCertFile);

                if (string.IsNullOrWhiteSpace(CertificateFile) && string.IsNullOrWhiteSpace(EnvCertFile)) {
                    var signFile = new Microsoft.Build.Tasks.SignFile();
                    signFile.CertificateThumbprint = CertificateThumbprint;
                    signFile.SigningTarget = SigningTarget;
                    signFile.TargetFrameworkVersion = TargetFrameworkVersion;
                    signFile.TimestampUrl = TimestampUrl;
                    return signFile.Execute();
                } else {

                    var certificate = string.IsNullOrWhiteSpace(CertificateFile) ? EnvCertFile : CertificateFile;
                    var EnvCertPassword = System.Environment.GetEnvironmentVariable("CertificatePassword");
                    var certificatePassword = string.IsNullOrWhiteSpace(CertificatePassword) ? EnvCertPassword : CertificatePassword;
                    var testString = new System.Security.SecureString();
                    // Use the AppendChar method to add each char value to the secure string.
                    if (!string.IsNullOrWhiteSpace(certificatePassword))
                        foreach (char ch in certificatePassword)
                            testString.AppendChar(ch);
                    Microsoft.Build.Tasks.Deployment.ManifestUtilities.SecurityUtilities.SignFile(certificate, testString,
                        TimestampUrl == null ? null : new Uri(TimestampUrl),
                        SigningTarget.ItemSpec);
                    return true;
                }

]]>
      </Code>
    </Task>
  </UsingTask>
</Project>

Код основан на: https://gist.github.com/KirillOsenkov/4cd32c40bffd3045f77e

Ссылки: https://github.com/Microsoft/msbuild/blob/fc10ea8ce260b764bb9fa5033b327af9fefcaabe/src/Tasks/ManifestUtil/SecurityUtil.cs https://github.com/Microsoft/msbuild/blob/master/src/Tasks/SignFile.cs

Другие вопросы по тегам