У меня есть сертификат Authenticode (.pfx), который я использую для подписи исполняемых файлов.
Как настроить Team Build так, чтобы он подписывал каждый исполняемый файл (.exe, .dll , ...) автоматически при сборке проекта?
У меня есть сертификат Authenticode (.pfx), который я использую для подписи исполняемых файлов.
Как настроить Team Build так, чтобы он подписывал каждый исполняемый файл (.exe, .dll , ...) автоматически при сборке проекта?
Вот метод, который мы используем:
Выгрузите проект WiX и выберите Edit.
Прокрутите вниз, где вы найдете <Import Project="$(WixTargetsPath)" />
Добавьте новую строку непосредственно над ним: <Import Project="ProjectName.custom.targets" />
Мы используем соглашение об именах «ProjectName.custom.targets», но файл можно назвать как угодно.
Создайте новый 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=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "%(_SourceAssemblyToSign.Identity)"" Condition="'$(AuthenticodeTimestamp)' != ''" />
<Exec Command=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /v "%(_SourceAssemblyToSign.Identity)"" 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=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "$(_MsiFileToSign)"" Condition="'$(AuthenticodeTimestamp)' != ''" />
<Exec Command=""$(_SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /v "$(_MsiFileToSign)"" Condition="'$(AuthenticodeTimestamp)' == ''" />
</Target>
</Project>
Создайте тестовый сертификат аутентификации (мы назвали наш AuthenticodeTest.pfx) и поместите его в систему управления версиями - путь к нему задается в свойстве AuthenticodeCertFile. Чтобы проверить это, запустите msbuild в командной строке и измените свойство OutDir - т.е. / msbuild Test.sln / p: OutDir = C: \ Test
Некоторые настройки потребуются, если:
Чтобы запустить финальную сборку, выберите «Очередь новой сборки» в TFS. Щелкните «Параметры» и разверните «Дополнительно». В разделе «Аргументы MSBuild» добавьте /p:AuthenticodeCertFile=ProductionCertFile.pfx /p:AuthenticodePassword=Secret
. Обратите внимание, что это может быть не совсем безопасно - может быть сложно заставить агент сборки найти файл PFX, не проверяя его, и пароль может быть зарегистрирован в выходных данных сборки. В качестве альтернативы вы можете создать для этого специальный заблокированный агент сборки или запустить сборку локально из командной строки, но, очевидно, это не будет среда «чистой комнаты». Возможно, стоит создать специальный заблокированный «чистый» сервер специально для этой цели.
Я не согласен. Поскольку сертификат подписи кода должен быть установлен на компьютере сборки для выполнения подписи, почему бы не подписывать все, что создается на этом компьютере каждый раз, когда он создается? Компьютер находится в «опасности», потому что на нем установлен сертификат подписи кода, поэтому его необходимо каким-либо образом защитить (физическая безопасность и безопасность системы). Если он защищен, почему бы не позволить ему выполнять работу, для которой он был предназначен, готовить файлы к доставке, последовательно, с возможностью повторения, каждый раз?
К сожалению, ответ «не надо» также кажется стандартным ответом Microsoft, поскольку они, похоже, почти не поддерживают в MSBuild цикл по списку имен файлов, вызывая программу один раз для каждого имени файла в списке. Я нашел способы передать сгенерированный с помощью подстановочных знаков список файлов в программу Signtool.exe, но она может обрабатывать только один файл за раз.
Я боюсь (для меня), что он вернулся к написанию командного файла, который перебирает свои аргументы и вызывает signtool для каждого аргумента. Написание командных файлов для общей задачи подписания выходных данных сборки заставляет меня думать, что MSBuild действительно не настолько зрелая система сборки, какой должна быть. Либо так, либо у signtool неправильный интерфейс. В любом случае подписывание нескольких файлов без перечисления имени каждого файла для подписи кажется неприемлемым для MSBuild.
Мой ответ основан на ответе ShadowChaser. разница в том, что моя цель MSBuild будет подписывать указанный файл или, если ничего не указано, она будет подписывать выходные данные сборки проекта, из которого была вызвана цель.
сохраните следующее в файле целей, скажем, signed.custom.targets. как только вы его сохраните, просто включите его в свой csproj или wixproj и используйте цель для подписи вашего вывода. это также будет работать на локальной машине разработчика, поскольку файл sign.properties (это гибридная среда разработки, поэтому мы не хотели указывать свойства сертификата дважды) не существует в локальном поле разработчика.
чтобы подписать файл из командной строки с помощью этого, вы можете
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=""$(SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "$(_MsiFileToSign)"" Condition="'$(PropsInOneLine)' != ''" />
</Target>
<Target Name="SignMsi"
DependsOnTargets="CreateProperties" >
<PropertyGroup>
<_MsiFileToSign>$(TargetPath)</_MsiFileToSign>
</PropertyGroup>
<Message Text=" File Name to sign= $(_MsiFileToSign)" />
<Exec Command=""$(SignToolPath)" sign /f "$(AuthenticodeCertFile)" /p "$(AuthenticodePassword)" /t $(AuthenticodeTimestamp) /v "$(_MsiFileToSign)"" 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>
Не надо.
Вы не хотите автоматически подписывать сборки. Большинство сборок в любом случае не нужно подписывать; они используются только для автоматизации тестов. Некоторые сборки могут быть переданы вашим штатным тестерам. Но только сборки, которые вы действительно выпускаете за пределами своей организации, нуждаются в подписях Authenticode.
В этом случае у вас все равно должен быть этап проверки вручную после подписания. Таким образом, подписание вручную не добавляет дополнительных ручных действий в процесс выпуска, а автоматизация очень мало экономит время. В обмен на это в вашей организации будет гораздо меньше подписанных файлов, и вы сможете дать гораздо более надежные гарантии относительно имеющихся файлов.