I have a project for which I have built a WiX msi file. I also have a WiX bootstrapper (exe file) that checks for the existence of C++ 2005, installs it if not found and then installs the msi package. My project includes Crystal Reports as an msm file which is installed with the msi but requires C++ 2005 to install properly.
On the MSI project I have included the following post build event to digitally sign the msi file.
sign /f "$(ProjectDir)\myPFXFile.pfx" /p mySecretKey/d "My Program" /t http://timestamp.verisign.com/scripts/timstamp.dll /v "MyProgram.msi"
If I install just the msi it correctly identifies the Publisher when requesting elevated permission to install.
I have tried adding the same post-build event to the bootstrapper project as follows:
sign /f "$(ProjectDir)\myPFXFile.pfx" /p mySecretKey/d "My Program" /t http://timestamp.verisign.com/scripts/timstamp.dll /v "MyProgram Setup.exe"
When I attempt to install the exe file it correctly identifies the Publisher but then fails to install with the following from the log file:
[1604:2574][2013-12-04T11:49:51]i001: Burn v3.7.1224.0, Windows v6.2 (Build 9200: Service Pack 0), path: C:\Users\.....\MyProgram Setup.exe, cmdline: ''
[1604:2574][2013-12-04T11:49:51]i000: Setting string variable 'WixBundleLog' to value 'C:\Users\.....\MyProgram_20131204114951.log'
[1604:2574][2013-12-04T11:49:51]i000: Setting string variable 'WixBundleOriginalSource' to value 'C:\Users\.....\MyProgram Setup.exe'
[1604:2574][2013-12-04T11:49:51]i000: Setting string variable 'WixBundleName' to value 'MyProgram'
[1604:2574][2013-12-04T11:49:51]i100: Detect begin, 2 packages
[1604:2574][2013-12-04T11:49:51]i000: Setting string variable 'vcredist_x86' to value '1'
[1604:2574][2013-12-04T11:49:51]i000: Setting string variable 'vcredist_x64' to value '1'
[1604:2574][2013-12-04T11:49:51]i052: Condition 'vcredist_x86 AND (vcredist_x86 >= 1)' evaluates to true.
[1604:2574][2013-12-04T11:49:51]i101: Detected package: vcredist_x86, state: Present, cached: None
[1604:2574][2013-12-04T11:49:51]i101: Detected package: MyProgram, state: Absent, cached: None
[1604:2574][2013-12-04T11:49:51]i199: Detect complete, result: 0x0
[1604:2574][2013-12-04T11:49:53]i200: Plan begin, 2 packages, action: Install
[1604:2574][2013-12-04T11:49:53]w321: Skipping dependency registration on package with no dependency providers: vcredist_x86
[1604:2574][2013-12-04T11:49:53]i000: Setting string variable 'WixBundleRollbackLog_MyProgram' to value 'C:\Users\.....\MyProgram_20131204114951_0_MyProgram_rollback.log'
[1604:2574][2013-12-04T11:49:53]i000: Setting string variable 'WixBundleLog_MyProgram' to value 'C:\Users\.....\MyProgram_20131204114951_0_MyProgram.log'
[1604:2574][2013-12-04T11:49:53]i201: Planned package: vcredist_x86, state: Present, default requested: Present, ba requested: Present, execute: None, rollback: None, cache: No, uncache: No, dependency: None
[1604:2574][2013-12-04T11:49:53]i201: Planned package: MyProgram, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, cache: Yes, uncache: No, dependency: Register
[1604:2574][2013-12-04T11:49:53]i299: Plan complete, result: 0x0
[1604:2574][2013-12-04T11:49:53]i300: Apply begin
[1FF8:10F8][2013-12-04T11:49:58]i360: Creating a system restore point.
[1FF8:10F8][2013-12-04T11:49:59]i361: Created a system restore point.
[1FF8:10F8][2013-12-04T11:50:00]i000: Caching bundle from: 'C:\Users\.....\{6ab8eece-89c6-4417-905f-6d9c5136519d}\.be\MyProgram Setup.exe' to: 'C:\ProgramData\Package Cache\{6ab8eece-89c6-4417-905f-6d9c5136519d}\MyProgram Setup.exe'
[1FF8:10F8][2013-12-04T11:50:00]i320: Registering bundle dependency provider: {6ab8eece-89c6-4417-905f-6d9c5136519d}, version: 2.0.0.0
[1604:2FB4][2013-12-04T11:50:00]i336: Acquiring container: WixAttachedContainer, copy from: C:\Users\.....\MyProgram Setup.exe
[1604:2FB4][2013-12-04T11:50:00]i000: Setting string variable 'WixBundleLastUsedSource' to value 'C:\Users\.....'
[1604:24F8][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to extract all files from container.
[1604:2FB4][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to wait for operation complete.
[1604:2FB4][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to open container.
[1604:2FB4][2013-12-04T11:50:00]e000: Error 0x80004005: Failed to open container: WixAttachedContainer.
[1604:2FB4][2013-12-04T11:50:00]e312: Failed to extract payloads from container: WixAttachedContainer to working path: C:\Users\.....\{6ab8eece-89c6-4417-905f-6d9c5136519d}\C7C1FB4E513C19E0F5E8F6856FF2ACC4D7D143A2, error: 0x80004005.
[1604:2574][2013-12-04T11:50:00]e000: Error 0x80004005: Failed while caching, aborting execution.
[1FF8:10F8][2013-12-04T11:50:00]i330: Removed bundle dependency provider: {6ab8eece-89c6-4417-905f-6d9c5136519d}
[1FF8:10F8][2013-12-04T11:50:00]i352: Removing cached bundle: {6ab8eece-89c6-4417-905f-6d9c5136519d}, from path: C:\ProgramData\Package Cache\{6ab8eece-89c6-4417-905f-6d9c5136519d}\
[1604:2574][2013-12-04T11:50:00]i399: Apply complete, result: 0x80004005, restart: None, ba requested restart: No
I then found another alternative to signing the exe by adding the following to the end of the .wixproj file:
<Target Name="SignBundleEngine">
<Exec Command=""C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\signtool.exe" sign /f "$(ProjectDir)\sigFile.pfx" /p sigKey /d "My Program" /t http://timestamp.verisign.com/scripts/timstamp.dll "@(SignBundleEngine)"" />
</Target>
<Target Name="SignBundle">
<Exec Command=""C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\signtool.exe" sign /f "$(ProjectDir)\sigFile.pfx" /p sigKey /d "My Program" /t http://timestamp.verisign.com/scripts/timstamp.dll "@(SignBundle)"" />
</Target>
<PropertyGroup>
<PostBuildEvent>"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe" sign /f "$(ProjectDir)\sigFile.pfx" /p sigKey /d "My Program" /t http://timestamp.verisign.com/scripts/timstamp.dll /v "MyProgram Setup.exe"</PostBuildEvent>
</PropertyGroup>
Using this method the setup file executes and installs everything correctly but doesn't identify the publisher when requesting elevated permission to install, it says "Publisher: Unknown".
Anyone know how to get the digital signing to work on this bootstrapper?
Here is my Bundle.wxs and vcredist.wxs files:
Bundle.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="My Program"
Version="2.0.0"
Manufacturer="My Company"
UpgradeCode="PUT-GUID-HERE"
HelpUrl="http://www.mycompany.com"
AboutUrl="http://www.mycompany.com"
HelpTelephone="888 888 8888"
IconSourceFile="Resources\program.ico">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.HyperlinkLicense">
<Payload SourceFile="Resources\Bootstrapper Screen.png" />
</BootstrapperApplicationRef>
<WixVariable Id="WixStdbaLicenseUrl" Value=""/>
<WixVariable Id="WixStdbaThemeXml" Value="Resources\CustomHyperlinkTheme.xml"/>
<WixVariable Id="WixStdbaThemeWxl" Value="Resources\CustomHyperlinkTheme.wxl"/>
<Chain>
<!-- Define the list of chained packages. -->
<PackageGroupRef Id="vcredist"/>
<MsiPackage Id="MyProgram"
SourceFile="$(var.MyProgramSetup.TargetPath)"
ForcePerMachine="yes" />
</Chain>
</Bundle>
</Wix>
vcredist.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Fragment>
<util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\10.0\VC\VCRedist\x86" Value="Installed" Variable="vcredist_x86" />
<util:RegistrySearch Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\10.0\VC\VCRedist\x64" Value="Installed" Variable="vcredist_x64" />
<PackageGroup Id="vcredist">
<ExePackage Id="vcredist_x86"
Cache="no"
Compressed="yes"
PerMachine="yes"
Permanent="yes"
Vital="yes"
Name="vcredist_x86.exe"
SourceFile="vcredist3.5_x86.exe"
InstallCommand="/q"
DetectCondition="vcredist_x86 AND (vcredist_x86 >= 1)">
</ExePackage>
</PackageGroup>
</Fragment>
</Wix>
<Target Name="UsesFrameworkSdk">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="FrameworkSdkPath" />
</GetFrameworkSdkPath>
<PropertyGroup>
<Win8SDK>$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0@InstallationFolder)</Win8SDK>
</PropertyGroup>
</Target>
<Target Name="UsesSignTool" DependsOnTargets="UsesFrameworkSdk">
<PropertyGroup>
<SignToolPath Condition="('@(SignToolPath)'=='') and Exists('$(FrameworkSdkPath)bin\signtool.exe')">$(FrameworkSdkPath)bin\signtool.exe</SignToolPath>
<SignToolPath Condition="('@(SignToolPath)'=='') and Exists('$(Win8SDK)\bin\x86\signtool.exe')">$(Win8SDK)\bin\x86\signtool.exe</SignToolPath>
</PropertyGroup>
</Target>
<Target Name="SignBundleEngine" DependsOnTargets="UsesSignTool">
<Exec Command=""$(SignToolPath)" sign /d "App Setup" /t http://timestamp.digicert.com /a "@(SignBundleEngine)"" />
</Target>
<Target Name="SignBundle" DependsOnTargets="UsesSignTool">
<Exec Command=""$(SignToolPath)" sign /d "App Setup" /t http://timestamp.digicert.com /a "@(SignBundle)"" />
</Target>
This works well for me. Either you do it during the build, or you need to use insignia.
Ex:
http://wixtoolset.org/documentation/manual/v3/overview/insignia.html
insignia -ib bundle.exe -o engine.exe
... sign engine.exe
insignia -ab engine.exe bundle.exe -o bundle.exe
... sign bundle.exe
For me using WiX's in-built tool insignia
is the most straight-forward. Here's the steps I made to do code-sign a WiX MSI and bootstrap installer:
(steps 1 & 2 are just set up to make 3 & 4 read easy and more reusable and updatable! Steps 3 & 4 are the actual signing)
signtool
as a batch file in my PATH so that I can call it and change it easily. I'm running Windows 10 and so my "signtool.bat" looks like this:"c:\Program Files (x86)\Windows Kits\10\bin\x64\signtool.exe" %*
insignia
as a batch file in my PATH too so you can change it with new WiX builds as they come. My "insignia.bat" looks like this:"C:\Program Files (x86)\WiX Toolset v3.10\bin\insignia.exe" %*
signtool sign /f "c:\certificates\mycert.pfx" /p cert-password /d "Your Installer Label" /t http://timestamp.verisign.com/scripts/timstamp.dll /v $(TargetFileName)
Sign my bundle in a post-build event for the bootstrap project like this:
CALL insignia -ib "$(TargetFileName)" -o engine.exe
CALL signtool sign /f "c:\certificates\mycert.pfx" /p cert-password /d "Installer Name" /t http://timestamp.verisign.com/scripts/timstamp.dll /v engine.exe
CALL insignia -ab engine.exe "$(TargetFileName)" -o "$(TargetFileName)"
CALL signtool sign /f "c:\certificates\mycert.pfx" /p cert-password /d "Installer Name" /t http://timestamp.verisign.com/scripts/timstamp.dll /v "$(TargetFileName)"
Further notes and thoughts:
I have also signed the application (I think) by just doing Project
Properties -> Signing
and enabling click-once manifests, selecting
the certificate and checking the Sign the assembly
option.
Specifying CALL is necessary in post-build events when calling a batch file or only the first one gets called.
Updating for VS2019, and based on @jchoover's answer, here is what I got working.
This leverages some MSBuild property function work by @webjprgm here that makes finding signtool.exe more generic across Windows Kit versions. As mentioned by @karfus in a comment above, adding the SignOutput section is the incantation that kicks everything off.
This goes at the end of your bootstrap.wixproj file, before the closing /Project tag.
<!-- SignOutput must be present in some PropertyGroup to trigger signing. -->
<PropertyGroup>
<SignOutput>true</SignOutput>
</PropertyGroup>
<!-- Find Windows Kit path and then SignTool path for the post-build event -->
<Target Name="FindSignTool">
<PropertyGroup>
<WindowsKitsRoot>$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot10', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
<WindowsKitsRoot Condition="'$(WindowsKitsRoot)' == ''">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot81', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
<WindowsKitsRoot Condition="'$(WindowsKitsRoot)' == ''">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
<SignToolPath Condition="'$(SignToolPath)' == '' And '$(Platform)' == 'AnyCPU' and Exists('$(WindowsKitsRoot)bin\x64\signtool.exe')">$(WindowsKitsRoot)bin\x64\</SignToolPath>
<SignToolPath Condition="'$(SignToolPath)' == '' And Exists('$(WindowsKitsRoot)bin\$(Platform)\signtool.exe')">$(WindowsKitsRoot)bin\$(Platform)\</SignToolPath>
<SignToolPathBin Condition="'$(SignToolPath)' == ''">$([System.IO.Directory]::GetDirectories('$(WindowsKitsRoot)bin',"10.0.*"))</SignToolPathBin>
<SignToolPathLen Condition="'$(SignToolPathBin)' != ''">$(SignToolPathBin.Split(';').Length)</SignToolPathLen>
<SignToolPathIndex Condition="'$(SignToolPathLen)' != ''">$([MSBuild]::Add(-1, $(SignToolPathLen)))</SignToolPathIndex>
<SignToolPathBase Condition="'$(SignToolPathIndex)' != ''">$(SignToolPathBin.Split(';').GetValue($(SignToolPathIndex)))\</SignToolPathBase>
<SignToolPath Condition="'$(SignToolPath)' == '' And '$(SignToolPathBase)' != '' And '$(Platform)' == 'AnyCPU'">$(SignToolPathBase)x64\</SignToolPath>
<SignToolPath Condition="'$(SignToolPath)' == '' And '$(SignToolPathBase)' != ''">$(SignToolPathBase)$(Platform)\</SignToolPath>
</PropertyGroup>
</Target>
<!-- Sign the bundle engine -->
<Target Name="SignBundleEngine" DependsOnTargets="FindSignTool">
<Exec Command=""$(SignToolPath)signtool.exe" sign /d "MyApp Setup" /fd SHA256 /td SHA256 /a /f "MyApp Code Certificate.pfx" /p CertPassword /tr http://timestamp.digicert.com /a "@(SignBundleEngine)"" />
</Target>
<!-- Sign the final bundle -->
<Target Name="SignBundle" DependsOnTargets="FindSignTool">
<Exec Command=""$(SignToolPath)signtool.exe" sign /d "MyApp Setup" /fd SHA256 /td SHA256 /a /f "MyApp Code Certificate.pfx" /p CertPassword /tr http://timestamp.digicert.com /a "@(SignBundle)"" />
</Target>
Further @jchoover's answer, you have 3 options when signing bundles:
Build the bundle unsigned, then sign it later. However, you also need to sign the engine exe which is embedded within the bundle. As @jchoover states, you can use insignia to get around this by extracting the engine to a file. You can then sign the file using your normal process (for example, with signtool.exe) and then import it back into the bundle
Add the SignBundle and SignBundleEngine targets to your project(s). You can do this by opening them up in a text editor, and editing the underlying MSBuild code. @jchoover's answer describes how you can do this.
Create a .targets file with the SignBundle and SignBundleEngine targets, and passing the path using the CustomAfterWixTargets property:
msbuild your.sln /p:CustomAfterWixTargets=customafterwix.targets /p:SignOutput=true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With