@joebigelow / wix-1 / commits / 4182d6d5

Introduce "msi inscribe" command and use it in wix.signing.targets

Rob Mensching committed Jan 8, 2022 at 07:44 UTC 4182d6d594301b88d4780d0222a3d1448b524fde
10 files changed +376 -31
src/wix/WixToolset.BuildTasks/InscribeMsiWithCabinetSignatures.cs new
+33
@@ -0,0 +1,33 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.BuildTasks
4 +{
5 + using Microsoft.Build.Framework;
6 +
7 + /// <summary>
8 + /// An MSBuild task to run WiX to update cabinet signatures in a MSI.
9 + /// </summary>
10 + public sealed partial class InscribeMsiWithCabinetSignatures : WixExeBaseTask
11 + {
12 + [Required]
13 + public ITaskItem DatabaseFile { get; set; }
14 +
15 + [Required]
16 + public ITaskItem IntermediateDirectory { get; set; }
17 +
18 + public ITaskItem OutputFile { get; set; }
19 +
20 + protected override void BuildCommandLine(WixCommandLineBuilder commandLineBuilder)
21 + {
22 + commandLineBuilder.AppendTextUnquoted("msi inscribe");
23 +
24 + commandLineBuilder.AppendFileNameIfNotNull(this.DatabaseFile);
25 + commandLineBuilder.AppendSwitchIfNotNull("-out ", this.OutputFile);
26 + commandLineBuilder.AppendSwitchIfNotNull("-intermediatefolder ", this.IntermediateDirectory);
27 +
28 + base.BuildCommandLine(commandLineBuilder);
29 +
30 + commandLineBuilder.AppendTextIfNotWhitespace(this.AdditionalOptions);
31 + }
32 + }
33 +}
src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+31 -20
@@ -12,20 +12,25 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
12 using WixToolset.Core.WindowsInstaller.Bind;
13 using WixToolset.Data;
14 using WixToolset.Data.WindowsInstaller;
15 - using WixToolset.Extensibility.Data;
15 using WixToolset.Extensibility.Services;
16
17 internal class InscribeMsiPackageCommand
18 {
20 - public InscribeMsiPackageCommand(IInscribeContext context)
19 + public InscribeMsiPackageCommand(IServiceProvider serviceProvider, string inputPath, string intermediateFolder, string outputPath)
20 {
22 - this.Context = context;
23 - this.Messaging = context.ServiceProvider.GetService<IMessaging>();
24 - this.WindowsInstallerBackendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>();
21 + this.Messaging = serviceProvider.GetService<IMessaging>();
22 + this.WindowsInstallerBackendHelper = serviceProvider.GetService<IWindowsInstallerBackendHelper>();
23 this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All);
24 + this.InputPath = inputPath;
25 + this.IntermediateFolder = intermediateFolder;
26 + this.OutputPath = outputPath;
27 }
28
28 - private IInscribeContext Context { get; }
29 + private string InputPath { get; }
30 +
31 + private string IntermediateFolder { get; }
32 +
33 + private string OutputPath { get; }
34
35 private IMessaging Messaging { get; }
36
@@ -39,14 +44,22 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
44 var foundUnsignedExternals = false;
45 var shouldCommit = false;
46
42 - var attributes = File.GetAttributes(this.Context.InputFilePath);
47 + var databasePath = this.OutputPath;
48 +
49 + if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase))
50 + {
51 + Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
52 + File.Copy(this.InputPath, this.OutputPath, true);
53 + }
54 +
55 + var attributes = File.GetAttributes(databasePath);
56 if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
57 {
45 - this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath));
58 + this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(databasePath));
59 return shouldCommit;
60 }
61
49 - using (var database = new Database(this.Context.InputFilePath, OpenDatabase.Transact))
62 + using (var database = new Database(databasePath, OpenDatabase.Transact))
63 {
64 // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
65 var codepage = 1252;
@@ -65,8 +78,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
78 {
79 foreach (var digitalSignatureRecord in digitalSignatureView.Records)
80 {
68 - Row digitalSignatureRow = null;
69 - digitalSignatureRow = digitalSignatureTable.CreateRow(null);
81 + var digitalSignatureRow = digitalSignatureTable.CreateRow(null);
82
83 var table = digitalSignatureRecord.GetString(0);
84 var signObject = digitalSignatureRecord.GetString(1);
@@ -78,7 +90,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
90 if (false == digitalSignatureRecord.IsNull(3))
91 {
92 // Export to a file, because the MSI API's require us to provide a file path on disk
81 - var hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature");
93 + var hashPath = Path.Combine(this.IntermediateFolder, "MsiDigitalSignature");
94 var hashFileName = String.Concat(table, ".", signObject, ".bin");
95
96 Directory.CreateDirectory(hashPath);
@@ -111,7 +123,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
123 var certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
124
125 // Export to a file, because the MSI API's require us to provide a file path on disk
114 - var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
126 + var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate");
127 Directory.CreateDirectory(certPath);
128 certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer"));
129
@@ -147,7 +159,6 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
159 foreach (var mediaRecord in mediaView.Records)
160 {
161 X509Certificate2 cert2 = null;
150 - Row digitalSignatureRow = null;
162
163 var cabName = mediaRecord.GetString(4); // get the name of the cab
164 // If there is no cabinet or it's an internal cab, skip it.
@@ -157,7 +168,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
168 }
169
170 var cabId = mediaRecord.GetString(1); // get the ID of the cab
160 - var cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName);
171 + var cabPath = Path.Combine(Path.GetDirectoryName(this.InputPath), cabName);
172
173 // If the cabs aren't there, throw an error but continue to catch the other errors
174 if (!File.Exists(cabPath))
@@ -207,7 +218,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
218 digitalCertificateRow[0] = certificateGeneratedId;
219
220 // Export to a file, because the MSI API's require us to provide a file path on disk
210 - var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
221 + var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate");
222 Directory.CreateDirectory(certPath);
223 certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer"));
224 File.Delete(certPath);
@@ -224,7 +235,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
235 certificates.Add(cert2.Thumbprint, certificateGeneratedId);
236 }
237
227 - digitalSignatureRow = digitalSignatureTable.CreateRow(null);
238 + var digitalSignatureRow = digitalSignatureTable.CreateRow(null);
239
240 digitalSignatureRow[0] = "Media";
241 digitalSignatureRow[1] = cabId;
@@ -234,7 +245,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
245
246 if (digitalCertificateTable.Rows.Count > 0)
247 {
237 - var command = new CreateIdtFileCommand(this.Messaging, digitalCertificateTable, codepage, this.Context.IntermediateFolder, true);
248 + var command = new CreateIdtFileCommand(this.Messaging, digitalCertificateTable, codepage, this.IntermediateFolder, true);
249 command.Execute();
250
251 database.Import(command.IdtPath);
@@ -243,7 +254,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
254
255 if (digitalSignatureTable.Rows.Count > 0)
256 {
246 - var command = new CreateIdtFileCommand(this.Messaging, digitalSignatureTable, codepage, this.Context.IntermediateFolder, true);
257 + var command = new CreateIdtFileCommand(this.Messaging, digitalSignatureTable, codepage, this.IntermediateFolder, true);
258 command.Execute();
259
260 database.Import(command.IdtPath);
@@ -257,7 +268,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
268 // If we did find external cabs but not all of them were signed, give a warning
269 if (foundUnsignedExternals)
270 {
260 - this.Messaging.Write(WarningMessages.ExternalCabsAreNotSigned(this.Context.InputFilePath));
271 + this.Messaging.Write(WarningMessages.ExternalCabsAreNotSigned(this.InputPath));
272 }
273
274 if (shouldCommit)
src/wix/WixToolset.Core.WindowsInstaller/InscribeSubcommand.cs new
+80
@@ -0,0 +1,80 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.WindowsInstaller
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Threading;
8 + using System.Threading.Tasks;
9 + using WixToolset.Core.WindowsInstaller.Inscribe;
10 + using WixToolset.Extensibility.Services;
11 +
12 + internal class InscribeSubcommand : WindowsInstallerSubcommandBase
13 + {
14 + public InscribeSubcommand(IServiceProvider serviceProvider)
15 + {
16 + this.ServiceProvider = serviceProvider;
17 + this.Messaging = serviceProvider.GetService<IMessaging>();
18 + }
19 +
20 + private IServiceProvider ServiceProvider { get; }
21 +
22 + private IMessaging Messaging { get; }
23 +
24 + private string InputPath { get; set; }
25 +
26 + private string IntermediateFolder { get; set; }
27 +
28 + private string OutputPath { get; set; }
29 +
30 + public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
31 + {
32 + if (String.IsNullOrEmpty(this.InputPath))
33 + {
34 + Console.Error.WriteLine("Input MSI database is required");
35 + return Task.FromResult(-1);
36 + }
37 +
38 + if (String.IsNullOrEmpty(this.IntermediateFolder))
39 + {
40 + this.IntermediateFolder = Path.GetTempPath();
41 + }
42 +
43 + if (String.IsNullOrEmpty(this.OutputPath))
44 + {
45 + this.OutputPath = this.InputPath;
46 + }
47 +
48 + var command = new InscribeMsiPackageCommand(this.ServiceProvider, this.InputPath, this.IntermediateFolder, this.OutputPath);
49 + command.Execute();
50 +
51 + return Task.FromResult(this.Messaging.LastErrorNumber);
52 + }
53 +
54 + public override bool TryParseArgument(ICommandLineParser parser, string argument)
55 + {
56 + if (parser.IsSwitch(argument))
57 + {
58 + var parameter = argument.Substring(1);
59 + switch (parameter.ToLowerInvariant())
60 + {
61 + case "intermediatefolder":
62 + this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
63 + return true;
64 +
65 + case "o":
66 + case "out":
67 + this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument);
68 + return true;
69 + }
70 + }
71 + else if (String.IsNullOrEmpty(this.InputPath))
72 + {
73 + this.InputPath = argument;
74 + return true;
75 + }
76 +
77 + return false;
78 + }
79 + }
80 +}
src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+4 -2
@@ -2,6 +2,7 @@
2
3 namespace WixToolset.Core.WindowsInstaller
4 {
5 + using System;
6 using WixToolset.Core.WindowsInstaller.Bind;
7 using WixToolset.Core.WindowsInstaller.Decompile;
8 using WixToolset.Core.WindowsInstaller.Inscribe;
@@ -72,8 +73,9 @@ namespace WixToolset.Core.WindowsInstaller
73
74 public bool Inscribe(IInscribeContext context)
75 {
75 - var command = new InscribeMsiPackageCommand(context);
76 - return command.Execute();
76 + //var command = new InscribeMsiPackageCommand(context);
77 + //return command.Execute();
78 + throw new NotImplementedException();
79 }
80
81 public Intermediate Unbind(IUnbindContext context)
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerCommand.cs new
+73
@@ -0,0 +1,73 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.WindowsInstaller
4 +{
5 + using System;
6 + using System.Threading;
7 + using System.Threading.Tasks;
8 + using WixToolset.Extensibility.Data;
9 + using WixToolset.Extensibility.Services;
10 +
11 + /// <summary>
12 + /// Windows Installer specialized command.
13 + /// </summary>
14 + internal class WindowsInstallerCommand : ICommandLineCommand
15 + {
16 + public WindowsInstallerCommand(IServiceProvider serviceProvider)
17 + {
18 + this.ServiceProvider = serviceProvider;
19 + }
20 +
21 + public bool ShowHelp { get; set; }
22 +
23 + public bool ShowLogo { get; set; }
24 +
25 + public bool StopParsing { get; set; }
26 +
27 + private IServiceProvider ServiceProvider { get; }
28 +
29 + private WindowsInstallerSubcommandBase Subcommand { get; set; }
30 +
31 + public Task<int> ExecuteAsync(CancellationToken cancellationToken)
32 + {
33 + if (this.ShowHelp || this.Subcommand is null)
34 + {
35 + DisplayHelp();
36 + return Task.FromResult(1);
37 + }
38 +
39 + return this.Subcommand.ExecuteAsync(cancellationToken);
40 + }
41 +
42 + public bool TryParseArgument(ICommandLineParser parser, string argument)
43 + {
44 + if (this.Subcommand is null)
45 + {
46 + switch (argument.ToLowerInvariant())
47 + {
48 + case "inscribe":
49 + this.Subcommand = new InscribeSubcommand(this.ServiceProvider);
50 + return true;
51 + }
52 +
53 + return false;
54 + }
55 +
56 + return this.Subcommand.TryParseArgument(parser, argument);
57 + }
58 +
59 + private static void DisplayHelp()
60 + {
61 + Console.WriteLine();
62 + Console.WriteLine("Usage: wix msi inscribe input.msi [-intermedidateFolder folder] [-out output.msi]");
63 + Console.WriteLine();
64 + Console.WriteLine("Options:");
65 + Console.WriteLine(" -h|--help Show command line help.");
66 + Console.WriteLine(" --nologo Suppress displaying the logo information.");
67 + Console.WriteLine();
68 + Console.WriteLine("Commands:");
69 + Console.WriteLine();
70 + Console.WriteLine(" inscribe Updates MSI database with cabinet signature information.");
71 + }
72 + }
73 +}
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionCommandLine.cs new
+41
@@ -0,0 +1,41 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.WindowsInstaller
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using WixToolset.Extensibility;
8 + using WixToolset.Extensibility.Data;
9 + using WixToolset.Extensibility.Services;
10 +
11 + /// <summary>
12 + /// Parses the "msi" command-line command. See <c>WindowsInstallerCommand</c>
13 + /// for the bulk of the command-line processing.
14 + /// </summary>
15 + internal class WindowsInstallerExtensionCommandLine : BaseExtensionCommandLine
16 + {
17 + public WindowsInstallerExtensionCommandLine(IServiceProvider serviceProvider)
18 + {
19 + this.ServiceProvider = serviceProvider;
20 + }
21 +
22 + private IServiceProvider ServiceProvider { get; }
23 +
24 + public override IReadOnlyCollection<ExtensionCommandLineSwitch> CommandLineSwitches => new ExtensionCommandLineSwitch[]
25 + {
26 + new ExtensionCommandLineSwitch { Switch = "msi", Description = "Windows Installer specialized operations." },
27 + };
28 +
29 + public override bool TryParseCommand(ICommandLineParser parser, string argument, out ICommandLineCommand command)
30 + {
31 + command = null;
32 +
33 + if ("msi".Equals(argument, StringComparison.OrdinalIgnoreCase))
34 + {
35 + command = new WindowsInstallerCommand(this.ServiceProvider);
36 + }
37 +
38 + return command != null;
39 + }
40 + }
41 +}
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerExtensionFactory.cs
+13 -2
@@ -1,4 +1,4 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.WindowsInstaller
4 {
@@ -7,11 +7,22 @@ namespace WixToolset.Core.WindowsInstaller
7
8 internal class WindowsInstallerExtensionFactory : IExtensionFactory
9 {
10 + public WindowsInstallerExtensionFactory(IServiceProvider serviceProvider)
11 + {
12 + this.ServiceProvider = serviceProvider;
13 + }
14 +
15 + private IServiceProvider ServiceProvider { get; }
16 +
17 public bool TryCreateExtension(Type extensionType, out object extension)
18 {
19 extension = null;
20
14 - if (extensionType == typeof(IBackendFactory))
21 + if (extensionType == typeof(IExtensionCommandLine))
22 + {
23 + extension = new WindowsInstallerExtensionCommandLine(this.ServiceProvider);
24 + }
25 + else if (extensionType == typeof(IBackendFactory))
26 {
27 extension = new WindowsInstallerBackendFactory();
28 }
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerSubcommandBase.cs new
+15
@@ -0,0 +1,15 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.WindowsInstaller
4 +{
5 + using System.Threading;
6 + using System.Threading.Tasks;
7 + using WixToolset.Extensibility.Services;
8 +
9 + internal abstract class WindowsInstallerSubcommandBase
10 + {
11 + public abstract bool TryParseArgument(ICommandLineParser parser, string argument);
12 +
13 + public abstract Task<int> ExecuteAsync(CancellationToken cancellationToken);
14 + }
15 +}
src/wix/WixToolset.Sdk/tools/wix.signing.targets
+20 -7
@@ -11,6 +11,10 @@
11 <UsingTask TaskName="GetCabList" AssemblyFile="$(WixTasksPath)" />
12 <UsingTask TaskName="GetLooseFileList" AssemblyFile="$(WixTasksPath)" />
13
14 + <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' == '' " AssemblyFile="$(WixTasksPath)" />
15 + <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath)" Architecture="x86" />
16 + <UsingTask TaskName="InscribeMsiWithCabinetSignatures" Condition=" '$(WixTasksPath64)' != '' " AssemblyFile="$(WixTasksPath64)" Architecture="x64" />
17 +
18 <!-- Default Inscribe properties. -->
19 <PropertyGroup>
20 <InscribeNoLogo Condition=" '$(InscribeNoLogo)' == '' ">$(NoLogo)</InscribeNoLogo>
@@ -21,6 +25,10 @@
25 <InscribeVerboseOutput Condition=" '$(InscribeVerboseOutput)' == '' ">$(VerboseOutput)</InscribeVerboseOutput>
26 </PropertyGroup>
27
28 + <PropertyGroup>
29 + <SignOutputCabs Condition=" '$(SignOutputCabs)'=='' ">$(SignOutput)</SignOutputCabs>
30 + </PropertyGroup>
31 +
32 <!--
33 ==================================================================================================
34 Signing
@@ -58,7 +66,7 @@
66 <Target
67 Name="Signing"
68 DependsOnTargets="$(SigningDependsOn)"
61 - Inputs="$(TargetPath)"
69 + Inputs="@(_WixBuildOutputFile)"
70 Outputs="$(SignedFilePath)"
71 Condition=" '$(SignOutput)' == 'true' ">
72
@@ -76,7 +84,7 @@
84 <Target
85 Name="CalculateSignTargetFiles">
86 <ItemGroup>
79 - <SignTargetPath Include="$(TargetPath)" />
87 + <SignTargetPath Include="@(_WixBuildOutputFile)" />
88 </ItemGroup>
89 </Target>
90
@@ -98,7 +106,8 @@
106 <Target
107 Name="GetCabsToSign"
108 Inputs="@(SignTargetPath)"
101 - Outputs="$(SignedFilePath)">
109 + Outputs="$(SignedFilePath)"
110 + Condition=" '$(SignOutputCabs)' == 'true' ">
111 <GetCabList Database="%(SignTargetPath.FullPath)">
112 <Output TaskParameter="CabList" ItemName="SignCabs" />
113 <Output TaskParameter="CabList" ItemName="FileWrites" />
@@ -174,18 +183,22 @@
183 Outputs="$(SignedFilePath)"
184 Condition=" '@(SignCabs)' != '' ">
185
177 - <Insignia
186 + <InscribeMsiWithCabinetSignatures
187 DatabaseFile="%(SignTargetPath.FullPath)"
188 OutputFile="%(SignTargetPath.FullPath)"
180 - ToolPath="$(WixToolPath)"
189 + IntermediateDirectory="%(SignTargetPath.RootDir)%(SignTargetPath.Directory)"
190 +
191 NoLogo="$(InscribeNoLogo)"
182 - RunAsSeparateProcess="$(RunWixToolsOutOfProc)"
192 SuppressAllWarnings="$(InscribeSuppressAllWarnings)"
193 SuppressSpecificWarnings="$(InscribeSuppressSpecificWarnings)"
194 TreatWarningsAsErrors="$(InscribeTreatWarningsAsErrors)"
195 TreatSpecificWarningsAsErrors="$(InscribeTreatSpecificWarningsAsErrors)"
196 VerboseOutput="$(InscribeVerboseOutput)"
188 - AdditionalOptions="$(InscribeAdditionalOptions)" />
197 + AdditionalOptions="$(InscribeAdditionalOptions)"
198 +
199 + RunAsSeparateProcess="$(RunWixToolsOutOfProc)"
200 + ToolExe="$(WixToolExe)"
201 + ToolPath="$(WixToolDir)" />
202 </Target>
203
204 <!--
src/wix/test/WixToolsetTest.CoreIntegration/SigningFixture.cs new
+66
@@ -0,0 +1,66 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolsetTest.CoreIntegration
4 +{
5 + using System.IO;
6 + using WixBuildTools.TestSupport;
7 + using WixToolset.Core.TestPackage;
8 + using Xunit;
9 +
10 + public class SigningFixture
11 + {
12 + [Fact]
13 + public void CanInscribeMsiWithSignedCabinet()
14 + {
15 + var folder = TestData.Get(@"TestData\SingleFileCompressed");
16 + var signedFolder = TestData.Get(@"TestData\.Data");
17 +
18 + using (var fs = new DisposableFileSystem())
19 + {
20 + var baseFolder = fs.GetFolder();
21 + var intermediateFolder = Path.Combine(baseFolder, "obj");
22 + var outputMsi = Path.Combine(intermediateFolder, @"bin\test.msi");
23 + var signedMsi = Path.Combine(baseFolder, @"signed.msi");
24 +
25 + var result = WixRunner.Execute(new[]
26 + {
27 + "build",
28 + Path.Combine(folder, "Package.wxs"),
29 + Path.Combine(folder, "PackageComponents.wxs"),
30 + "-loc", Path.Combine(folder, "Package.en-us.wxl"),
31 + "-bindpath", Path.Combine(folder, "data"),
32 + "-intermediateFolder", intermediateFolder,
33 + "-o", outputMsi
34 + });
35 +
36 + result.AssertSuccess();
37 +
38 + var beforeRows = Query.QueryDatabase(outputMsi, new[] { "MsiDigitalSignature", "MsiDigitalCertificate" });
39 + Assert.Empty(beforeRows);
40 +
41 + // Swap in a pre-signed cabinet since signing during the unit test
42 + // is a challenge. The cabinet contents almost definitely don't
43 + // match but that's okay for these testing purposes.
44 + File.Copy(Path.Combine(signedFolder, "signed_cab1.cab"), Path.Combine(Path.GetDirectoryName(outputMsi), "example.cab"), true);
45 +
46 + result = WixRunner.Execute(new[]
47 + {
48 + "msi",
49 + "inscribe",
50 + outputMsi,
51 + "-o", signedMsi,
52 + "-intermediateFolder", intermediateFolder,
53 + });
54 +
55 + result.AssertSuccess();
56 +
57 + var rows = Query.QueryDatabase(signedMsi, new[] { "MsiDigitalSignature", "MsiDigitalCertificate" });
58 + WixAssert.CompareLineByLine(new[]
59 + {
60 + "MsiDigitalCertificate:cer8xpsawK5TG4sIx4em8F.i7ocIKU\t[Binary data]",
61 + "MsiDigitalSignature:Media\t1\tcer8xpsawK5TG4sIx4em8F.i7ocIKU\t"
62 + }, rows);
63 + }
64 + }
65 + }
66 +}