11

我有一个用于签署可执行文件的Authenticode证书 (.pfx)。

如何配置Team Build以便在构建项目时自动签署每个可执行文件(.exe、.dll、...)?

4

4 回答 4

17

这是我们使用的方法:

  1. 卸载 WiX 项目并选择编辑

  2. 滚动到底部,您可以在其中找到<Import Project="$(WixTargetsPath)" />

  3. 在其上方添加一个新行:<Import Project="ProjectName.custom.targets" /> 我们使用命名约定“ProjectName.custom.targets”,但文件可以命名为您想要的任何名称。

  4. 创建一个名为 ProjectName.custom.Targets 的新 XML 文件并将以下代码放入其中:

    <?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>
    

创建一个测试验证码证书(我们将其命名为 AuthenticodeTest.pfx)并将其放在源代码管理中 - 它的路径在 AuthenticodeCertFile 属性中设置。要对其进行测试,请在命令行运行 msbuild 并更改 OutDir 属性 - 即/ msbuild Test.sln /p:OutDir=C:\Test

如果出现以下情况,则需要进行一些自定义:

  • 如果你不想使用“私人”物品组(我被骗了)
  • 如果您不使用 WiX 项目参考
  • 如果您的 cab 文件与 MSI 分开,则它们也需要签名

要运行最终构建,请在 TFS 中选择“Queue New Build”。单击“参数”并展开“高级”。在“MSBuild 参数”下添加/p:AuthenticodeCertFile=ProductionCertFile.pfx /p:AuthenticodePassword=Secret. 请注意,这可能并不完全安全——让构建代理在不签入的情况下找到 PFX 文件可能会很棘手,并且密码可能会记录在构建输出中。或者,您可以为此创建一个特殊的锁定构建代理,或者在命令行本地运行构建 - 但显然这不会是一个“洁净室”环境。专门为此目的创建一个特殊的锁定“干净”服务器可能是值得的。

于 2011-06-07T15:26:05.653 回答
3

我不同意。由于必须在构建计算机上安装代码签名证书才能执行签名,为什么不在每次构建该计算机时对构建在该计算机上的所有内容进行签名呢?计算机处于“风险”状态,因为它安装了代码签名证书,因此需要以某种方式保护它(物理安全和系统安全)。如果它受到保护,为什么不让它做它打算做的工作,每次都一致地、可重复地准备要交付的文件?

不幸的是,“不要”的答案似乎也是微软的标准答案,因为它们似乎在 MSBuild 中几乎不支持循环文件名列表,为列表中的每个文件名调用一次程序。我找到了将通配符生成的文件列表传递给 Signtool.exe 程序的方法,但它一次只能处理一个文件。

我担心(对我来说)它又回到了编写一个批处理文件,该文件循环遍历其参数并为每个参数调用signtool。为签署构建输出的常见任务编写批处理文件让我认为 MSBuild 确实不像它应有的那样成熟的构建系统。要么,要么 signtool 有错误的接口。在任何一种情况下,在不枚举要签名的每个文件的名称的情况下对多个文件进行签名似乎都是 MSBuild 的“不可行”。

于 2009-10-29T23:55:55.260 回答
2

我的回答建立在 ShadowChaser 的回答之上。我的 MSBuild 目标将对指定文件进行签名,或者如果未指定任何文件,它将对调用目标的项目的构建输出进行签名。

将以下内容保存在目标文件中,例如signing.custom.targets。保存后,只需将其包含在您的 csproj 或 wixproj 中,然后使用目标对您的输出进行签名。这也将在本地开发机器上工作,因为本地开发机器上不存在 sign.properties(它是一个混合开发环境,所以我们不想指定两次证书属性)文件。

使用这个从命令行签署文件,你可以

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

签名.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>
于 2015-11-20T16:50:22.483 回答
-5

不。

您不想自动签署构建。无论如何,大多数构建都不需要签名。它们仅用于自动化测试。一些构建可能会交给您的内部测试人员。但只有您在组织外部实际发布的构建才需要 Authenticode 签名。

在这种情况下,无论如何,您都应该在签名后进行手动验证步骤。因此,手动签名不会在发布过程中插入额外的手动步骤,并且自动化可以节省很少的时间。作为交换,您组织中的签名文件将会少得多,并且您可以对这些文件做出更强有力的保证。

于 2009-08-28T14:57:15.513 回答