Подписать каждый исполняемый файл с помощью сертификата Authenticode через MSBuild - PullRequest
9 голосов
/ 28 августа 2009

У меня есть Authenticode сертификат (.pfx), который я использую для подписи исполняемых файлов.

Как настроить Team Build , чтобы он автоматически подписывал каждый исполняемый файл (.exe, .dll, ...) при сборке проекта?

Ответы [ 4 ]

16 голосов
/ 07 июня 2011

Вот метод, который мы используем:

  1. Выгрузите проект WiX и выберите Редактировать

  2. Прокрутите вниз, где вы можете найти <Import Project="$(WixTargetsPath)" />

  3. Добавить новую строку сразу над ней: <Import Project="ProjectName.custom.targets" /> Мы используем соглашение об именах «ProjectName.custom.targets», но имя файла может быть любым, как вам угодно.

  4. Создайте новый XML-файл с именем ProjectName.custom.Targets и поместите в него следующий код:

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <PropertyGroup>
        <!-- replace the contents of this with your private test authenticode certificate -->
        <AuthenticodeCertFile Condition="'$(AuthenticodeCertFile)' == ''">$(MSBuildProjectDirectory)\AuthenticodeTest.pfx</AuthenticodeCertFile>
      </PropertyGroup>
    
      <!-- this gets the path to signtool.exe and places it in the _SignToolSdkPath property -->
      <Target Name="_GetSignToolPath">
        <GetFrameworkSdkPath>
          <Output TaskParameter="Path" PropertyName="_SignToolSdkPath" />
        </GetFrameworkSdkPath>
        <PropertyGroup>
          <_SignToolPath>$(_SignToolSdkPath)bin\signtool.exe</_SignToolPath>
        </PropertyGroup>
      </Target>
    
      <!-- This gets a list of all of the "referenced" assembies used by the installer project --> 
      <!-- Unfortunately, I cheated and used an "internal" item list - you could replace this with each specific assembly but it gets complicated if your build output is redirected -->
      <Target Name="_GetSourceAssembliesToSign" DependsOnTargets="ResolveReferences">
        <!-- Kludge - not supposed to target internal items, but there are no other options -->
        <CreateItem Include="@(_ResolvedProjectReferencePaths)">
          <Output ItemName="_SourceAssemblyToSign" TaskParameter="Include" />
        </CreateItem>
      </Target>
    
      <!-- This signs the assemblies in the @(_SourceAssemblyToSign) item group -->
      <!-- Note that it only executes when build output is redirected ie/ on TFS Build or when OutDir is changed -->  
      <!-- Authenticode timestamp is optional - doesn't make sense to timestamp the test certificate -->
      <Target Name="_AuthenticodeSignSourceAssemblies" AfterTargets="BeforeBuild" DependsOnTargets="_GetSignToolPath;_GetSourceAssembliesToSign" Condition="'$(AuthenticodeCertFile)' != '' and '$(OutDir)' != '$(OutputPath)'">
        <Exec Command="&quot;$(_SignToolPath)&quot; sign /f &quot;$(AuthenticodeCertFile)&quot; /p &quot;$(AuthenticodePassword)&quot; /t $(AuthenticodeTimestamp) /v &quot;%(_SourceAssemblyToSign.Identity)&quot;" Condition="'$(AuthenticodeTimestamp)' != ''" />
        <Exec Command="&quot;$(_SignToolPath)&quot; sign /f &quot;$(AuthenticodeCertFile)&quot; /p &quot;$(AuthenticodePassword)&quot; /v &quot;%(_SourceAssemblyToSign.Identity)&quot;" Condition="'$(AuthenticodeTimestamp)' == ''" />
      </Target>
    
      <!-- This signs the MSI file itself -->
      <!-- Note that additional changes may be needed if your CAB files are separate - those would need to be signed as well -->
      <!-- Note that it only executes when build output is redirected ie/ on TFS Build or when OutDir is changed -->  
      <Target Name="_AuthenticodeSignMsi" AfterTargets="SignMsi" DependsOnTargets="_GetSignToolPath" Condition="'$(AuthenticodeCertFile)' != '' and '$(OutDir)' != '$(OutputPath)'">
        <PropertyGroup>
          <_MsiFileToSign>$(TargetDir)%(CultureGroup.OutputFolder)$(TargetName)$(TargetExt)        </_MsiFileToSign>
        </PropertyGroup>
    
        <Exec Command="&quot;$(_SignToolPath)&quot; sign /f &quot;$(AuthenticodeCertFile)&quot; /p &quot;$(AuthenticodePassword)&quot; /t $(AuthenticodeTimestamp) /v &quot;$(_MsiFileToSign)&quot;" Condition="'$(AuthenticodeTimestamp)' != ''" />
        <Exec Command="&quot;$(_SignToolPath)&quot; sign /f &quot;$(AuthenticodeCertFile)&quot; /p &quot;$(AuthenticodePassword)&quot; /v &quot;$(_MsiFileToSign)&quot;" Condition="'$(AuthenticodeTimestamp)' == ''" />
      </Target>
    </Project>
    

Создайте тестовый сертификат authenticode (мы назвали наш AuthenticodeTest.pfx) и поместите его в систему контроля версий - путь к нему задается в свойстве AuthenticodeCertFile. Чтобы проверить это, запустите msbuild в командной строке и измените свойство OutDir - т.е. / msbuild Test.sln / p: OutDir = C: \ Test

Некоторые настройки потребуются, если:

  • Если вы не хотите использовать «частную» группу предметов (я обманул)
  • Если вы не используете ссылки на проекты WiX
  • Если ваши cab-файлы отделены от MSI, их также необходимо подписать

Чтобы запустить финальную сборку, выберите «Очередь новой сборки» в TFS. Нажмите «Параметры» и разверните «Дополнительно». В разделе «Аргументы MSBuild» добавьте /p:AuthenticodeCertFile=ProductionCertFile.pfx /p:AuthenticodePassword=Secret. Обратите внимание, что это может быть не совсем безопасно - может быть сложно заставить агент сборки найти файл PFX, не регистрируя его, и пароль может быть зарегистрирован в выходных данных сборки. В качестве альтернативы вы можете создать для этого специальный заблокированный агент сборки или запустить сборку локально из командной строки - но очевидно, что это не будет «чистая комната». Возможно, стоит создать специальный заблокированный «чистый» сервер специально для этой цели.

3 голосов
/ 30 октября 2009

Я не согласен. Так как сертификат подписи кода должен быть установлен на компьютере сборки для выполнения подписи, почему бы не подписывать все, что собрано на этом компьютере, каждый раз, когда он собирается? Компьютер находится «в опасности», поскольку на нем установлен сертификат подписи кода, поэтому его необходимо будет каким-то образом защитить (физическая безопасность и защита системы). Если он защищен, почему бы не позволить ему выполнить работу, для которой он был предназначен, каждый раз подготавливать файлы для доставки последовательно, многократно?

К сожалению, ответ «не» также кажется стандартным ответом Microsoft, так как в MSBuild они почти не поддерживают циклический перебор списка имен файлов, вызывая программу один раз для каждого имени файла в список. Я нашел способы передать сгенерированный подстановочный список файлов программе Signtool.exe, но она может обрабатывать только один файл за раз.

Я боюсь (для меня), что он вернулся к написанию командного файла, который перебирает свои аргументы и вызывает signtool для каждого аргумента. Написание командных файлов для обычной задачи подписания результатов сборки заставляет меня думать, что MSBuild на самом деле не настолько зрелая система сборки, как должна быть. Либо так, либо signtool имеет неправильный интерфейс. В любом случае, подписание нескольких файлов без перечисления имени каждого файла для подписи кажется «пустым» с MSBuild.

2 голосов
/ 20 ноября 2015

Мой ответ основан на ответе ShadowChaser. разница в том, что моя цель MSBuild подпишет указанный файл или, если он не указан, он подпишет выходные данные сборки проекта, из которого была вызвана цель.

сохраните следующее в файле целей, скажем, signature.custom.targets. как только вы сохраните его, просто включите его в ваш csproj или wixproj и используйте цель, чтобы подписать ваш вывод. это также будет работать на локальной машине dev, так как файл sign.properties (это гибридный dev env, поэтому мы не хотели указывать свойства cert дважды) не существует в локальной коробке dev.

чтобы подписать файл из командной строки, вы можете

MSBuild.exe signing.custom.targets /t:SignAssembly /p:_MsiFileToSign="..\Builds\Release\Setups\yourFileToSign.msi

определение signature.custom.targets

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <CertPath Condition= "'$(CertPath)' == ''">c:\dev\sign\</CertPath>
    <Config Condition="'$(Config)' == ''">Release</Config>
    <MSIProductVersion Condition ="'$(MSIProductVersion)' ==''">16.3</MSIProductVersion>
    <MSIBuildNumber Condition ="'$(MSIBuildNumber)' ==''">3207</MSIBuildNumber>
  </PropertyGroup>

  <PropertyGroup Condition="'$(OutputType)'=='Library'">
    <_MsiFileToSign Condition="'$(_MsiFileToSign)' ==''" >$(OutputPath)$(AssemblyName).dll</_MsiFileToSign>
  </PropertyGroup>
  <PropertyGroup Condition="'$(OutputType)'=='Exe'">
    <_MsiFileToSign Condition="'$(_MsiFileToSign)' ==''">$(OutputPath)$(AssemblyName).exe</_MsiFileToSign>
  </PropertyGroup>

  <Target Name="ReadProperties">
    <ReadLinesFromFile File="$(CertPath)sign.properties">
      <Output TaskParameter="Lines" PropertyName="PropsInOneLine" />
    </ReadLinesFromFile>
  </Target>
  <Target Name="CreateProperties" DependsOnTargets="ReadProperties">
        <PropertyGroup>
            <SignToolPath>$(CertPath)signtool.exe</SignToolPath>
            <AuthenticodeCertFile Condition="'$(PropsInOneLine)' != ''">$(CertPath)$([System.String]::Copy($(PropsInOneLine)).Split(';')[0].Split('=')[1])</AuthenticodeCertFile>
            <AuthenticodePassword Condition="'$(PropsInOneLine)' != ''">$([System.String]::Copy($(PropsInOneLine)).Split(';')[2].Split('=')[1])</AuthenticodePassword>
            <AuthenticodeTimestamp Condition="'$(PropsInOneLine)' != ''">$([System.String]::Copy($(PropsInOneLine)).Split(';')[4].Split('=')[1])</AuthenticodeTimestamp>
        </PropertyGroup>
    </Target>
  <Target Name="SignAssembly"
          DependsOnTargets="CreateProperties"  >
    <Message Text=" File Name to sign= $(_MsiFileToSign)" />
    <Exec Command="&quot;$(SignToolPath)&quot; sign /f &quot;$(AuthenticodeCertFile)&quot; /p &quot;$(AuthenticodePassword)&quot; /t $(AuthenticodeTimestamp) /v &quot;$(_MsiFileToSign)&quot;"  Condition="'$(PropsInOneLine)' != ''" />
  </Target>
  <Target Name="SignMsi"
          DependsOnTargets="CreateProperties"  >
        <PropertyGroup>

            <_MsiFileToSign>$(TargetPath)</_MsiFileToSign>
        </PropertyGroup>

        <Message Text=" File Name to sign= $(_MsiFileToSign)" />
        <Exec Command="&quot;$(SignToolPath)&quot; sign /f &quot;$(AuthenticodeCertFile)&quot; /p &quot;$(AuthenticodePassword)&quot; /t $(AuthenticodeTimestamp) /v &quot;$(_MsiFileToSign)&quot;"  Condition="'$(PropsInOneLine)' != ''" />
  </Target>
</Project>

включая его в ваш csproj

<Import Project="$(SolutionDir)signing.custom.targets" />
<Target Name="AfterBuild" DependsOnTargets="SignAssembly">
</Target>

чтобы включить его в свой wixproj

<Import Project="$(SolutionDir)signing.custom.targets" />
<Target Name="AfterBuild" DependsOnTargets="SignAssembly">
</Target>
0 голосов
/ 28 августа 2009

Не.

Вы не хотите автоматически подписывать сборки. Большинство сборок в любом случае не требует подписи; они используются только для автоматизации тестов. Некоторые сборки могут быть переданы вашим внутренним тестерам. Но только те сборки, которые вы фактически выпускаете за пределами своей организации, нуждаются в сигнатурах Authenticode.

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

...