diff --git a/README.md b/README.md index 64621d93..9cfe567f 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ See https://ebourg.github.io/jsign for more information. * The Oracle Cloud signing service has been integrated * Signing of NuGet packages has been implemented (contributed by Sebastian Stamm) * The intermediate certificates are downloaded if missing from the keystore or the certificate chain file +* File list files prefixed with `@` are now supported with the command line tool to sign multiple files * Jsign now checks if the certificate subject matches the app manifest publisher before signing APPX/MSIX packages * The `extract` command has been added to extract the signature from a signed file, in DER or PEM format * The `remove` command has been added to remove the signature from a signed file diff --git a/docs/index.html b/docs/index.html index 93585f2b..660b4e21 100644 --- a/docs/index.html +++ b/docs/index.html @@ -482,7 +482,7 @@

Command Line Tool

The parameters expected are the same as those used by the Ant task:

-  usage: jsign [OPTIONS] [FILE]...
+  usage: jsign [OPTIONS] [FILE] [@FILELIST]...
   Sign and timestamp Windows executable files, Microsoft Installers (MSI), Cabinet
   files (CAB), Catalog files (CAT), Windows packages (APPX/MSIX), Microsoft Dynamics
   365 extension packages, NuGet packages and scripts (PowerShell, VBScript, JScript, WSF).
@@ -534,6 +534,8 @@ 

Command Line Tool

-h,--help Print the help
+

After the options Jsign accepts one or more files to sign as arguments. If a filename starts with @ it is considered +as a text file containing a list of files to sign, one per line.

Examples

diff --git a/jsign-cli/src/main/java/net/jsign/JsignCLI.java b/jsign-cli/src/main/java/net/jsign/JsignCLI.java index 089329d5..c330d297 100644 --- a/jsign-cli/src/main/java/net/jsign/JsignCLI.java +++ b/jsign-cli/src/main/java/net/jsign/JsignCLI.java @@ -17,9 +17,14 @@ package net.jsign; import java.io.File; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -162,8 +167,25 @@ void execute(String... args) throws SignerException, ParseException { throw new SignerException("No file specified"); } - for (String filename : cmd.getArgList()) { - helper.execute(new File(filename)); + for (String arg : cmd.getArgList()) { + for (String filename : expand(arg)) { + helper.execute(new File(filename)); + } + } + } + + /** + * Expands filenames starting with @ to a list of filenames. + */ + private List expand(String filename) { + if (filename.startsWith("@")) { + try { + return Files.readAllLines(Paths.get(filename.substring(1))); + } catch (IOException e) { + throw new IllegalArgumentException("Failed to read the file list: " + filename.substring(1), e); + } + } else { + return Collections.singletonList(filename); } } @@ -189,7 +211,7 @@ private void printHelp() { formatter.setDescPadding(1); PrintWriter out = new PrintWriter(System.out); - formatter.printUsage(out, formatter.getWidth(), getProgramName() + " [COMMAND] [OPTIONS] [FILE]..."); + formatter.printUsage(out, formatter.getWidth(), getProgramName() + " [COMMAND] [OPTIONS] [FILE] [@FILELIST]..."); out.println(); formatter.printWrapped(out, formatter.getWidth(), header); diff --git a/jsign-cli/src/test/java/net/jsign/JsignCLITest.java b/jsign-cli/src/test/java/net/jsign/JsignCLITest.java index 89d77b35..d922412d 100644 --- a/jsign-cli/src/test/java/net/jsign/JsignCLITest.java +++ b/jsign-cli/src/test/java/net/jsign/JsignCLITest.java @@ -18,9 +18,11 @@ import java.io.File; import java.net.ProxySelector; +import java.nio.file.Files; import java.security.InvalidParameterException; import java.security.Permission; import java.security.ProviderException; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import io.netty.handler.codec.http.HttpRequest; @@ -216,6 +218,20 @@ public void testSigningMultipleFiles() throws Exception { } } + @Test + public void testSigningMultipleFilesWithListFile() throws Exception { + File listFile = new File("target/test-classes/files.txt"); + Files.write(listFile.toPath(), Arrays.asList(targetFile.getAbsolutePath(), targetFile.getAbsolutePath())); + + cli.execute("--name=WinEyes", "--url=http://www.steelblue.com/WinEyes", "--alg=SHA-1", "--keystore=target/test-classes/keystores/" + keystore, "--keypass=" + keypass, "@" + listFile); + + assertTrue("The file " + targetFile + " wasn't changed", SOURCE_FILE_CRC32 != FileUtils.checksumCRC32(targetFile)); + + try (PEFile peFile = new PEFile(targetFile)) { + SignatureAssert.assertSigned(peFile, SHA1, SHA1); + } + } + @Test public void testSigningPowerShell() throws Exception { File sourceFile = new File("target/test-classes/hello-world.ps1"); diff --git a/jsign/src/deb/data/usr/share/man/man1/jsign.1 b/jsign/src/deb/data/usr/share/man/man1/jsign.1 index a66ede2f..cba07ac1 100644 --- a/jsign/src/deb/data/usr/share/man/man1/jsign.1 +++ b/jsign/src/deb/data/usr/share/man/man1/jsign.1 @@ -6,7 +6,7 @@ jsign \- sign and timestamp executable files for Windows, Microsoft Installers ( .SH SYNOPSIS .B jsign -[OPTIONS] [FILE]... +[OPTIONS] [FILE] [@FILELIST]... .SH DESCRIPTION Jsign is a Java implementation of Microsoft Authenticode that lets you sign @@ -178,6 +178,9 @@ If the signature exists it is attached to the file, replacing any existing signa .B -h, --help Print the help +After the options Jsign accepts one or more files to sign as arguments. If a filename starts with @ it is considered +as a text file containing a list of files to sign, one per line. + .SH EXAMPLES